您的位置:首页 > 其它

6.Handler机制

2015-05-29 20:19 323 查看
转载请标明出处:

http://blog.csdn.net/yujun411522/article/details/46049131

本文出自:【yujun411522的博客】

对android开发了解多一些的开发者应该都知道handler在android中的重要性。

Android中的消息机制是针对某一个具体线程的。一个线程只能有一个looper,通过looper来不停取出消息队列中的消息。

在默认创建的线程中是不具备消息队列消息循环的,如果想让其被这些功能,需要调用Looper.prepare()绑定Looper实例,调用Looper.loop()进入消息循环

非UI线程如何与UI线程通信部分我们看出UI线程中有两个原则:

1.不要阻塞ui线程

2.不要在非ui线程修改ui线程元素

那么问题来了:在子线程中进行耗时操作后如何将通知ui线程更新数据呢?这就是handler机制的主要用途。

官方文档给出的用途有两个:

1.在ui线程未来的某个时间点调度message和runnables(postAtTime,postDelay,sendMessageAtTime等);

2.让一个action在另一个子线程中运行(子线程中执行耗时操作,完成之后通知UI thread更新)。

6.1 常见用法

先介绍一下与之配合使用的几个重要概念:

6.1.1 MessageMessage队列

message是一个可以包含描述和任意类型数据对象的类,可以将Message发送给handler进行处理。下面从代码角度来分析一下:

1 它实现了Parcelable接口,可以用于系统之间传递数据: public final class Message implements Parcelable;

2 比较中要的成员变量:Handler target(处理该message的handler),Runnable callback(定义runnable,主要作用是将Runnable可以封装成message对象来处理,后面有分析),pubic int what(指明消息类型,使用较多),,public
Object obj(可以是的任意对象);

3 如何创建message对象:虽然提供了public 构造方法,但是建议使用Message.obtain()或者Message.obtain(Message)。因为内部使用了message池,避免了过多的创建message对象。上源码:




它还重载了很多方法,都是调用obtain来实现的如:public static Message obtain(Message)



再看看如果回收:




4 sentToTarget函数:






MessageQueue顾名思义就是Message队列

着重分析两个函数

1 enqueueMessage(Message msg,long when)




可以看出队列是按照时间前后顺序的,时间小的在队首。

2 next()从队列中取出来一个message

for(;;){//不停循环取数据

synchronized (this ) {

// Try to retrieve the next message. Return if found.

final long now = SystemClock.uptimeMillis();

final Message msg = mMessages ;

if (msg != null ) {

final long when = msg.when;

if (now >= when) {//如果时间该message已经到了到执行时间

mBlocked = false ;//不阻塞

mMessages = msg.next;//队尾指向下一个

msg.next = null ;

if (false ) Log.v( "MessageQueue" , "Returning
message: " + msg);

msg.markInUse();//标记正在使用

return msg;

} else {

//下一次pool时间间隔,native层调用

nextPollTimeoutMillis = ( int ) Math.min(when - now, Integer.MAX_VALUE);

}

}

//....

}

}

6.1.2 Runnable

实际可以看成是message。看看Handler中是如何处理的post(Runnable)






调用的是处理message的方法,再看getPostMessage(Runnable):






尼玛,真省事,就是将Runnable包装到message中,作为该message一个成员变量。

6.1.3 Looper

不断的将messageQueue中的信息取出来执行

重要的变量:

1. static final ThreadLocal<Looper> sThreadLocal;用来存放和线程相关的信息。也就是将线程和Looper绑定到一起。

2. final MessageQueue mQueue 要操作的消息队列。也就是一个looper对应一个messageQuene

重要方法:

1 public static void prepare:




就是将Thread 和looper进行绑定。可以看出一个线程有且只能有一个looper。在和线程进行绑定时调用了Looper的构造方法Looper():




初始化messagequeue,将当前线程赋值给looper对象mThread变量。

再看looper()函数:

2 public static void loop()
{

//先判断是否绑定了looper,如果该线程没有绑定Looper对象,则提示应该先调用prepare方法。

Looper me = myLooper();

if (me == null ) {

throw new RuntimeException( "No Looper; Looper.prepare() wasn't called on this thread.");

}



MessageQueue queue = me. mQueue;

while (true ) {

//从queue中取message并进行分发message

Message msg = queue.next(); // might block

if (msg != null ) {

if (msg.target == null) {

// No target is a magic identifier for the quit message.

return ;

}

//分发message

msg.target.dispatchMessage(msg);

//回收

msg.recycle();

}

}

}

先看myLooper():就是返回线程所绑定的looper实例,可以看出如果没有绑定则报错。

再看dispatchMessage(msg)这个方法实际是Handler类中定义的(message中的target对象就是Handler类型)。

3 public void dispatchMessage(Message msg) {

if (msg.callback != null) {

//执行message中callback中的run()方法

handleCallback(msg);// message.callback.run();

} else {

if (mCallback != null) {

//如果创建handler时指定了callback,执行该callback中的handleMessage,返回

if (mCallback .handleMessage(msg)) {

return;

}

}

//message中没有定义callback(runnable)时才执行自己实现的handleMessage。

handleMessage(msg);

}

}

其中 mCallback 是Callback类型

public interface Callback
{

public boolean handleMessage (Message
msg);

}

4 Looper中还有一个重要的方法,这个方法一般不用,但在后面的分析中会涉及到:prepareMainLooper()



我们在主线程中创建looper对象时并没有主动调用prepare方法,是因为这里已经实例化一个mainLooper对象给主线程使用。



6.1.4 Handler

handler类的主要作用就是发送和处理message对象的。

先看看有哪些重要的成员变量

1 final Looper mLooper:该线程绑定的Looper实例

2 final MessageQueue mQueue:mLooper中的成员变量

3 final Callback mCallback:自定义handleMessage方法接口

再来看重要的方法

1 构造方法:public Handler(),

public Handler(){

//..

mLooper=Looper.myLooper();//获得该线程绑定的Looper实例

if(mLooper==null){

throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");

}

mQueue = mLooper.mQueue;

mCallback=null;

}

2 带参数的构造方法:public Handler(Looper,Callback)

public Handler(Looper looper,Callback callback){

mLooper=looper;

mQueue = looper.mQueue;

mCallback= callback;

}

3 发送消息(Runnable实际上也是消息,这里不再分析),有好几种方式,最终都是sendMessageAtTime(Message,Long)

public boolean sendMessageAtTime(Message
msg, long uptimeMillis)

{

boolean sent = false ;

MessageQueue queue = mQueue;

if (queue != null) {

msg.target = this ;//该message对象的target成员变量设置为此handler实例

sent = queue.enqueueMessage(msg, uptimeMillis);//调用messageQueue来讲message加入队列

}

else {

RuntimeException e = new RuntimeException(

this + " sendMessageAtTime() called with no mQueue");

Log. w("Looper", e.getMessage(), e);

}

return sent;

}

2 handleMessage(Message)

根据取出来的message对象进行消息处理,一般override



典型用法:

1.子线程中:

class MyThread extends Thread{

public Handler mHanlder;

public void run(){

//将线程与Looper绑定。

//此方法必须在handler创建之前调用,handler否则实例化报错

Looper.prepare();

mHandler = new Handler(){

public void handleMessage(Message msg){

//处理消息

}



};



//不停的取出该线程中message队列中的消息。

Looper.loop();

}

}

2 主线程中直接实例化Handler,这里简要介绍一个为什么不需要Looper.prepare和Looper.loop.

是因为在程序启动已经做了这些工作,在ActivityThread类中main方法:

public static void main(String[] args) {

Looper.prepareMainLooper();
if (sMainThreadHandler == null) {

sMainThreadHandler = new Handler();

}

Looper.loop();


}



6.2 流程分析

下面以一个具体案例来分析怎么调用的:

比如说在子线程中发送一个消息给UI线程:

首先UI线程中定义了一个Handler:

private Handler handler = new Handler(){

public void handleMessage(Message msg){

if(msg.what=1){

tv.setText("数据下载完毕");

}

}

};



需要在子线程中执行耗时操作:

new Thread(){

public void run(){



try{

//模拟耗时操作

Thread.sleep(10*1000);

//发送消息,通知UI线程更新

handler.sendEmptyMessage(1);

}catch(Exception e){

}

}

}.start();

流程分析:

1该UI线程中的Looper.prepareMainLooper();和 Looper.loop(); 执行,这样就不需要在实例化handler时调用了prepare和looper。线程中已经有一个looper实例,且已经开始不停的从messageQueue中读取message。

2.子线程中给通过UI线程的handler变量调用handler.sendMessage系类方法,给Ui线程的消息队里发送一个消息。

3.Looper.loop方法调用messageQueue的next()方法取消息,取出来之后调用handler的dispatchMessage方法。

4.执行dispatchMessage方法,由于message中callback ==null,所以执行override的handleMessage方法,更新textview。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: