我们知道Java程序开始于一个Main函数,如果只是顺序执行有限任务很快这个Main函数所在的线程就结束了。如何来保持Main函数一直存活并不断的处理已知或未知的任务呢?
1 采用死循环。但是死循环的一次循环需要处理什么任务。如果任务暂时没有,也要程序保持活跃的等待状态怎么办?
(需要:处理外来任务,可阻塞)
2 如果有两个线程或者多个线程如何来协作以完成一个微型系统任务?
(相互之间有对方的任务通知“把柄”)
我们熟悉的Windows其实是消息驱动的。由消息来通知做什么任务就做,没有消息静止等待。还有一个强大的游戏引擎cocos2d也是MainLooper内等待消息驱动。
我们的Android同样也采用了这个消息驱动模式。
Android 通过Looper MessageQueue(对应的 Native Looper和MessageQueue)Handler和Message来实现。
一:
首先,线程在启动时要在ThreadLocal内的Map中保存一个Looper对象。构造一个Looper和Thread一对一的关系 。
对于UI主线程我们在初始化Looper时传入allowQuit为false。即不可以退出主线程。
UI主线程中这样调用的:
Looper.prepareMainLooper();
意味着:MessageQueue 在 Main thread not allowed to quit.
如果是在UI线程中或者已经初始化了Looper的线程我们直接用:
Handler handler = new Handler();
Handler重载了几种构造方法:
public Handler() {//直接在UI主线程、初始化了Looper的线程使用
this(null, false);
}
public Handler(Callback callback) {//传入消息回调处理类
this(callback, false);
}
public Handler(Looper looper) {//可以指定是哪个Looper的消息处理Handler
this(looper, null, false);
}
如果是在非UI线程并且没有Looper:
我们需要创建一个Looper,并且让其循环处理消息队列:
* Looper.prepare();
*
* mHandler = new Handler() {//创建发送和和处理Message的handler
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
当然 ,以上只是表明用法,handler 的初始化可以在本线程或者其他线程的某个地方。
UI主线程ActivityThread中Main方法是UI线程的入口:(主要做的是开启一个Looper)
public static void main(String[] args) {
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
疑问:我们知道Ui线程负责加载初始化和处理UI事件。那么如果Ui线程的main函数中就做启动了一个Looper并死循环一件事的话,UI绘画工作怎么来做呢?
这就要回到开始那句话,Android是消息驱动的。在UI进程中更是这样。在ActivityThread中我们创建一个Handler mH = new H()。用来处理AMS远程调用ApplicationThread中Activity的周期绘制函数所发出的Message。换句话说,整个Activity生命周期都是Message驱动的。
那么我们去创建Handler发送自己的消息给UI线程是为了做什么呢?
(注意,这里只配合UI线程来谈使用。消息驱动不仅仅为UI县城服务)
0 保证UI线程执行任务中尽量只做Ui相关操作
可以保证减少页面卡顿。
1 开启线程处理耗时操作
通常会因为这个原因而创建自己的线程并传入handler。
传入handler是为了 非线程处理UI事件
我觉得首先要理解ANR机制:
Activity 响应超时(InputEvents消息响应超时),五秒超时。
Broadcast onreciver。运行在主线程中的无状态类。前台10
Service 20
这三个组件都运行在主线程中,都要确保不要超出限制的组件处理事务的时间。如果时间太长比如 IO 文件、网络、复杂数据处理等。
疑问:非UI线程发送的消息和UI绘制消息无序的在MessageQueue中排队。那么UI绘制消息处理顺序中势必会掺杂着其他消息的处理,依然会违背上面0所保证的呀?
MessageQueue中为了保证绘制UI的message消息任务及时执行处理。加入了SyncBarrier概念——同步消息处理障碍。
如图所示,插入Barrier后,其后面的同步消息被忽视,直接去顺序执行异步消息。咱们默认创建的Message都是同步的。异步消息在View绘制或者变化时候由系统创建并插入Barrier。以保证UI绘制的及时处理。
二,创建消息发送和处理Handler。
Handler 中常用的函数:
Post系列(主要是给Message传入Runnable可执行体)
post(Runnable r)
postAtTime(Runnable r, long uptimeMillis)
postDelayed(Runnable r, long delayMillis)
postAtFrontOfQueue(Runnable r) //一般不建议用,容易打破消息队列执行顺序
Send系列(发送消息)
sendEmptyMessage(int what) //仅预示着某个节点到达的通知
sendEmptyMessageAtTime(int what, long uptimeMillis)
sendMessageDelayed(Message msg, long delayMillis) //可以做循环打点
sendMessageAtTime(Message msg, long uptimeMillis)
sendMessageAtFrontOfQueue(Message msg)
Remove系列 //移除未处理又不希望被处理的消息
obtain消息系列 // 调用Message.obtain()获得消息对象池的空闲消息。
uptimeMillis:指的是在这个时间点被执行 delayMillis:在当前uptimeMillis基础上的uptimeMillis+ delayMillis时间点被执行。
处理函数:在Looper循环中一直查询MessageQueue的next消息msg。如果不为空则调用:
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
这里的target就是发送这个Message的Handler。
public void dispatchMessage(Message msg) {
if (msg.callback != null) { //创建消息时候传入的处理Runnable
handleCallback(msg);
} else {
if (mCallback != null) { //一般是自己定义的处理接口
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //handler 自己的 要我们实现的处理接口
}
}
三:Looper 的loop()
在线程中启动了Looper.loop()之后。线程就进入了消息处理或者等待的模型中。
主要干了下面的事情(代码中Trace工作被去掉了):
在死循环中不停地去向MessageQueue去要下一个消息,并调用消息的Handler处理这个消息。在拿下一个消息的时候有可能被阻塞掉(在MessageQueue的next内循环)。
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
Message中做的事情是不停地去遍历Message队列。这里的Message队列是通过message中添加了Message类型的 next串联起来的message链。
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr; //这个是Native MessageQueue 的指针
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//通知 Native中的MessageQueue下次遍历时间
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) { //遇到一个同步消息障碍
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {//遍历获得一个异步消息 一般是UI绘制消息
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) { //1 找到异步消息 2 第一个出队的就是同步消息
if (now < msg.when) {//还没有到处理这个消息的时间点
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);//重置下次Poll的时间
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {//摘出来异步消息
prevMsg.next = msg.next;
} else { //直接出队同步消息
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {// UI线程中不允许推出 非UI线程中分为安全退出(处理完当前消息)和强制立马推出。
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;//looper loop中的阻塞在这里是循环内等待消息
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
画个简约图看一下Message MessageQueue Looper Handler的关系:
最后:
**1 合理利用消息驱动机制处理UI线程中费时操作
2 合理封装框架处理自己的Handler 和 自定义Message
3 合理在非UI线程中创建UI线程的Handler。
4 这个消息驱动机制 虽然我们接触时切入点在了UI线程上。但是不要一想到UI线程就想到这个机制就是为其服务的。抛开UI线程,这个机制依然自己玩的转。在系统的后台应用中大量使用。即利用了这个消息驱动的 多线程之间传递消息并相互协作的特性。*