在Android中在后台执行多线程异步任务的时候,当异步任务执行成功之后就需要回调主线程,在Android中有很多种回调的方式,我这边自己根据自己开发出的项目,整理出了自己的一个异步回调的工具类,是基于Handler写出来的,但是比以往的方式使用起来更方便一点:
在自己项目中,使用到了USB协议,整个协议的完成需要自己封装,在走USB协议的时候自己选择的是异步处理,当然Android系统是允许USB协议中的数据发送和接受是在主线程执行的。自己还是觉得把它放在异步任务好一点。在相关的一些初始化操作执行完成之后,这个时候就需要回调,在界面上显示出,我现在和设备所处的状态是什么样子的。此时自己使用Handler写了一个,马上就搞定了,代码也很简单;
使用Handler,让Handler去执行某一个固定的方法,这样就可以,代码如下:
public class TestThread extends Thread { private OnMainCallBack mOnMainCallBack; private MyHandler mHandler; public TestThread(OnMainCallBack mainCallBack){ this.mOnMainCallBack=mainCallBack; mHandler=new MyHandler(); } @Override public void run() { super.run(); //*******do something //*******线程中的操作执行完了,需要回调通知界面去进行更新 mHandler.sendMessage(mHandler.obtainMessage(MyHandler.WHAT_ON_MAIN, "更新数据...")); } private class MyHandler extends Handler{ public static final int WHAT_ON_MAIN=1; public MyHandler() { super(Looper.getMainLooper()); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case WHAT_ON_MAIN://在主线程中回调该方法,这样在毁掉中就可以进行相关UI操作 if(mOnMainCallBack!=null)mOnMainCallBack.onMainCallBack(msg.obj.toString()); break; } } } /**主线程回调接口*/ public interface OnMainCallBack{ /**回调方法*/ void onMainCallBack(String data); } }
当线程中的run,执行完操作只要需要通知界面去更新数据,这个时候让handler去执行需要回调的方法即可。这样方法回调就在主线程内了,在回调的方法中就可以直接去更新UI界面了
这样做确实能实现我们所需要的,但是如果是多线程去执行,需要new 多个像这样的线程去进行异步,岂不是也要new 多个Handler,那我们就把Handler的代码抽出来吧,以免重复的去new。代码如下
public class MyHandler extends Handler{ public static final int WHAT_ON_MAIN=1; private static MyHandler sMyHandler; private TestThread.OnMainCallBack mOnMainCallBack; public static MyHandler createHandler(TestThread.OnMainCallBack callBack){ if(sMyHandler==null)sMyHandler=new MyHandler(); sMyHandler.setCallBack(callBack); return sMyHandler; } private MyHandler() { super(Looper.getMainLooper()); } public void setCallBack(TestThread.OnMainCallBack mainCallBack){ this.mOnMainCallBack=mainCallBack; } @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case WHAT_ON_MAIN://在主线程中回调该方法,这样在毁掉中就可以进行相关UI操作 if(mOnMainCallBack!=null)mOnMainCallBack.onMainCallBack(msg.obj.toString()); break; } } }
代码抽出来之后,给了一个单例模式,这样在多线程里就可以被复用了,这样避免了new 多个handler的情况,但是Ne,多线程可能需要执行的业务逻辑不一样,那么回调的方法也会不一样,我们难道需要一个个的像setCallBack(TestThread.OnMainCallBack mainCallBack)这样去重复的去写set方法吗?是不是就很麻烦了,而且这样Handler的解耦性也不够好,要根据不同的业务逻辑去写很多歌setCallBack的方法。其实呢,有一个好的方法能解决这个问题,那就是Java的反射,通过反射的原理去实现你在主线程执行任何方法。对Java反射机制不够了解的可以去看看这篇文章:[http://blog.csdn.net/u013238950/article/details/52164402]
在执行反射去调用方法的时候,有几个地方你是要必须能获取到的,一个是该方法的引用、一个是该方法的class、一个是你调用方法的方法名、一个是你调用方法的参数类型以及对于的参数;需要知道这么多的东西,那我们就需要定义一个类去存放这些信息,在执行反射的时候把这个类直接作为参数参入去执行。我这边为了方便写了一个Builder的类,去记录这些信息,在Handler中期反射调用方法。
public class InvokeUtils extends Handler { private final int WHAT_ON_MAIN_EXCUTE_METHOD =1;//Handler主线程执行 private static InvokeUtils sInvoke; protected InvokeUtils(){ super(Looper.getMainLooper()); } public static InvokeUtils getInstance(){ if(sInvoke ==null) sInvoke =new InvokeUtils(); return sInvoke; } /**创建方法执行的builder*/ public static MethodExcuteBuilder createBuilder(){ return new MethodExcuteBuilder(); } /**在主线程反射执行方法*/ public void onMainExcute(MethodExcuteBuilder builder){ if(builder==null)return; this.sendMessage(this.obtainMessage(WHAT_ON_MAIN_EXCUTE_METHOD, builder)); } /**反射执行方法*/ public void excuteMethod(MethodExcuteBuilder builder){ try { Method m=builder.clazz.getDeclaredMethod(builder.method, builder.parameterTypes); m.setAccessible(true); m.invoke(builder.objClazz, builder.params); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); }catch (Exception e){ e.printStackTrace(); } } @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case WHAT_ON_MAIN_EXCUTE_METHOD://在主线程内去执行方法 excuteMethod((MethodExcuteBuilder) msg.obj); break; } } /**方法执行的builder*/ public static class MethodExcuteBuilder { private Object objClazz; private Class clazz; private String method; private Class[] parameterTypes; private Object[] params; /**设置调用方法的实体类*/ public MethodExcuteBuilder setObjClazz(Object objClazz) { this.objClazz = objClazz; return this; } /**设置方法的名称*/ public MethodExcuteBuilder setMethod(String method) { this.method = method; return this; } /**设置class*/ public MethodExcuteBuilder setClazz(Class clazz) { this.clazz = clazz; return this; } /**设置方法的参数类型*/ public MethodExcuteBuilder setParameterTypes(Class... parameterTypes) { this.parameterTypes = parameterTypes; return this; } /**设置方法的参数*/ public MethodExcuteBuilder setParams(Object... params) { this.params = params; return this; } private MethodExcuteBuilder(){}; /**反射执行方法*/ public void excuteMethod(){ InvokeUtils.getInstance().excuteMethod(MethodExcuteBuilder.this); } /**在主线程中反射执行方法*/ public void onMainExcute(){ InvokeUtils.getInstance().onMainExcute(MethodExcuteBuilder.this); } } }
这样记录下了需要反射执行的方法的信息到MethodExcuteBuilder中,为了方便代码书写,我加入了链式调用的方式。在InvokeUtils去调用onMainExcute(MethodExcuteBuilder builder)和excuteMethod(MethodExcuteBuilder builder)去执行需要反射的方法;或者可以在MethodExcuteBuilder中调用excuteMethod()和onMainExcute()去执行需要反射的方法。
在使用反射了之后,Handler再也不再受到CallBack的拘束了,使用反射的方式去调用任何CallBack里的任何回调方法。这样实现了解耦的操作,能让自己的代码变得更加的灵活了。
public class TestThread extends Thread { private OnMainCallBack mOnMainCallBack; public TestThread(OnMainCallBack callBack){ mOnMainCallBack=callBack; } @Override public void run() { super.run(); //*******do something //*******线程中的操作执行完了,需要回调通知界面去进行更新 //mHandler.sendMessage(mHandler.obtainMessage(MyHandler.WHAT_ON_MAIN, "更新数据...")); //使用解耦的方式去进行调用 InvokeUtils.MethodExcuteBuilder builder=InvokeUtils.createBuilder(); builder.setObjClazz(mOnMainCallBack)//设置调用方法的类的引用 .setClazz(OnMainCallBack.class)//设置class .setMethod("onMainCallBack")//设置需要调用的方法名 //设置参数的类型, // !!!注意!!! //定义的方法参数类型不能为int、boolean等基础数据类型,要写成Integer、Boolean等; // 否则无法调用方法 .setParameterTypes(String.class) .setParams("更新数据...");//设置方法需要传入的参数 //调用方式1 builder.onMainExcute();//在主线程执行,如果只是想执行改方法调用excuteMethod()即可 //调用方式2 //InvokeUtils.getInstance().onMainExcute(builder); } /**主线程回调接口*/ public interface OnMainCallBack{ /**回调方法*/ void onMainCallBack(String data); } }
如果大家觉得我的代码还存在瑕疵或者自己有更好的方法,希望大家能提出意见,谢谢!