您的位置:首页 > 移动开发 > Android开发

Android多线程异步任务,在主线程中回调

2017-06-29 18:44 513 查看

Android多线程异步任务,在主线程中回调

在Android中在后台执行多线程异步任务的时候,当异步任务执行成功之后就需要回调主线程,在Android中有很多种回调的方式,我这边自己根据自己开发出的项目,整理出了自己的一个异步回调的工具类,是基于Handler写出来的,但是比以往的方式使用起来更方便一点:

实现的思路

在自己项目中,使用到了USB协议,整个协议的完成需要自己封装,在走USB协议的时候自己选择的是异步处理,当然Android系统是允许USB协议中的数据发送和接受是在主线程执行的。自己还是觉得把它放在异步任务好一点。在相关的一些初始化操作执行完成之后,这个时候就需要回调,在界面上显示出,我现在和设备所处的状态是什么样子的。此时自己使用Handler写了一个,最后一步步的修改最后封装出了一个工具类。初版实现的代码,马上就搞定了,代码也很简单;

使用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。代码如下

抽出的MyHandler代码如下:

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(XXXXCallBack mainCallBack)这样去重复的去写set方法吗?是不是就很麻烦了,而且这样Handler的解耦性也不够好。要根据不同的业务逻辑去写很多个setCallBack的方法。其实呢,有一个好的方法能解决这个问题,那就是Java的反射,通过反射的原理去实现你在主线程执行任何方法。对Java反射机制不够了解的可以去看看这篇文章:java反射机制之Method invoke执行调用方法例子

使用反射去执行

在反射去执行调用方法的时候,有几个参数你是要必须能获取到的,一个是该方法的引用、一个是该方法的class、一个是你调用方法的方法名、一个是你调用方法的参数类型以及对于的参数。需要知道这么多的东西,那我们就需要定义一个类去存放这些信息,在执行反射的时候把这个类直接作为参数传入方法去执行。我这边为了方便写了一个Builder的类,去记录这些信息,在Handler中期反射调用方法去执行就好了。

改良后的MyHandler代码如下:

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里的任何方法就都不会存在问题了。这样实现了解耦的操作,能让自己的代码变得更加的灵活。

调用Demo如下:

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);
}
}


如果大家觉得我的代码还存在瑕疵或者自己有更好的方法,希望大家能提出意见,谢谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息