您的位置:首页 > 移动开发 > Android开发

Android---Handler消息处理机制

2016-04-13 13:52 387 查看
搞Android的人都知道,android是不允许你在子线程中更新UI操作的,这主要出于线程安全方面的考虑,通常的做法是在主线程中创建一个Handler对象,在子线程中创建一个Message对象,该Message对象中封装一些更新UI操作的数据,通过Handler的sendMessage方法发送出去,主线程利用Handler的handleMessage方法来对该Message进行相应的处理,但发现没有,子线程调用Handler的sendMessage发出Message之后,消息是怎么传递到主线程的handleMessage方法里面进而进行处理的呢,接下来我们从源码角度慢慢进行分析:

先来看看平常我们是怎么使用handler的:

实例1:

public class MainActivity extends Activity {

public Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler(){

@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
System.out.println("接收到空消息");
break;
default:
break;
}
}
};
new Thread(new Runnable() {

@Override
public void run() {
mHandler.sendEmptyMessage(1);
}
}).start();
}
}

解释:

可以看到,在子线程中我们使用的是主线程的Handler来进行sendMessage的,那么问题来了,子线程中可以存在自己的Handler么?并且用这个Handler来sendMessage?下面我们进行测试:

实例2:

public class MainActivity extends Activity {

public Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler(){

@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
System.out.println("接收到空消息");
break;
default:
break;
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
Handler handler = new Handler();
handler.sendEmptyMessage(1);
}
}).start();
}
}

解释:

这段程序我们调用的是自己的子线程自己的Handler,运行之后报了以下错误:

E/AndroidRuntime(963): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

意思就是说在没有调用Looper.prepare()之前是不允许创建Handler对象的,这一点我们可以从Handler的构造函数中查看原因:

以下是Handler的构造函数源码:

public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//从ThreadLocal获取到Looper对象,这个对象是由Looper.prepare()函数创建并且添加到ThreadLocal中的
mLooper = Looper.myLooper();
if (mLooper == null) {
<span style="color:#ff6666;">throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");</span>
}
//获得Looper对象中的MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

解释:

抛出异常原因在于mLooper为null,而mLooper是一个Looper对象,这个对象是通过Looper的static方法myLooper从ThreadLocal中获取的,而创建Looper对象是由Looper的static方法prepare()实现的,并且将其加入到了ThreadLocal中;

因此我们找到了抛出异常的原因,也就是Looper对象为null,正如异常中所提示的一样,需要调用Looper.prepare()来创建Looper对象;

我们来查看Looper的static方法prepare源码:

public static void prepare() {
prepare(true);//创建子线程的Looper对象的时候,此处始终为true,但是待会会发现主线程的Looper对象在创建的时候此参数值是false
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}

解释:

从源码中我们看到在创建Looper对象之前先查看ThreadLocal中是否已经存在一个Looper对象,一个线程只能创建一个Looper对象,如果多次创建Looper会抛异常,如果不存在的话,调用new Looper(true)创建当前线程的Looper对象,并且将其set到ThreadLocal中(在此多少可以发现ThreadLocal其实是一个Map型的数据结构实现的,其源码分析以后补上),来吧,该看看new Looper(true)到底做了写什么事的时候了,源码如下:

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建了一个MessageQueue消息队列
mRun = true;
mThread = Thread.currentThread();
}

解释:

很简单,就是创建了一个MessageQueue并且将mThread设置为当前线程;

好了,至此我们创建了Looper对象,那么我们把Looper.prepare()加入到实例2的Handler handler = new Handler( )之前,看看会有什么事发生吧:

实例3:

public class MainActivity extends Activity {

public Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler(){

@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
System.out.println("1:   "+Thread.currentThread());
break;
default:
break;
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println("2:  "+Thread.currentThread());
}
};
handler.sendEmptyMessage(0);
mHandler.sendEmptyMessage(0);
}
}).start();
}
}


解释:

输出结果:1: Thread[main,5,main]

发现在我们的输出信息中,并没有输出:子线程中handleMessage的信息

原因其实也很简单:你现在只是有Looper对象了,但是你并没有对Looper对象进行任何操作,就像你很有钱,但是你不花这些钱,那钱还有什么用呢?那该怎么用呢?要想找到这个问题的答案,我们需要分析主线程中是怎么创建Looper对象以及怎么使用这个Looper对象的呢?

我们知道应用程序是通过ActivityThread主线程来创建的,为什么这样子说呢?看看源码就知道啦:

public static void main(String[] args) {
SamplingProfilerIntegration.start();

// CloseGuard defaults to true and can be quite spammy.  We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);

Environment.initForCurrentUser();

// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());

Security.addProvider(new AndroidKeyStoreProvider());

Process.setArgV0("<pre-initialized>");

Looper.prepareMainLooper();//创建Looper对象

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

AsyncTask.init();

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

Looper.loop();//使用Looper对象中的MessageQueue来进行消息处理

throw new RuntimeException("Main thread loop unexpectedly exited");
}

解释:

因为这个类里面有我们苦苦想要寻找的main函数,可能你以前也会疑惑android程序到底是怎么启动的呢?现在明白了吧,入口函数main在这里呢,来看看里面我们可能熟悉的代码吧,Looper.prepareMainLooper()有点类似于我们之前见过的Looper.prepare()吧,显然他也是用来创建一个Looper对象并且放入ThreadLocal里面的,只不过他是在主线程中创建的:

public static void prepareMainLooper() {
<span style="color:#ff6666;">prepare(false);//这里的参数值是false,子线程创建Looper的prepare参数值是true</span>
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

解释:

在有了Looper对象之后,main方法中的Looper.loop()就是利用Looper对象中的MessageQueue来进行消息接收和处理的,源码如下:

public static void loop() {
final Looper me = myLooper();//获得Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
<span style="color:#ff6666;">final MessageQueue queue = me.mQueue;//获得Looper对象中的MessageQueue</span>

// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//采用死循环的方式从MessageQueue中取出消息
for (;;) {
<span style="color:#ff6666;"> Message msg = queue.next(); // might block</span>
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}

// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//调用dispatchMessage来进行消息的处理
msg.target.dispatchMessage(msg);

if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}

// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//消息处理结束回收消息
msg.recycle();
}
}

解释:

具体过程是:

(1)首先通过myLooper( )静态方法获取到Looper对象;

(2)通过获取到的Looper对象来获取到该对象中的MessageQueue消息处理队列;

(3)采用死循环的方式对消息队列中的每个消息调用此消息所在的handler(通过msg.target获取此handler)的dispatchMessage方法进行处理;

(4)消息处处理结束后调用recycle方法回收消息;

这里最重要的方法当然就是dispatchMessage消息处理和消息回收函数啦,接下来我们先分析一下消息是怎么传递到MessageQueue队列中的,随后再来分别来看看到底在dispatchMessage和recycle这两个函数中做了什么?

要想处理消息,首先你得有消息吧,就像你想花钱一样,首先你总得有钱吧,handler机制中,消息是从哪来的呢?

很显然从实例中我们可以看出,我们都是通过handler的sendEmptyMessage方法来发送消息的,在handler中存在很多发送消息的方法,但是归根结底他们最后都会调用sendMessageAtTime方法的(不管你是采用Runnable的post机制还是采用Message的send机制),源码如下:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
解释:

这个方法有两个参数,一个是想要传递给主线程的Message对象,uptimeMillis表示我们发送消息的时间,如果调用的不是sendMessageDelayed的话,uptimeMillis的值为0,在消息队列非空的前提下调用enqueueMessage将消息加入到队列中;

我们来看看enqueueMessage的源码:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
解释:

很简单,他就是直接调用了MessageQueue队列中的enqueueMessage入队方法,来看MessageQueue里面的enqueueMessage方法源码:

boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}

synchronized (this) {
if (mQuitting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}

msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue.  Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}

// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
解释:

这里我们只讨论核心代码,因为要插入Message对象到队列,所以我们必须找到队尾元素的位置,而这个位置在上面的源码是通过p的指针值是否为空来进行判断的,如果p本身为空的话,说明p已经到达了队尾,我们只需要将该Message对象插入到p之后即可啦,假如队列本身为空的话,那么p本身就是队尾,直接插入;假如队列本身不为空的话,需要遍历整个队列,找到队尾元素即可啦,然后插入,我们可以看到,MessageQueue的队列是由单链表来实现的;

好了,这下子Message对象加入到了MessageQueue中啦,随后就是我们该怎么处理的问题啦;

查看Handler.java源码中的dispatchMessage方法:

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}


解释:
在正式分析此方法之前先补充一点,Handler支持两种消息类型,Runnable和Message,因此发送消息提供了post(Runnable
r)和sendMessage(Message msg)两个方法,Message中的callback属性存储的就是Runnable对象;
具体分析:

(1)首先查看该Message的callback字段是否为null,即查看此消息是否存在Runnable属性值(该值是通过post方法将其加入到Message的callback中的),有的话则执行handleCallback方法,该方法会执行Runnable的run方法,源码如下:

private static void handleCallback(Message message) {
message.callback.run();
}
(2)如果Message的callback字段为null的话,则查看是否存在外部的Callback类型对象,这个值是在创建Handler的时候通过构造函数传递进来的,如果存在的话执行该Callback对象的handleMessage方法;

(3)如果既不存在Runnable对象,又不存在外部的Callback对象的话,则直接执行handler自身的handleMessage方法,这个方法需要我们在new Handler之后进行重写,因为Handler本身是没有实现这个方法的;

public void handleMessage(Message msg) {
}
好了,消息已经处理结束啦,接下来我们就该回收该消息啦,也就是Looper.loop( )方法所执行的最后一句代码,msg.recycle( ),再次回到了源码级别查看:

public void recycle() {
clearForRecycle();//将Message的各种标识位全部归位

synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;//将该消息返回给消息缓冲池
}
}
}
void clearForRecycle() {
flags = 0;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
when = 0;
target = null;
callback = null;
data = null;
}
解释:

释放消息前首先将消息里面的各个消息标识位归位,随后将该消息加入到消息缓冲池中,以备下次我们使用消息的时候可以直接调用Message.obtain( )方法来获取消息,这样子就不用new Message( )啦,从而减少了new对象的时空开销,这就是缓冲机制的好处,因为缓冲池里面的所有消息对象是可以重复使用的,只是在使用的时候进行必要标识位的设置即可,看看Message的obtain方法就一目了然啦:

public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
解释:

我们可以发现,只有当消息缓冲池为null的时候我们才会new Message出来,如果消息缓冲池不为空的话,直接获取缓冲池中的第一个,并且让缓冲池中的消息个数减少1即可;

至此,整个消息处理过程已经结束了;

那么我们应该知道怎么修改实例3让他能够输出:子线程中handleMessage的信息

实例4:

public class MainActivity extends Activity {

public Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new Handler(){

@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
System.out.println("1:   "+Thread.currentThread());
break;
default:
break;
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println("2:  "+Thread.currentThread());
}
};
handler.sendEmptyMessage(0);
mHandler.sendEmptyMessage(0);
Looper.loop();
}
}).start();
}
}
差别就是增加了Looper.loop( )这句代码而已;

输出结果:

2: Thread[Thread-120,5,main]

1: Thread[main,5,main]

到底,我们可以做做总结啦,看看Handler消息处理机制中到底用到些什么?

1. Looper

(1)创建消息循环

prepare( )用于创建Looper对象,并且保存到ThreadLocal中;

(2)获得消息循环对象

myLooper( ),采用ThreadLocal的get方式获取存储在ThreadLocal里面的消息循环对象;

(3)开始消息循环

具体过程:

首选获取MessageQueue里面的队头Message

接着调用该Message所在handler的dispatchMessage方法,最后在dispatchMessage里面调用handlerMessage方法

消息使用完毕之后将其加入到本地消息缓冲池中,以便下次使用,节省创建Message的开销

2. MessageQueue

每个Looper对象对应一个MessageQueue队列,他是消息的存储区,向Handler发送的消息最终都会存储到该队列中

(1)消息入队

消息入队采用的方法是enqueueMessage( ),首先会查看队列是否为空,如果为空,则直接入队即可,如果非空需要轮询链表,根据when从低到高的顺序插入链表的合适位置,这里的队列是由单链表实现的;

(2)轮询队列

next( )用于获取MessageQueue中的Message

3. Handler

(1)获取消息

以obtain打头的方法,这些方法实现的功能就是从本地消息缓冲池中获取消息,这样做的目的就只是为了提高时空效率,这些方法实际上还是调用的Message中的各种obtain方法

(2)发送消息

Handler支持两种消息类型,分别是Runnable和Message,他们发送消息的方法分别是以post打头的post(Runnable runnable)和以sendMessage(Message message),但是post方法中的Runnable对象在最后还是会被封装到Message中称为Message的属性callback的值,也就是说原则上还是自由Message这种方式的,在调用各种sendMessage的时候,都会最终执行sendMessageAtTime,而在这个方法里面就会调用MessageQueue的enqueueMessage来将Message加入到队列中啦;

(3)处理消息

Handler中处理消息的开始方法是dispatchMessage,在这个方法里面会调用Handler本身的handleMessage方法,而这个方法是需要我们在创建Handler的时候重写的

4. Message

(1)创建消息

创建消息有两种方式,我们可以通过new Message( )的方式创建一个新的Message,也可以通过Message.obtain从本地消息缓冲池中获取一个消息,后者在时空上效率更高;

(2)释放消息

消息使用完毕之后,调用recycle函数释放消息,将该消息加入到本地消息缓冲池中,以便下次使用;

以上就是Handler消息处理机制的主要内容了,我们也自己实现了一个子线程中的handler并且创建了子线程自己的MessageQueue,此外子线程能够通过loop方法处理传递给子线程的消息了,但是如果每次我们想让子线程实现这样的功能的话,我们都必须利用Looper.prepare( )和Looper.loop(
)方法将我们的子线程中的handler代码包裹起来,这样是不是太麻烦啦,这就导致了HandlerThread的出现,在下一篇,我们讲解下HandlerThread的源码分析
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: