由于刚开始学习Android,所以肯定有很多见解不成熟。希望看到的人能多多指正,有问题的地方可以提在评论,多谢赐教!
在Android程序中,如果你阻塞一个UI线程多达5秒之后,一般会造成ANR(Application Not Responding)情况的出现,导致程序出现无响应的提示。所以如果要做一些比较耗时的操作,例如与服务器进行数据交互,则需要重开一条线程进行处理。如果新开一条线程那么两条线程之间的通信就迫在眉睫。
1、异步消息处理机制基本概念
Android中的异步消息处理机制主要有四部分组成,Message、Handler、MessageQueue和Looper。下面详细介绍一下这四个内容:
①Message
Message是线程之间的消息传递者,可以携带少量的信息,用于不同线程之间的数据传递。它的属性字段主要关注:arg1、arg2、what、obj。其中arg1和arg2是用来存放整型数据的;what是用来保存消息标示的;obj是Object类型的任意对象。
获得Message的构造方法最好的方式是调用Message.obtain() 和 Handler.obtainMessage()方法。这两个方法会先去MessagePool中去找,如果没有可用的Message则会调用new Message()方法新建。
②Handler
Handler是消息处理者,主要用于发送和处理消息。发送消息一般使用Handler的sendMessage()方法,发出去的消息最终会传递到Handler的handleMessage()方法。一般我们都会重写handleMessage这个方法,里面放我们需要做的耗时操作。
③MessageQueue
MessageQueue是消息队列的意思。主要存放Handler发送的消息。每个线程中只会有一个MessageQueue对象。
④Looper
Looper是MessageQueue的管家,MessageQueue对象就是在Looper的构造函数中创建出来的。调用Looper的Loop()方法,会进入一个无限循环中,每当发现MessageQueue中存在一条消息就会取出来,传到Handler中的handleMessage()方法中。
2、异步消息处理机制基本流程
首先可以参考鸿洋大神的博客,这里分析的很透彻。然后从他的博客偷出来一段内容:
1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。
4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。
5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
好了,总结完成,大家可能还会问,那么在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。
这里需要补充一下就是当子线程中开启Loop消息循环,在线程任务完成以后需要关闭消息循环,方法如下:
//完成了操作要结束子线程中的消息队列的循环
childHandler.getLooper().quit();
3、使用实例
public class MainActivity extends Activity implements View.OnClickListener {
private TextView tv;
private Button update_btn;
public final static int UPDATE_TEXT =1, CHILD_UPDATE_TEXT = 2;
private Handler childHandler;
private Handler mainHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case UPDATE_TEXT:
tv.setText("nice to meet u!");
break;
case CHILD_UPDATE_TEXT:
tv.setText("this is child thread!");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
update_btn = (Button) findViewById(R.id.update_btn);
update_btn.setOnClickListener(this);
//开启准备测试的子线程
new ChildThread().start();
}
@Override
protected void onDestroy() {
super.onDestroy();
//完成了操作要结束子线程中的消息队列的循环
childHandler.getLooper().quit();
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.update_btn:
new Thread(new Runnable() {
@Override
public void run() {
//发送消息到主线程
Message message = Message.obtain();
message.what = UPDATE_TEXT;
mainHandler.sendMessage(message);
//发送消息到子线程
Message cMessage = childHandler.obtainMessage();
childHandler.sendMessage(cMessage);
}
}).start();
break;
default:
break;
}
}
public class ChildThread extends Thread {
@Override
public void run() {
super.run();
/***
* 初始化消息循环队列,需要在handler之前创建,因为handler中的sendMessage方法中会用到
* MessageQueue对象,而这个对象是在Looper的构造函数中初始化的
*/
Looper.prepare();
childHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("zwf", "child thread get message!");
try {
//子线程做一些耗时操作但是不能做更新UI相关的操作
sleep(2000);
Message message = Message.obtain();
message.what = CHILD_UPDATE_TEXT;
mainHandler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//启动子线程消息队列循环
Looper.loop();
}
}
}```
上面的实例中有一个TextView和一个Button。点击按钮,会发出两条消息,分别是发布到主线程和另外一个测试子线程中。主线程收到消息直接将界面文本修改为nice to meet u!,测试子线程有一个延时操作,两秒钟后将文本消息修改为this is child thread!
先写这么多,关于在子线程中更新UI等明天整理一下在写。