Hanlder和message机制:
【概述】
android的Handler+Thread机制是android实现异步任务或者说实现更新UI界面最本质的方式,Handler+thread机制区别于Async Task机制的不同在于Async Task封装了Handler(因为在其源码实现中就已经获取了uiThread的Handler实现了其looper)并用线程池来实现。这两者在本质上区别不大。因为Handler+Thread是最本质的实现异步任务的方式,所以其效率肯定更高于Async Task。但是Async Task是android的改良品,代码更易读,在代码维护方面是很大的优势。
1、为何需要多线程
在日常应用中,我们通常需要处理一些“后台,用户不可见”的操作,例如说,我们需要下载一个音乐,要是你的应用必须等用户下载完成之后才可以进行别的操作,那肯定让用户非常的不爽。这时候,我们通常的做法是,让这些操作去后台执行,然后等后台执行完毕之后,再给用户弹出相应的提示信息。这时候,我们就需要使用多线程机制,然后通过创建一个新的线程来执行这些操作。
明白了,实现需求,我们就准备着手实现了。但是,经过进一步的了解,我们悲剧的发现,android中的线程机制是,只能在UI线程中和用户进行交互。当我们创建了一个新线程,执行了一些后台操作,执行完成之后,我们想要给用户弹出对话框以确认,但是却悲剧的发现,我们根本无法返回UI主线程了。(UI线程就是你当前看到的这些交互界面所属的线程)。
这时候,我们如果想要实现这些功能,我们就需要一个android为我们提供的handler和message机制。
2、解说Handler下的编程机制
我们通常在UI线程中创建一个handler,handler相当于一个处理器,它主要负责处理和绑定到该handler的线程中的message。每一个handler都必须关联一个looper,并且两者是一一对应的,注意,这点很重要哦!此外,looper负责从其内部的messageQueue中拿出一个个的message给handler进行处理。因为我们这里handler是在UI线程中实现的,所以经过这么一个handler、message机制,我们就可以回到UI线程中了。
handler:处理后台进程返回数据的工作人员。
message:后台进程返回的数据,里面可以存储bundle等数据格式
messageQueue:是线程对应looper的一部分,负责存储从后台进程中抛回的和当前handler绑定的message,是一个队列。
looper:looper相当于一个messageQueue的管理人员,它会不停的循环的遍历队列,然后将符合条件的message一个个的拿出来交给handler进行处理。
注意,handler是在UI线程中声明的,如果我们直接用类似代码执行一个线程的话,实际上并没有创建一个新的线程,因为handler已经跟默认的UI线程中的looper绑定了。
如果有兴趣的话,可以去看下Handler的默认空构造函数便知道原因了,里面直接绑定了当前UI线程的looper。
3、示例小程序:
- public class MainActivity extends Activity implements OnClickListener {
- private Button btnTXT;
- private TextView tvTXT;
- private StringBuffer returnMsg;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- btnTXT = (Button)findViewById(R.id.btnTXT);
- tvTXT = (TextView)findViewById(R.id.tvTXT);
- btnTXT.setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- returnMsg = new StringBuffer();
- // 创建一个包含Looper的线程,这里如果没有HandlerThread的调用,会直接将后边的MyRunnable放到UI线程队列(myHandler.post(new MyRunnable()))
- HandlerThread handlerThread = new HandlerThread("handler_thread");
- handlerThread.start(); // 启动自定义处理线程
- myHandler = new MyHandler(handlerThread.getLooper()); // 将handler绑定到新线程
- myHandler.post(new MyRunnable()); // 在新线程中执行任务
- }
- /** 主线程Handler,可以与UI控件交互 */
- Handler mainHanlder = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- if(msg.what == 0) {
- tvTXT.setText(returnMsg.toString()); // 与主线程控件打交道(直接访问)
- }
- }
- };
- /** 构造Hanlder,不可与UI控件直接交互 */
- private MyHandler myHandler = null;
- private class MyHandler extends Handler{
- /**
- * 使用默认的构造函数,会将handler绑定当前UI线程的looper。
- * 如果想使用多线程这里是不能使用默认的构造方法的。
- */
- public MyHandler(){
- super();
- }
- /** 构造函数,自定义looper */
- public MyHandler(Looper looper) {
- super(looper);
- }
- // 处理具体的message消息,继承自父类的方法
- @Override
- public void handleMessage(Message msg) {
- int what = msg.what;
- Bundle bundle = (Bundle)msg.obj; // 提取bundle中的信息
- String name = bundle.getString("name");
- String sex = bundle.getString("sex");
- boolean marry = bundle.getBoolean("marray");
- int age = bundle.getInt("age");
- StringBuffer strBuf = new StringBuffer(); // 拼接bundle信息
- strBuf.append("what = ").append(what).append("\n\n");
- strBuf.append("name = ").append(name).append("\n");
- strBuf.append("sex = ").append(sex).append("\n");
- strBuf.append("marry = ").append(marry).append("\n");
- strBuf.append("age = ").append(age).append("\n\n");
- strBuf.append("http://blog.csdn.net/sunboy_2050");
- returnMsg = returnMsg.append(strBuf); // 保存要显示的结果
- mainHanlder.sendEmptyMessage(0); // 向主线程mainHanlder发送消息,与UI控件交互显示结果
- super.handleMessage(msg);
- }
- }
- // 构造Runnable,处理后台业务逻辑,如下载
- private class MyRunnable implements Runnable{
- @Override
- public void run() {
- try {
- Message msg = Message.obtain(myHandler); // 捕获myHandler消息
- msg.what = 10;
- Bundle bundle = new Bundle(); // 封装bundle信息
- bundle.putString("name", "yanggang");
- bundle.putString("sex", "pure boy");
- bundle.putBoolean("marry", false);
- bundle.putInt("age", 18);
- msg.obj = bundle;
- long thID = Thread.currentThread().getId();
- returnMsg.append(thID).append(" : send msg start...").append("\n");
- msg.sendToTarget(); // 向myHandler发送消息
- Thread.sleep(3000);
- } catch (Exception e) {
- Log.i("", "Runnable send msg error...");
- e.printStackTrace();
- }
- }
- }
- }
运行结果: