Android开发 之 理解Handler、Looper、MessageQueue、Thread关系
本文转自博客:http://blog.csdn.net/he90227/article/details/43567073
一. 图解与概述
首先Android中 的每一个线程都会对应一个MessageQueue和Looper。见名知意,MessageQueue即线程用来维护线程产生的消息的消息队列,而这个 队列的调度则是由Looper来完成的。Looper负责将产生的消息放入队列,并及时的将合适的消息从队列中取出并交由合适的接受者处理。处理消息的便 是每个线程内部的Handler对象,特别是在UI线程中,由于Handler与UI处于同一个线程,所以我们就可以通过Handler处理接收到的消息 并及时更新UI中的组件。
上述描述即可以实现在其它线程中完成耗时操作并在UI线程中通过Handler更新组件以反映耗时操作的处理效果。
以下是它们之 间关系的图示:
理解概念:
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
简单关系:
Handler,Looper和MessageQueue就是简单的三角关系。
Looper和MessageQueue一一对应,线程和looper也是一一对应,创建一个Looper的
同时,会创建一个MessageQueue。
而Handler与它们的关系,只是简单的聚集关系,即Handler里会引用当前线程里的特定Looper
和MessageQueue。
这样说来,多个Handler都可以共享同一Looper和MessageQueue了。
当然,这些Handler也就运行在同一个线程里。
消息循环过程:
生成消息
1 Message message = handler.obtainMessage();
2 message.arg1 = id;
3 message.obj = drawable;
4 handler.sendMessage(message);
发送消息
1 /**
2 * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
3 * to this handler.
4 * @param uptimeMillis The absolute time at which the message should be
5 * {@link android.os.SystemClock#uptimeMillis} time-base.
6 * @return Returns true if the message was successfully placed in to the
7 * looper processing the message queue is exiting. Note that a
8 * the looper is quit before the delivery time of the message
9 */
public boolean sendMessageAtTime(Message msg, uptimeMillis)
10 {
11 sent = ;
12 MessageQueue queue = mQueue;
13 (queue != ) {
14 msg.target = ;
15 sent = queue.enqueueMessage(msg, uptimeMillis);
16 }
17 {
18 RuntimeException e = RuntimeException(
19 + , e.getMessage(), e);
20 }
21 sent;
22 }
在 Handler.java的sendMessageAtTime(Message msg, long uptimeMillis)方法中,我们看到,它找到它所引用的MessageQueue,然后将Message的target设定成自己(目的是为了在 处理消息环节,Message能找到正确的Handler),再将这个Message纳入到消息队列中。
Looper从消息队列中抽取消息
1 public static final void loop() {
2 Looper me = myLooper();
3 MessageQueue queue = me.mQueue;
4 () {
5 Message msg = queue.next();
6
7
8
9 (msg != ) {
10 (msg.target == ) {
11
12 ;
13 }
14 (me.mLogging!= ) me.mLogging.println(
15 );
16 msg.target.dispatchMessage(msg);
17 (me.mLogging!= ) me.mLogging.println(
18 + msg.callback);
19 msg.recycle();
20 }
21 }
22 }
在Looper.Java的loop()函数里,我们看到,这里有一个死循环,不断地从MessageQueue中获取下一个(next方法)Message,然后通过Message中携带的target信息,交由正确的Handler处理(dispatchMessage方法)。
处理
1 /**
2 * Handle system messages here.
3 */
4 public void dispatchMessage(Message msg) {
5 if (msg.callback != null) {
6 handleCallback(msg);
7 } else {
8 if (mCallback != null) {
9 if (mCallback.handleMessage(msg)) {
10 return;
11 }
12 }
13 handleMessage(msg);
14 }
15 }
在 Handler.java的dispatchMessage(Message msg)方法里,其中的一个分支就是调用handleMessage方法来处理这条Message,而这也正是我们在职责处描述使用Handler时需要 实现handleMessage(Message msg)的原因。
至于dispatchMessage方法中的另外一个分支,我将会在后面的内容中说明.
至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。
Handler所处的线程及更新UI的方式
在 主线程(UI线程)里,如果创建Handler时不传入Looper对象,那么将直接使用主线程(UI线程)的Looper对象(系统已经帮我们创建 了);在其它线程里,如果创建Handler时不传入Looper对象,那么,这个Handler将不能接收处理消息。在这种情况下,通用的作法是:
1 class LooperThread extends Thread {
2 public Handler mHandler;
3 public void run() {
4 Looper.prepare();
5 mHandler = new Handler() {
6 public void handleMessage(Message msg) {
7 // process incoming messages here
8 }
9 };
10 Looper.loop();
11 }
12 }
在创建Handler之前,为该线程准备好一个Looper(Looper.prepare),然后让这个Looper跑起来(Looper.loop),抽取Message,这样,Handler才能正常工作。
因此,Handler处理消息总是在创建Handler的线程里运行。而我们的消息处理中,不乏更新UI的操作,不正确的线程直接更新UI将引发异常。因此,需要时刻关心Handler在哪个线程里创建的。
1,MessageQueue与Message的关系
Message中文是消息,及线程处理的最小单元,里面带有处理的数据集,或还带有操作,及告诉目的地要做什么事情, 每个MessageQueue里包含有Message。每个Message不是直接插入到MessageQueue里的,而是通过MessageQueue.IdleHandler 与looper一起工作,把Message放到MessageQueue里,及addIdleHandler(MessageQueue.IdleHandler handler)
和removeIdleHandler(MessageQueue.IdleHandler handler)
方法,把MessageQueue.IdleHandler压入到MessageQueu里。
2,Thread和HandlerThread的关系
HandlerThread就是带有Looper循环的Thread.
3,Looper介绍
Looper即一个循环,必须绑定到一个固定的线程里,通过getThread()方法可以获得该looper绑定的Thread对象,通过
getMainLooper()获得主线程里的looper对象,myLooper()可以获得当前线程里的looper对象,通过myQueue()方
法可以获得当前线程里的messageQueue对象。
4,Thread、HandlerThread、和Looper的关系的关系
一般声明一个thread是没有带looper循环的,但是可以通过Looper.prepare()方法给一个线程加上
looper;Looper.loop()执行该looper绑定的线程里的messageQueue,直到该loopger结
束;Loop.quit()结束该循环,实例代码如下:
1 class LooperThread extends Thread {
2 public Handler mHandler;
3
4 public void run() {
5 Looper.prepare();
6
7 mHandler = new Handler() {
8 public void handleMessage(Message msg) {
9 // process incoming messages here
10 }
11 };
12
13 Looper.loop();
14 }
15 }
5,Handler和Thread HandlerThread Looper Message MessageQueue的关系
它把消息发送到该生成它的线程里的MessageQueue里,然后当Message出队列的时候,就执行该消息。
post(Runnable),postAtTime(Runnable, long)
,postDelayed(Runnable, long)
,是用来处理runnable对象的;
sendEmptyMessage(int),sendMessage(Message)
,sendMessageAtTime(Message, long)
, andsendMessageDelayed(Message, long)方法是
可以通过handleMessage (Message msg) 方法来接收消息。
实例化:Handler(Looper looper)
二. 多线程与异步
Main Thread & UI Thread
当程序启动的时候android会自动创建一个进程和一个线程,这个线程负责界面更新,收集系统事件和用户的操作事件等并分配给对应的组件,所以这个线程非常重要 被称为主线程,因为所的和UI有关的操作都是在这个线程当中进行的所以也被称作UI线程。所以说默认情况下主线程和UI线程指的是同一个线程。
Android的UI系统参考了很多SWT的设计模式。比如,UI线程机制,底层JNI实现 etc.
Android 的UI系统是非线程安全的,意思是说只有创建UI的线程(也就是主线程)才可以对UI进程操作。如果我们在其它线程中执行了一些操作,而这些操作的结果又需要通过UI展现给用户,必需把这更新UI的操作转移到到UI线程中执行。
很多Java起步的程序员对UI线程中的"消息循环"会感觉陌生。其实就是在线程内部有一个死循环一直监听系统事件(用户的操作等)并把任务分发给对应的 组件(有兴趣的可以看看NDK附带的native-activity的一个sample,在c中的实现方式就是一个while(1)的死循环)。主线程通 过Looper实现了消息的循环处理。
The ralationship between Handler,Message,MessageQueue and Looper?
如果一个线程里边有一个死循环,那么这个循环就会一直在死循环里边循环,并且这个线程不会过多的cpu资源,那么这个线程肯定的阻塞的。如果线程只是一直 循环没有什么意义,实际情况通常需要线程根据不同的条件运行不同的方法。我们通常的作法可能会加一个switch开关,根据条件的不同去调用不同的方法。
线程间通信(最常见的就是,worker线程需要更新UI),这个时候实际是包含两个内容:一,数据的传递;二,方法的传递; Android中的Message用于数据传递,而Handler就是方法传递(其实是方法的执行者,Handler会把这个方法放到Handler所在 的线程当中去执行);MessageQueue是Message的容器和Java中的Queue一样都是容器,只不过Message Queue是专门用于装载Message的容器。Looper则是一个线程中的死循环用于管理MessageQueue,它负责阻塞读取 MessageQueue中的信息(如果MessageQueue中没有信息会一直在那里),挨个读取并把这个Message分发给对应的Handler 去执行。
首先看一下mHandler.obtainMessage(SHOW_ALERT, Greetings).sendToTarget();会执行什么。
通过查看Handler的源码发现执行了下边的代码。
1 public final Message obtainMessage(int what, Object obj){
2 return Message.obtain(this, what, obj);
3 }
1 public static Message obtain(Handler h, int what, Object obj) {
2 Message m = obtain();
3 m.target = h;
4 m.what = what;
5 m.obj = obj;
6 return m;
7 }
8 /**
9 * Return a new Message instance from the global pool. Allows us to
10 * avoid allocating new objects in many cases.
11 */
12 public static Message obtain() {
13 synchronized (mPoolSync) {
14 if (mPool != null) {
15 Message m = mPool;
16 mPool = m.next;
17 m.next = null;
18 return m;
19 }
20 }
21 return new Message();
22 }
看到这里我们应该明白为什么Google建议我们使用obtainMessage而不是new一个Message.这也是符合了Android中的对象回收机制。
1 public void sendToTarget() {
2
3 target.sendMessage(this);//targe就是一个Handler
4
5 }
6 sendMessage()会调用下面的sendMessageAtTime()把Message加入MessagQueue;
7
8 public boolean sendMessageAtTime(Message msg, long uptimeMillis){
9 boolean sent = false;
10 MessageQueue queue = mQueue;
11 if (queue != null) {
12 msg.target = this;
13 sent = queue.enqueueMessage(msg, uptimeMillis);
14 }else {
15 RuntimeException e = new RuntimeException(
16 this + " sendMessageAtTime() called with no mQueue");
17 Log.w("Looper", e.getMessage(), e);
18 }
19 return sent;
20 }
到此我们的Message对象被加入到了Handler所在线程(也就是主线程)中的MessageQueue这个存储和管理Message的容器当中。下一步就该由Looper接手一个一个的取出Message对象处理了。Looper最主要的方法就是loop()方法
1 public static final void loop() {
2 Looper me = myLooper();
3 MessageQueue queue = me.mQueue;
4 while (true) {//死循环
5 Message msg = queue.next(); // might block
6 if (msg != null) {
7 if (msg.target == null) {//退出死循环的机制
8 // No target is a magic identifier for the quit message.
9 return;
10 }
11 if (me.mLogging!= null) me.mLogging.println(
12 ">>>>> Dispatching to " + msg.target + " "
13 + msg.callback + ": " + msg.what
14 );
15 msg.target.dispatchMessage(msg);//target of a msg is a Handler
16 if (me.mLogging!= null) me.mLogging.println(
17 " Finished to " + msg.target + " "
18 + msg.callback);//msg.callback is a Runnable
19 msg.recycle();
20 }
21 }
22 }
Looper会一直阻塞读取MessagQueue中的Message对象(Message msg = queue.next()),当读取到一个Message时就会调用msg.target.dispatchMessage(msg)把这个 Message分发给对应的Handler去处理,等Handler把任务处理完了再接着读取下一个Message对象并处理。
1 /**
2 * Handle system messages here.
3 */
4 public void dispatchMessage(Message msg) {
5 if (msg.callback != null) {
6 handleCallback(msg);// call the run method of the runnable object I guess
7 } else {
8 if (mCallback != null) {
9 if (mCallback.handleMessage(msg)) {
10 return;
11 }
12 }
13 handleMessage(msg);
14 }
15 }
最后回来了我们的handlerMessage(msg);这个方法中。如果是用的不是sendMessage是是Hadler.post(Runnable)会调用
1 private final void handleCallback(Message message) {
2 message.callback.run();
3 }
这就是为什么在Handler中post的Runnable不会开启一个新的线程的原因。经过上面的追踪我们应该能明白不是所有 的线程都可以有Handler去执行handlerMessage或者处理Runnalbe的。其必要条件就是这个线程要有一个一直循环的 Looper.Looper一直循环肯定要有一个方法可以退出循环。
当我们调用msg.sendToTarget()的时候,我们的msg对象应付被压入MessageQueue的尾部。Looper在 MessageQueue的另一端一个一个的读取信息并处理。根据msg的target属性把cpu分配给对应的Handler去执行任务,这时 Handler会根据msg中的属性调用msg.handleMsg()或者msg.callback.run();当一个msg中的消息处理完返回之后 Looper会把这个msg放入msgPool当中方便下次再重复利用。从而减少对象的创建。Looper再从MessageQueue中读取下一个信 息,如此反复。。
理解了这些其实就不难想象ANR是如何实现的了。当用户执行操作(比如点击了一个按钮)系统会生成一个Message对象,把用户操作的信息写入 Message对象,并把这个Message对象压入MessageQueue队列的尾部。系统过一段时间(一般是五秒)后会再来检查,刚刚放入的信息是 不是已经被处理了,如果信息还在队列中就表明。处理前面信息的过程当中发生的阻塞,用户的操作没有及时得到响应。系统弹出ANR对话框。
作个总结:
因为UI线程需要保持一直运行的状态,所以要有一个循环保持这个线程不会死掉,但这个线程又必需阻塞,以减少cpu的消耗。android中的这个循 环就是通过Looper实现的。有了这个 Looper,Looper就占据了整个线程,导致所有的方法想在些线程中运行就必需通过这个Looper,所以要有个方法可以进入这个Looper的内 部。MessageQueue就担当了这个通道 的角色。Message担当了集合的角色。所有在UI线程中运行的方法都必需通过MessageQueue进入Looper内部,不管 是用户定义的方法还是系统事件包括onCreate(),onStop(),用户点击事件etc..
三.Android中消息系统模型和Handler Looper
消息系统的基本原理和构成
从一般的消息系统模型的建立大致构成以下几个部分:
l 消息原型
l 消息队列
l 发送消息
l 消息循环
l 消息获取
l 消息派发
l 消息处理
大致模型图如下:
消息系统模型一般会包括以上七个部分(消息原型,消息队列,消息发送,消息循环,消息获取,消息派发,消息处理)。实际上的核心是消息队列和消息循环,其余部分都是围绕这两部分进行的。
从前面文档的分析中我们知道Handler就是用来建立消息处理的系统模型,那么和这里基本消息系统模型相比,那么Handler又是如何囊括这七个部分的呢?
在Android中对这六个部分进行了抽象成四个独立的部分:
Handler,Message,MessageQueue,Looper;
- Message就是消息原型,包含消息描述和数据,
- MessageQueue就是消息队列,
- Looper完成消息循环
- Handler就是驾驭整个消息系统模型,统领Message,MessgeQueue和Looper;
Handler能够实现消息系统模型,那么具体是如何进行工作的呢,下面探究一下这其中工作的方法和原理。
四. Handler工作原理分析
要了解Handler工作原理,先看一下这个系统模型具体组成的层次结构框架是个什么样的。
实现Thread的消息循环和消息派发,缺省情况下Thread是没有这个消息循环的既没有Looper;需要主动去创建,然后启动Looper的消息循环loop;与外部的交互通过Handler进行;消息队列,由Looper所持有,但是消息的添加是通过Handler进行;
消息循环和消息队列都是属于Thread,而Handler本身并不具有Looper和MessageQueue;但是消息系统的建立和交互,是Thread将Looper和MessageQueue交给某个Handler维护建立消息系统模型。所以消息系统模型的核心就是Looper。消息循环和消息队列都是由Looper建立的,
而建立Handler的关键就是这个Looper。
一个Thread同时可以对应多个Handler,一个Handler同时只能属于一个Thread。Handler属于哪个Thread取决于Handler在那个Thread中建立。
在一个Thread中Looper也是唯一的,一个Thread对应一个Looper,建立Handler的Looper来自哪个Thread,Handler属于哪个Thread。
故建立Thread消息系统,就是将Thread的Looper交给Handler去打理,实现消息系统模型,完成消息的异步处理。
Handler与Thread及Looper的关系可以用下面图来表示:
Handler并不等于Thread,必须通过Thread的Looper及其MessageQueue,用来实现Thread消息系统模型,依附于Thread上。
在线程建立Handler时:
使Handler满足消息系统需要的条件,将Thread中的Looper和MessageQueue交给Handler来负责维护。
在线程中建立Handler,需要做以下工作:
l 获取Thread中的Looper交给Handler的成员变量引用维护;
l 通过Looper获取MessageQueue交给Handler的成员变量引用维护。
那么消息系统模型建立完成之后,按照消息系统运行,从Handler来看就是发送消息派发消息,与此线程消息系统的交互都由Handler完成。
消息发送和派发接口:
l post(runnable)消息,Runnable是消息回调,经过消息循环引发消息回调函数执行;
l sendMessage(Message)消息,经过消息循环派发消息处理函数中处理消息;
l dispatchMessage 派发消息,若是post或带有回调函数则执行回调函数,否则执行
消息处理函数Handler的handleMessage(通常派生类重写)。
以上就是Handler如何实现Thread消息系统模型的大致介绍, 下面将具体分析是如何实现消息系统模型运行的。
五. Handler实现流程分析
我们知道Handler就是一个消息系统的外壳,属于某个Thread并包装了Thread的Looper及其MessageQueue;与外部进行交互(同一个线程内或者线程之间),接收派发和处理消息,消息系统模型的核心是Looper。下面看看Handler是如何建立跑起来的,以msg消息为例,runnable实质是一样。
1 Handler的建立
Handler唯一属于某个Thread,在某个Thread中建立Handler时,需要获取Thread的Looper及其MessageQueue,建立Handler关键是Looper的来源。
Handler提供了好几个构造函数但其本质一致:
由外部传入Looper:当前线程或其他线程
public Handler(Looper looper) {
//初始化构建消息系统参数
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
从当前线程获取:由创建Handler的Thread决定
public Handler() {
//初始化构建消息系统参数
mLooper = Looper.myLooper();
mQueue = mLooper.mQueue;
mCallback = null;
} public static Looper myLooper() {
return sThreadLocal.get();
}
不管哪种方式,我们知道Thread在默认情况下是没有建立消息循环Looper实例的。
要实现消息循环必须确保Thread的Looper建立。如何确保呢?
Looper提供了静态函数:
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
} sThreadLocal.set(new Looper());
} //存储线程的局部变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
每一个线程调用Looper.prepare时,都会创建为其唯一的Looper。
要建立Handler,需要先创建线程的Looper,才能建立消息系统模型。通过Looper我们建立了
Thread上的消息系统模型Handler,可以来进行消息系统的一系列流程了。
2 消息发送
消息发送两种方式:post和sendMessage;
post:针对runnable对象;Runnable是一个接口,就是一个回调函数(提供了run方法)
sendMessage:针对Message对象;
下面通过代码具体看一下这个过程:
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
} public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
看到post和sendMessage发送消息时,仅仅是对象不同而已,Runnable和Message;
但实际上都是Message的形式来描述。
这跟我通常理解的消息机制不同:
通常post消息是将消息加入到消息队列中并不立即执行就返回,send消息是立即执行等待消息执行完才返回。
而这里post或者send都是将消息放入到消息队列中,然后立即返回,等待消息循环时获取消息被执行。
这里提供了众多的消息发送方法来指定消息的执行时间和顺序,具体可以查看源代码。
消息执行顺序是根据消息队列中消息的排列顺序而定。
下面看一下发送消息后将消息加入到消息队列中的代码:
由Handler调用MessageQueue的enqueueMessage方法:
1 final boolean enqueueMessage(Message msg, long when) {
2
3 Message p = mMessages;
4
5 if (p == null || when == 0 || when < p.when) {
6 msg.next = p;
7 mMessages = msg;
8 }
9 else {
10
11 Message prev = null;
12 while (p != null && p.when <= when) {
13 prev = p;
14 p = p.next;
15 }
16
17 msg.next = prev.next;
18 prev.next = msg;
19 }
20 ……
21 }
可以看到是按照时间顺序将消息加入到MessageQueue中;
现在将消息加入到消息队列中存储起来,消息并未得到处理,下一步必然是如何派发消息和处理消息。
3 消息派发
建立Thread消息循环由Looper完成,存在一个消息调度死循环:
public static void loop() {
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
} //派发消息 到target(Handler)
msg.target.dispatchMessage(msg); //回收Msg到msgPool
msg.recycle();
}
}
}
这里看到消息派发是由Message的target完成,这个target是什么呢?是一个Handler。
消息系统是通过Handler用来与外部交互,把消息派发出去。可以看到没有这个Handler,消息循环将结束。
消息派发由Looper通过Handler完成:
public void dispatchMessage(Message msg) { //首先判断runnable对象
if (msg.callback != null) {
handleCallback(msg);
}
else {
//整个消息系统的回调函数 可以不用实现自己Handler
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
} //消息处理 通常交给Handler派生类
handleMessage(msg);
}
}
通过消息派发,这样就实现消息的异步处理。
4 消息原型
前面看到消息发送有两种方式:
post(Runnable对象),sendMessage(Message对象),而中间都是通过Message对象保存在MessageQueue中。然后消息派发时处理方式不同。如果在sendMessage时将将消息对象附上Runnable对象,则post和sendMessage没有区别了。所以这两种方式很好理解基本一致,处理的方式不同罢了。
消息系统模型中,我们的真正的消息原型是什么,都具有那些功能,下面看一下Message中到底包含了那些东西,能有效帮助我们合理的运用消息系统来完成一些任务和处理。
Message消息原型:
public final class Message implements Parcelable {
//标识消息
public int what;
int flags;
long when; //传递简单数据
public int arg1;
public int arg2; //传递较复杂数据 对象
public Object obj;
Bundle data; //处理消息的目标Handler
Handler target; //消息派发时 执行的Runnable对象
Runnable callback; //使消息形成链表
Message next; //建立一个消息pool,回收msg,以避免重复创建节约开销
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 10;
}
看到提供了很丰富的属性来描述消息,针对具体问题选择使用那些属性去怎么样描述消息了。
获取新的Message对象时,Message提供了obtain方法:避免我们自己去分配Message新的对象,通过obtain获取,可能从MessagePool中获取,节约开销。
下面看一下这个MessagePool是如何建立的:
通常消息处理完毕的时候,消息也基本上处于无用状态可以释放回收了。对于需要频繁的创建释放的对象来说,创建和释放类实例都是要开销的,太频繁的使开销增大不好,像Message这种很有可能会频繁的创建。
于是我们可以将创建的对象用完之后保存在一个Pool里面,以便再重复利用节约频繁创建释放开销。是如何建立的呢?必然是在消息处理完毕之后才能进行。
MessagePool建立:
public static void loop() {
while (true) {
//派发消息
msg.target.dispatchMessage(msg); //消息处理完毕 回收
msg.recycle();
}
} public void recycle() {
//回收Message 建立全局的MessagePool
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
六. Handler的应用
以上这就是整个Handler作用及消息系统模型的建立。
使用也非常简单,虽然有很多方式,但只要理解Handler是建立在Looper上,实现Thread的
消息系统处理模型,实现消息异步处理,我想对与Handler基本应用上没有什么不能理解的了。
其他方面可以去看源码了。
Handler使用起来是非常简单的,关键就是如何利用消息的异步处理,来合理的完成我们
需要功能和任务。对于一个Thread,我们使用好几个Handler来进行异步处理,也可以创建新的Thread,
通过Handler来实现消息异步处理等等,应用场景很多如何用的好用的合理,这就没什么经验了。
至于如何使用,源码中很多例子可以看一下AsyncQueryHandler这个类,其中两个线程,
完成查询工作,通过Handler进行线程之间有消息传递。感觉这个利用的很好很巧妙。
Android开发 之 理解Handler、Looper、MessageQueue、Thread关系的更多相关文章
- Android 开发 深入理解Handler、Looper、Messagequeue 转载
转载请注明出处:http://blog.csdn.net/vnanyesheshou/article/details/73484527 本文已授权微信公众号 fanfan程序媛 独家发布 扫一扫文章底 ...
- Android开发笔记之:Handler Runnable与Thread的区别详解
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一 个类只要继承了Thread类同时覆写了本类中的run( ...
- 讲讲Handler+Looper+MessageQueue 关系
Handler+Looper+MessageQueue这三者的关系其实就是Android的消息机制.这块内容相比开发人员都不陌生,在面试中,或者日常开发中都会碰到,今天就来讲这三者的关系. 概述: H ...
- Handler+Looper+MessageQueue深入详解
概述:Android中的异步处理机制由四部分组成:Handler+Looper+MessageQueue+message,用于实现线程间的通信. 用到的概念: Handler: 主要作用是发送消息和处 ...
- Handler Looper MessageQueue 之间的关系
Handler Looper MessageQueue 之间的关系 handler在安卓开发中常用于更新界面ui,以及其他在主线程中的操作.内部结构大概图为: 1.handler持有一个Looper对 ...
- android开发 系统时间与定时器之间有关系嘛?
如题: android开发 系统时间与定时器之间有关系嘛? 答案:有. 看定时器源码: /* * Schedule a task. */ private void scheduleImpl(Timer ...
- Handler,Looper,MessageQueue流程梳理
目的:handle的出现主要是为了解决线程间通讯. 举个例子,android是不允许在主线程中访问网络,因为这样会阻塞主线程,影响性能,所以访问网络都是放在子线程中执行,对于网络返回的结果则需要显示在 ...
- Android之消息机制Handler,Looper,Message解析
PS:由于感冒原因,本篇写的有点没有主干,大家凑合看吧.. 学习内容: 1.MessageQueue,Looper,MessageQueue的作用. 2.子线程向主线程中发送消息 3.主线程向子线程中 ...
- android开发架构理解
1. android 开发和普通的PC程序开发的,我觉得还是不要过度设计,因为手机开发,项目相对传统软件开发就小很多,而且手机的性能有限,过度设计代码mapping需要消耗的能相对就高,而且手机开发的 ...
随机推荐
- LeetCode-107-二叉树的层序遍历 II
二叉树的层序遍历 II 题目描述:给定一个二叉树,返回其节点值自底向上的层序遍历. (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) 示例说明请见LeetCode官网. 来源:力扣(Leet ...
- SQL Server的Linked Servers(链接)
我们在使用SQL Server时,有时会有这种需求,需要从一个SQL Server服务器A中,查询另一个SQL Server服务器B中的表,然后将SQL Server服务器A中的表和SQL Serve ...
- Ansible 使用配置
1.配置 /etc/ansible/hosts 文件,添加被管控主机ip #vim /etc/ansible/hosts 文件末尾添加组[group1]和被管控主机的IP [group1] 192 ...
- 如何防止 IP 被盗用 保护网络正常运行
IP被盗用是指盗用者使用未经授权的IP来配置网上的计算机.目前IP盗用行为非常常见,许多"不法之徒"用盗用地址的行为来逃避追踪.隐藏自己的身份.IP的盗用行为侵害了网络正常用户的权 ...
- Dapr 官方文档中文翻译 v1.5 版本正式发布
作者:敖小剑 - Dapr Approver 经过 Dapr 中国社区十余位贡献者一个多月的努力,Dapr 官方文档中文翻译 v1.5 版本完成翻译和审校,正式发布并上线 Dapr 官网. 访问方式 ...
- .net Core 调用EF Core 爬坑
.net Core 中调用 EF Core项目 首先得建一个类库供.net Core 的主项目引用:其次,在.net Core 中做注入:最后,在页面中调用 1.类库项目中的操作 1.新建类库项目 2 ...
- zookeeper可视化WEB工具(zkui)搭建与配置
前提:zookeeper 可视化WEB工具zkui依赖java环境,因此需要安装jdk,同时zkui源码要Maven编译,需要安装apache-maven. JDK下载地址:https://www.o ...
- wordpress在线检测主题和插件
http://wpthemedetector.coderschool.cn/ http://www.wpthemedetector.com/ http://whatwpthemeisthat.com ...
- 『现学现忘』Docker基础 — 35、实战:自定义CentOS镜像
目录 1.前提说明 2.编写Dockerfile文件 3.构建镜像 4.运行镜像 5.列出镜像的变更历史 1)目标:自定义镜像wokong_centos. 2)所用到的保留字指令: FROM:基础镜像 ...
- 使用redis作为django缓存数据库
1.Redis的Windows版本.打开https://github.com/MSOpenTech/redis/releases下载msi安装包.该版本是64位.安装msi过程中,有个选项是否加入系统 ...