Handler概念
主要用于异步消息的处理,它的设计通常用来处理相对耗时比较长的操作。
Android的单线程机制,只有主线程可以对UI(界面)进行更新,为了Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新。
由于Handler是运行在主线程中(UI线程中), 而且它的设计可以使它与子线程通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。
子线程对主线程发信息Message对象(Message包含的信息可以是int,object类型)
使用方法:在初始化Handler对象时重写的handleMessage方法来接收Message并进行相关操作。
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {};
};
API上对这句的注释是:Handler子类必须实现这个方法以接收信息,在这个方法里可以实现执行更新UI界面(譬如更新ListView等操作)
<span style="color:#333333;"> /**
* </span><span style="color:#ff0000;">Subclasses must implement this to receive messages.</span><span style="color:#333333;">
*/
public void handleMessage(Message msg) {
}</span>
耗时操作一般执行在非UI线程中,比如AsyncTask和AsyncTaskLoader都可以实现耗时操作,然后更新UI界面。这些也都是为了解决安卓线程设计上的问题的办法。
常用类
五个:Handler、Looper、Message、MessageQueue
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。Handler类的主要作用:(有两个主要作用)1)、在工作线程中发送消息;2)、在主线程中获取、并处理消息。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
如何使用
当我们开启线程请求了一段网络数据或数据库查询:
<pre name="code" class="java">new Thread(){
<span style="white-space:pre"> </span>public void run(){
<span style="white-space:pre"> </span>//下载数据
<span style="white-space:pre"> </span>..
<span style="white-space:pre"> </span>获得了一段字符串或cursor----》mMessage
<span style="white-space:pre"> </span>//发送到handlerMessage进行UI更新操作
<span style="white-space:pre"> </span>*******
<span style="white-space:pre"> </span>1、获取message对象
<span style="white-space:pre"> </span>2、给message对象传值
<span style="white-space:pre"> </span>3、发送message
<span style="white-space:pre"> </span>*******
<span style="white-space:pre"> </span>}
}.start();
Message
使用Message需要注意4点:1、Message虽然也可以通过new来获取,但是通常使用Message.obtain()或Handler.obtainMessage()方法来从消息池中获得空消息对象,以节省资源;2、如果一个Message只需要携带简单的int型数据,应优先使用arg1和arg2属性来传递数据,这样比其他方式节省内存;3、尽可能使用Message.what来标识信息,以便用不同的方式处理Message;4、如果需要从工作线程返回很多数据信息,可以借助Bundle对象将这些数据集中到一起,然后存放到obj属性中,再返回到主线程。
获取message对象:
- obtainMessage()---》Handler
- 通过构造方法Message()
- Obtain()--->Message
为message对象传值
message携带数据,有四个属性:
arg1、arg2:携带int值
obj:携带一个对象---》Object
what:int,用来识别Messag
分发消息的一些方法(handler)
post(Runnable)可以传一个线程,这个线程运行在主线程中!所以事实上一些轻量级的操作,可以这样来实现,下面有介绍。
postAtTime(Runnable,long)
postDelayed(Runnable long)sendEmptyMessage(int)sendMessage(Message)sendMessageAtTime(Message,long)sendMessageDelayed(Message,long)
这些方法底层经过层层调用最终调用的都还是sendMessageAtTime(),然后由此调用了Handler私有的函数enqueueMessage,把handler对象赋值给msg.target,调用queue.enqueueMessage,进入了消息队列机制中。
以上post类方法允许你排列一个Runnable对象到主线程队列中,sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.
例子:更新进度条demo
发送一段数据:
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Message msg = handler.obtainMessage();
msg.arg1 = i; //arg1和arg2参数都是int类型
<span style="white-space:pre"> </span>msg.what=1;<span style="white-space:pre"> </span>//设置消息的编号
handler.sendMessage(msg);//发送数据
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
在handler里接收数据:
<span style="white-space:pre"> </span>// 处理消息-----》处理子线程发过来的消息;----handlerMessage,更新UI
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
int i = msg.arg1;
progressbar.setProgress(i);//更新进度条
};
};
主线程给子线程发送消息:
原理一样,只是把handler的实例化放到了子线程,发送消息的方法执行在UI线程;
public class MainActivity extends Activity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread() {
public void run() {
Looper.prepare();
handler = new Handler() {
public void handleMessage(android.os.Message msg) {
Toast.makeText(MainActivity.this,
Thread.currentThread().getName(), 0).show();
};
};
Looper.loop();
}}.start();
}
public void btnClick(View view)
{
handler.sendEmptyMessage(1);
}
}
Looper
Looper对象用来为一个线程开启一个消息循环,从而操作MessageQueue;默认情况下,Android创建的线程没有开启消息循环Looper,但是主线程例外。系统自动为主线程创建Looper对象,开启消息循环;所以主线程中使用new来创建Handler对象。而子线程中不能直接new来创建Handler对象就会异常。
子线程中创建Handler对象,步骤如下:
Looper.prepare();
<span style="white-space:pre"> </span>Handler handler = new Handler() {
<span style="white-space:pre"> </span> //handlemessage(){}
<span style="white-space:pre"> </span>}
Looper.loop();
Looper的loop方法可以循环的从消息队列中监听消息,属于阻塞式操作。
线程这块涉及的概念比较多,Looper,Message,Handler,由此衍生的有HandlerThread、java.util.concurrent、Task、AsyncTask
Looper又是什么呢? 其实Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,但是Looper和Handler没有什么关系,我们从开源的代码可以看到Android还提供了一个Thread继承类HanderThread可以帮助我们处理,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler中去来实现一个线程同步机制,Looper对象的执行需要初始化Looper.prepare方法就是昨天我们看到的问题,同时推出时还要释放资源,使用Looper.release方法。
简单来说就是:
每一个线程只能有一个Looper对象,Handler持有一个Looper,Handler、Looper持有一个MessageQueue(消息队列)。
附:Handler、Looper、Message、MessageQueue之间的关系: