Handler 动态注入

在Android中,我们经常会用到匿名内部类,比如:

new Thread(new Runnable(){

 public void run(){

System.out.println("test");

}

}).start();


Buttion buttion = new Buttion(this);

buttion.setOnClickListener(new OnClickListener(){

public void onClick(View view){

}

});

private Handler handler = new Handler(){

public void HandleMessage(Message msg){

}

}

刚刚接触编程的时候可能觉得这种用法比较怪,我们明明可以单独写成一个类,在类中写上要处理的逻辑,比如:

public class MyListener implements OnClickListener {

public void onClick(View view){

}

}

而且这个类可以重复使用,而匿名内部类只使用了一次,这个类的内部实现是无法被保存下来的,那么我们为什么还要使用内部类呢?这种方式有什么好处呢?

首先在android中有很多的界面UI响应事件需要处理,每个处理的Listener类我们如果都要去写一个类的话,那么会有很多代码,另一个方面,我们处理UI问题时,经常涉及到的就是Activity中的显示的各种控件,每种处理都是针对当前的布局页面或者可以说成是当前的Activity,这正处理和其他类有共同之处的地方可能不多,所以单独写一个类的必要性很小,而且单独写成一个类的话,还要把我们当前的Activity作为参数传过去,才能处理Activity中的控件,而匿名内部类可以无限范围的访问外部类的成员和方法,这就显示出它的优点了。

回过头来我们在细细看下代码:

buttion.setOnClickListener(new OnClickListener(){

public void onClick(View view){

}

});

这个new 出来的Listener没有具体的实现类类名,没有引用,只有一个接口或者抽象类,然后我们在new的同时来完成抽象方法的具体实现,而且可以随意调用外部类的成员和方法,就像自己在调用自己的方法,完全是透明的,这种使用就很方便了。它既可以用面向对象的方式来构造或者区分一个类型的处理,又可以省略单独写一个完整的类,关键的地方在于随意调用修改外部类,完全没有任何对象参数的传递,就好像外部类在自己处理自己。


上面说了匿名内部类的好处,尤其在处理界面UI方面使用很多,但是过多的控件需要处理时,我们就要new 很多的内部类,使我们的代码看起来有些杂乱,毕竟匿名内部类也是一个类,和我们所关注使用的外部类(比如Activity)看起来还是不协调,于是我们习惯的做法是让一个Activity实现OnClickListener接口,这时Activity就是一个公用类,它本身既可以显示控件,同时又可以处理控件的点击事件:

public void MyActivity extends Activity implements OnClickListener{

private Buttion buttion = new Buttion(this);


    @Override
    public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

this.buttion.setOnClickListener(this);

}

 @Override

public void onClick(View view){

if (view.getTag().equals("test")){

//todo

}

}

}

这样代码看起来整洁了很多,而且事件处理方法onClick()就是Activity本身的实现方法,看起来好像更舒服了,在这里处理修改控件的事件也更加顺利成章了。



在xUtils这种通过Annotation注解方式来动态注入控件,onClick事件中,我们的控件省略的手动调用findViewById();  buttion.setOnClickListener(this);Activity也不用实现一堆的Listener接口了,对于类的继承和实现有强迫症的同学来说更加简洁了。

    @OnClick(R.id.search_top_back)
    private void onClick(View v) {
        switch (v.getId()) {
        case R.id.search_top_back:
            finish();
            break;
        case R.id.search_top_search:
            Intent intent = new Intent(MapsActivity.this, SearchActivity.class);
            intent.putExtras(bundle);
            context.startActivity(intent);
            break;
        }
    }

这种动态注入其实就是使用动态代理的方式,表面返回的是Listener的代理对象,真正调用的是Activity中的加入@onClick注解的方法。


说了这么多,现在回到我们的主题,在xUtils中没有发现Handler的动态注入,为什么Handler不能使用动态代理呢,我们看下动态代理的代码:Proxy.java中

 public static Object newProxyInstance(ClassLoader loader,
            Class<?>[] interfaces, InvocationHandler h);

这里Class<?>[] interfaces就是返回的动态代理类的接口,也就是返回我们一个有着interfaces行为实现方法的对象,当然真正的处理是在InvocationHandler中的

public Object invoke(Object proxy, Method method, Object[] args)

这里来真正调用我们传入的对象,在Proxy.newProxyInstance()方法中调用了

return getProxyClass(loader, interfaces).getConstructor(
                    new Class<?>[] { InvocationHandler.class }).newInstance(
                    new Object[] { h });

然后我们再看下getProxyClass方法

public static Class<?> getProxyClass(ClassLoader loader,
            Class<?>... interfaces) throws IllegalArgumentException {
        // check that interfaces are a valid array of visible interfaces
        if (interfaces == null) {
            throw new NullPointerException("interfaces == null");
        }
        String commonPackageName = null;
        for (int i = 0, length = interfaces.length; i < length; i++) {
            Class<?> next = interfaces[i];
            if (next == null) {
                throw new NullPointerException("interfaces[" + i + "] == null");
            }
            String name = next.getName();
            if (!next.isInterface()) {
                throw new IllegalArgumentException(name + " is not an interface");
            }

我们发现了 if (!next.isInterface())如果不是接口的话,就会抛出异常,正是这样由于Handler是一个普通的类,没有实现接口,所以无法使用动态代理。还有一个更重要的原因是Handler的使用需要new Handler();在初始化的过程中来绑定当前线程,得到线程中的MessageQueue队列,new之后有了消息队列和所属线程,我们在handler.sendMessage发送消息的时候,才能把Message放入到handler对应的线程的消息队列中。

基于以上原因我们无法使用动态代理类来注入handler,但是我们也想在xUtils的ViewUtils中加入,那我们只好手动改造一下了,这个时候我们就像最原始的使用那样,使用匿名内部类,不过要借助反射。

我们先定义了一个叫handle类型的注解,注意这个命名为了和handler区别开

@Retention(RetentionPolicy.RUNTIME)
public @interface Handle {
    String value();
}


我们在ViewUtils中的 

private static void injectObject(final Object handler, ViewFinder finder) 方法中,加入下面的代码:

Handle handle = field.getAnnotation(Handle.class);
                if (handle != null) {

//得到Handle注解类型成员变量的值
                    String name = handle.value();
                    Method[] methods = handlerType.getDeclaredMethods();
                    if (methods != null && methods.length > 0) {
                        for (final Method method : methods) {
                        //得到handle注解类型方法    

Handle handleMessage = method.getAnnotation(Handle.class);
                            if (handleMessage != null) {
                                String methodValue = handleMessage.value();

//如果注解值value相同(建议使用handler成员的引用名)
                                if (name.equals(methodValue)){
                                    try {
                                        field.setAccessible(true);

//这里的field就是成员变量handler,注意参数handler是

//我们的Acitivity实例,这里注入匿名内部类的实现
                                        field.set(handler, new Handler(){
                                            @Override
                                            public void handleMessage(Message msg){
                                                //调用Acitvity的handle注解类型方法

try {
                                                    method.invoke(handler, msg);
                                                } catch (Exception e) {
                                                    LogUtils.e(e.getMessage(), e);
                                                } 
                                            }
                                        });
                                    } catch (Throwable e) {
                                        LogUtils.e(e.getMessage(), e);
                                    }
                                }
                            }
                        }
                    }
                }



然后在Activity中我们就可以像其他控件一样注入handler了,

    @Handle("myHandler ")
    private Handler myHandler = null;


    @Handle("myHandler ")
    public void handleMsg(Message msg){
        if (msg.what == 1){
            aniEnd = true;
            showMap();
        }
    }

现在代码看起来更加统一风格了,优雅的设计,简易的代码始终是我们喜欢的风格吧,

第一次写博客,有点乱,后面继续,回家吃饭了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值