首先来一个整体的印象:
我们将这四个对象看做一条流水线的工作流程,Handler(工人),MessageQueue(传送带),Message(待处理的货物),Looper(传送带的发动机)
工作流程: 工人(Handler) 将 货物(Message) 放到 传送带(MessageQueue)上,发动机(Looper) 带动整个传送带往前走,工人将传送带上的货物取下来打包处理。
流水线要能正常的工作,工人(Handler)首先要有一条传送带,传送带要有发动机,要传送货物当然还得有货物(Message),当然发动机还得启动(Looper.loop())。
这个对应了Handler中的几个属性,来看看Handler的属性组成:
final Looper mLooper;//发动机
final MessageQueue mQueue;//传送带
final Callback mCallback;//
final boolean mAsynchronous;
所以一个正常的Handler要正常工作,所必需的的三大要素:
1.构造一个MessageQueue,这个消息队列用来存储handler的sendMessage(Message msg)传过来的Message。
2.构造发动机唯一的Looper对象(每一条流水线只能有一台发动机),这个looper是整个消息传递机制动力的来源
3.所有工作完成之后,要启动发动机,即Looper要启动,注意looper一经启动就意味着所有的准备工作已经完成,也就是说所有的准备工作必须在looper启动之前,因为looper是个死循环,在这个循环启动之后的代码如果执行了,说明整个系统已经出错了或者退出了。这也就是一般Looper.looper()是写在方法最后一句的原因。
那么问题来了:
1.有人会问,既然looper是个死循环,它不会一直运行导致占用太多的系统资源导致系统内存溢出崩溃吗?
要回答这个问题首先要知道,线程中默认是没有Looer这个东西的,但是如果你需要使用 Handler,那么这个东西还会被创建,那么我们先来看看Looper是在哪里被创建的,又是如何与线程产生联系的。那么首先来看Handler的创建,不多说上源码:
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());
}
}
mLooper = Looper.myLooper();//这里是构造我们的发动机
if (mLooper == null) {
throw new RuntimeException(//这里给出了提示,如果没有调用Looper.prepare()方法就运行Handler的话会报错
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//构造发动机的同时也构造了传送带
mCallback = callback;
mAsynchronous = async;
}
Handler有三个构造函数,但最后会调用上面的,代码很简单,就是简单的构造所需要的组件,下面来看 Looper.myLooper()方法:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
..............................
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
是不是觉得有点熟悉了,对,ThreadLocal!前面讲了ThreadLocal的set与get方法可以设置变量为线程的私有变量。那么我们去看看sThreadLocal 的set方法在哪调用:
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));//这里简单的new了一个looper对象
}
..........................
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建了messageQueue对象
mThread = Thread.currentThread();
}
这也就是我们前面提到的在创建Handler之前不调用Looper.prepare()方法会报错的原因。因为sThreadLocal.get()拿到的是空值!这样looper与当前线程就形成了逻辑上的一对一的关系
了解了looper的创建我们来看看Android的主线程,Android应用的入口在ActivityThread类,我们来看看它的main函数:
public static void main(String[] args) {
..................................
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();//准备主循环
ActivityThread thread = new ActivityThread();//创建ActivityThread对象,其中会创建一个H 对象,其实也就是我们说的handler对象
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...........................
Looper.loop();//启动主循环
throw new RuntimeException("Main thread loop unexpectedly exited");
}
final H mH = new H();//这是activityThread中创建的H对象
从上面我们可以知道,looper的创建是在 Handler 创建之前的,当一切准备就绪之后,调用Looper.loop()应用程序就进入了死循环。
回到上面的问题,既然主线程进入了死循环,那么这个死循环会不会导致应用卡死,即使不会卡死会不会过度的耗费系统资源(CPU)?
答案大家都知道 是肯定不会,但是是为什么呢?
就我个人的理解哈,设计成死循环的目的,是让主线程能一直运行下去(除非用户自己退出),我们也不想我们的应用程序运行一段时间就自动退出吧?出于这个考虑,死循环是最好的选择。其实binder内部也是采用的死循环。再回过头来看Looper.looper方法:
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.");
}
final MessageQueue queue = me.mQueue;//获取当前的消息队列
// 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();
for (;;) {//死循环 一个个去取出消息
Message msg = queue.next(); // might block不断去取messageQueue中的消息,这个地方有可能阻塞
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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.recycleUnchecked();
}
}
上面就是整个looper的工作过程,首先拿到当前线程的looper,然后通过这个looper拿到当前的messageQueue,然后进入死循环,不断的去取messageQueue中的消息,即调用messageQueue中的next方法,这个方法有可能会阻塞,也就是主线程休眠。如果next方法取到的值为 null ,那么表示消息队列在退出了,也就是整个线程在退出了
这里的next方法的结果只会有三种状态
1.返回正常值
2.阻塞,表示队列中没有消息,主线程进入休眠
3.返回null,表示线程在退出
上面说了,在队列中没有消息时(实际判断没这么简单),主线程会进入休眠,因此也不至于过度的耗费系统资源。
简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
2.没看见哪里有相关代码为这个死循环准备了一个新线程去运转?
这个我们其实在最初接触android的时候就知道,当一个安卓应用启动的时候,,每个App进程中至少会有两个binder线程 ApplicationThread(简称AT)和ActivityManagerProxy(简称AMP),除了图中画的线程,其中还有很多线程,比如signal catcher线程等。
事实上,会在进入死循环之前便创建了新binder线程,在代码ActivityThread.main()中:
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);//便会创建一个Binder线程
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程
3. Activity的生命周期是怎么实现在死循环体外能够执行起来的?
前面说到了ActivityThread中H对象,ActivityThread的内部类H继承于Handler,通过handler消息机制,简单说Handler机制用于同一个进程的线程间通信,而Binder是用于进程中通信!
Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施:在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期。 比如收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会通过反射机制,创建Activity实例,然后再执行Activity.onCreate()等方法; 再比如收到msg=H.PAUSE_ACTIVITY,则调用ActivityThread.handlePauseActivity()方法,最终会执行Activity.onPause()等方法。 上述过程,我只挑核心逻辑讲,真正该过程远比这复杂。
主线程的消息又是哪来的呢?当然是App进程中的其他线程通过Handler发送给主线程,请看接下来的内容:
![](https://img.haomeiwen.com/i11299729/37ddeee19bd11599.jpg)
结合图说说Activity生命周期,比如暂停Activity,流程如下:
1.线程1的AMS中调用线程2的ATP;(由于同一个进程的线程间资源共享,可以相互直接调用,但需要注意多线程并发问题)
2.线程2通过binder传输到App进程的线程4;
3.线程4通过handler消息机制,将暂停Activity的消息发送给主线程;
4.主线程在looper.loop()中循环遍历消息,当收到暂停Activity的消息时,便将消息分发给ActivityThread.H.handleMessage()方法,再经过方法的调用,最后便会调用到Activity.onPause(),当onPause()处理完后,继续循环loop下去。
总结来说就是:
网友评论