代码改变世界

android中利用java.lang.reflect.Proxy实现私有接口

2011-10-08 18:09  MudooT  阅读(1935)  评论(2编辑  收藏  举报

  看了标题,肯定有人会说我没事吃饱了撑的,谁去实现私有接口啊?当然这是有情景的事,android的SDK中有一个widget叫NumberPicker,这个东西被官方给hide掉了——就是可以看,但是不能用!

  这不是开玩笑么!好好的一个组件,不让人用怎么行?来吧,用反射吧!

          Class.forName("android.widget.NumberPicker");

  很好,搞定,通过getMethod来获得方法,进行调用,嗯嗯嗯,一切都很顺利。突然有一个方法难住了!setOnChangeListener这个方法的参数是在NumberPicker内部定义的一个接口,当然这个接口是public的,但由于整个类都被隐藏了,所以就算是public也无法调用,没办法了,自己搞吧。

      public interface OnChangedListener {
      void onChanged(NumberPicker picker, int oldVal, int newVal);
      }
  首先我想到了用bytecode去生成一个动态的类,恩,bytecode中比较容易写的是javassist,这个包的优点在于可以不用去写字节码,很容易的写了出来,结果提示给我错误说找不到android.widget.NumberPicker$OnChangedListener这个类,不会啊,明明有的!随便试了试Object这个类,囧,这个也找不到,看来是javassist的问题,后来想到好像android的虚拟机的编译的字节码好像和普通的JVM不太一样,google了一下,果然在android里不能用javassist,只好放弃。

  然后就用asm吧,恩,看了看相关的文档,一头雾水,很高深,不适合我这样的懒人去用嘛~再次放弃!

  绝望的时候往往也是希望来的时候,突然想到了动态代理这个东西,动态代理可以实现类似AOP的东西嘛,那我就把onChange中要实现的事情在代理中实现应该就可以了,好吧,来试试看。

          Class<?> i = Class.forName("android.widget.NumberPicker$OnChangedListener");
Object listener = Proxy.newProxyInstance(i.getClassLoader(),
new Class<?>[] { i }, new InvocationHandler() {

@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
mYear = (Integer) args[2];
notifyDateChanged();
return null;
}
});

  这样可以创建一个“匿名”的listener,实现了OnChangedListener接口,然后就把它作为参数传过去就好了!

         Method method = c.getDeclaredMethod("setOnChangeListener", Class
.forName("android.widget.NumberPicker$OnChangedListener"));
method.invoke(numberPicker, listener);
  搞定!

  来张截图哈!


  这样一个可以响应listener的numberpicker就可以使用了!

  当然,也可以把android的源码check out 然后把hide标签去掉,重新编译,然后也可以,不过这样也太暴力了,还是和谐一点比较好!