Android中异步实现的两个方式,一个是AsyncTask,一个是Handler+Thread。两个方式基本上都是在每个activity里去创建一个AsyncTask或者Handler+Thread。自己也如此做过几个项目,发现的有很多代码冗余,而且不利于管理。自己也在琢磨有什么办法解决这些问题,想着能不能将handler和Thread独立出来。自己试着写了个demo,也算是抛砖引玉,大家评价一下如何。
这里使用的是handler+thread的方式,与一般的结构不同。Handler和Thread的创建并没有在Activity中。App中只用了一个全局的handler,还有有个专门管理子线程的类。
结构如上图,Activity A、B通过调用AppRest提供的接口,完成子线程任务的创建。线程任务结束会通过handler发送信息到AppHandler。在这个过程中activity的实例会通过依次传递,最终到AppHandler。AppHandler通过instanceof方法判断调用哪个回调方法。
这样的设计实现了Activity,Handler,Thread三者的分离。
XML代码就是一显示文本内容的Textview和一个页面跳转的Button,这里就不贴代码了。
代码:
Base类:
package com.example.testh;
import android.app.Activity;
/**
* @author SunnyCoffee
* @date 2013-8-4
* @version 1.0
* @desc activity的基类,泛型用于指明回调函数的返回值
*/
public abstract class Base<T> extends Activity {
/**
* 回调方法。
*
* @param T
*/
public abstract void deal(T result);
}
Activity A:
package com.example.testh;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
//通过泛型指明回调函数返回值类型
public class A extends Base<String> implements OnClickListener {
private Button btn;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.a);
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(this);
tv = (TextView) findViewById(R.id.tv);
AppRest.login(this, "zhangsan", "123456");// 子线程登录操作
}
// 回调函数
@Override
public void deal(String result) {
tv.setText(result);
}
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(this, B.class);
startActivity(intent);
}
}
Activity B:
package com.example.testh;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class B extends Base<String> implements OnClickListener {
private Button btn;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.b);
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(this);
tv = (TextView) findViewById(R.id.tv);
AppRest.getGoodsList(this);
}
@Override
public void deal(String result) {
tv.setText(result);
}
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(this, A.class);
startActivity(intent);
}
}
AppRest 类:
package com.example.testh;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
/**
* @author SunnyCoffee
* @date 2013-8-4
* @version 1.0
* @desc 调用子线程工作的接口
*/
public class AppRest {
private static final int THREAD_POOL_SIZE = 5;
private static Handler handler = AppHandler.getInstance();
private static ExecutorService service = Executors// 线程池
.newFixedThreadPool(THREAD_POOL_SIZE);
/**
* 启动登录线程的接口
*
* @param context
* @param name
* @param passwd
*/
public static void login(final Context context, final String name,
final String passwd) {
Runnable r = new Runnable() {
@Override
public void run() {
/**
* TODO 连接服务器获取数据的操作写在这里,返回结果将通过handler发送
*
* 这里假设服务器返回的都是文本类型的数据。 如果返回的是json对象,建议这里使用gson进行解析javabean,
* 然后通过bundle .putSerializable(key,value)方式发送。
* 这会要求javabean实现Serializable接口
*
*/
Message msg = handler.obtainMessage();
Bundle b = new Bundle();
b.putString("result", "服务器返回登录结果:hello," + name);// 模拟数据
msg.setData(b);
msg.obj = context;
handler.sendMessage(msg);
}
};
service.execute(r);
}
/**
* 获得商品列表数据
*
* @param context
*/
public static void getGoodsList(final Context context) {
Runnable r = new Runnable() {
@Override
public void run() {
// TODO 实际的操作
Message msg = handler.obtainMessage();
Bundle b = new Bundle();
b.putString("result", "服务器返回的商品列表数据");
msg.setData(b);
msg.obj = context;
handler.sendMessage(msg);
}
};
service.execute(r);
}
}
AppHandler 类:
package com.example.testh;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
/**
* @author SunnyCoffee
* @date 2013-8-4
* @version 1.0
* @desc 整个APP的handler,用于处理子线程向主线程的信息传递
*/
public class AppHandler extends Handler {
private static AppHandler handler = new AppHandler();
// 单例模式
private AppHandler() {
}
public static AppHandler getInstance() {
return handler;
}
@Override
public void handleMessage(Message msg) {
Object obj = msg.obj;
if (msg == null || obj == null)
return;
Bundle b = msg.getData();
if (b == null)
return;
/**
* obj即为Activity的实例,这里建议进行生命周期的判断,比如puse destroy。
* 如果确定服务器返回值为文本类型且要把json的解析放在主线程,建议使用这种方式 if (obj instanceof Base) {
* String json = b.getString("result"); ((Base) obj).deal(result) }
*
*/
String result = b.getString("result");
if (obj instanceof A) {
((A) obj).deal(result);
} else if (obj instanceof B) {
((B) obj).deal(result);
}
}
}
这个设计现在还是一个初步设计,觉得和AsyncTask的设计很相似,很多可以借鉴AsyncTask的思想。当然还有好多自己的想法没有涉及。
1.当一个Activity中有两个以上的线程的时候,简单的方法就是通过在Activity再创建一个方法供AppHandler进行回调。
2.如果服务器返回的只用文本信息(如json等),可以考虑将json放在主线程中解析,这样回调函数就能有一个统一的参数类型String。
3.等待页面的问题,将等待页面写在AppRest类中以实现代码复用。
4.将AppHandler和AppRest合二为一?这个我现在还没打算这么做,现在这样觉得更清晰条理。
5.关于泛型的问题。因为handler使用了单例,所以无法使用泛型。下一步考虑不再使用static对象或方法。
6.如果线程的调用者不是activity而是Fragment,这个问题也再考虑中。
改进方案参考: