Android消息机制
本质上是一个线程开启无限循环并持续监听其他线程给他发的消息,如果没有消息自身就堵塞(相当于wait)
looper是要被调用的,android-main方法的调用:https://www.jianshu.com/p/302fe75d6778(未看)
大致的流程
先来大致梳理下整个流程:
-
应用程序启动的时候,在主线程中会默认调用了 Looper.prepare()方法,初始化Looper对象并绑定到当前线程中,并在Looper内部维护一个MessageQueue
-
接着调用handler.sendMessage()发送消息,会通过MessageQueue.enqueueMessage()向MessageQueue中添加一条消息
-
主线程调用Looper.looper()开启循环,不断轮询消息队列,通过MessageQueue.next()取出消息
-
取出的message不为空则调用msg.target.dispatchMessage()传递分发消息,目标handler收到消息后会执行handler.handlerMessage()方法处理消息
Looper:
//构造函数:不允许别人创建 同时创建了一个MessageQueue对象
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
-
ActivityThread.main()方法中系统已经帮我们创建好Looper对象,而子线程的looper是我们自己创建的。
主线程中的Looper的prepare与子线程不一样:
从prepare()可以看出而调用prepare的线程如果是第二次构建looper对象会抛出异常
//Looper类中拥有一个ThreadLocal<Looper>池
//ThreadLocal中装着所有线程的Looper对象
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//Looper类中拥有主线程的looper对象!
private static Looper sMainLooper;
//所有子线程要构建looper之前需要调用
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));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. See also: {@link #prepare()}
*
* @deprecated The main looper for your application is created by the Android environment,
* so you should never need to call this function yourself.
*/
//主线程中不需要自己创建Looper
public static void main(String[] args) {
......
Looper.prepareMainLooper();//为主线程创建Looper,该方法内部又调用 Looper.prepare()
......
Looper.loop();//开启消息轮询
......
}
//子线程
public class LooperThread extends Thread {
Loop()函数:
LoopOnce函数:挑重点
Loop循环不会卡死的原因:
主线程的任何代码都是被looper从队列中取出来的执行的,也就是说主线程的是接受所有其他线程给他发送消息来执行动作的,生命周期的回调也是通过系统服务系统服务ActivityManagerService通过Binder发送IPC调用给APP进程,App进程接到到调用后,通过App进程的Binder线程给主线程的消息队列插入一条消息来实现的。而Binder线程是在主线程进入无限循环的时候创建的,而当调用messageQueue方法的next如果没有消息的时候,主线程会释放当前cpu资源并且进入相当于wait状态(之后版本实现方法不一样)
其中Looper与ThreadLocal息息相关:不妨先看一下ThreadLocal<T>
ThreadLocal: 线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。这里线程自己的本地存储区域存放是线程自己的Looper。
不同的线程执行相对应的函数的时候都会调用Looper类中的sThreadLoacl.get获得对应的线程实例
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
小结一下:
Looper中管理这一个ThreadLocal对象,用该对象来实现不同线程间的looper对象切换以及使用其他功能
Handler
作用:用于同一个进程间的线程通信,
handler为了防止内存泄漏会有检查
//可以随时获得主线程的handler
public static Handler getMain() {
if (MAIN_THREAD_HANDLER == null) {
MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
}
return MAIN_THREAD_HANDLER;
}
//Callback,这也表明了callback函数是运行在当前线程上的,不可以执行ui工作
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
boolean handleMessage(
构造方法:新建一个Handler会得到当前线程的looper对象以及里面的MessageQueue,并且获得一个传进来的callback对象
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
//不是static 发出可能内存泄露的警告!
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());
}
}
//获取当前线程的Looper,还记得前面讲过 Looper.myLooper()方法了吗?
//Looper.myLooper()内部实现可以先简单理解成:map.get(Thread.currentThread())
//获取当前线程的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
//当前线程不是Looper 线程,没有调用Looper.prepare()给线程创建Looper对象
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//让Handler 持有当前线程消息队列的引用
mQueue = mLooper.mQueue;
//这些callback先不管,主要用于handler的消息发送的回调,优先级是比handlerMessage高,但是不常用
mCallback = callback;
mAsynchronous = async;
}
Handler 在sendMessage的时候就通过mQueue引用往消息队列里插入新消息。Handler 的另外一个作用,就是能统一处理消息的回调。这样一个Handler发出消息又确保消息处理也是自己来做,这样的设计非常的赞。具体做法就是在队列里面的Message持有Handler的引用(哪个handler 把它放到队列里,message就持有了这个handler的引用),然后等到主线程轮询到这个message的时候,就来回调我们经常重写的Handler的handleMessage(Message msg)
方法。
在准备启动一个Activity的时候,系统服务进程下的ActivityManagerService
(简称AMS)线程会通过Binder发送IPC调用给APP进程,App进程接到到调用后,通过App进程下的Binder线程最终调用ActivityThread
类下面的scheduleLaunchActivity
方法来准备启动Activity,看下scheduleLaunchActivity方法:
注:Binder线程:具体是指ApplicationThread,在App进程中接受系统进程传递过来的信息的线程(在主线程进入死循环之前创建了这个线程)。
最后调用!!!
public void handleMessage(
MessageQueue
存在于Looper中,MessageQueue 存在的原因很简单,就是同一线程在同一时间只能处理一个消息,同一线程代码执行是不具有并发性,所以需要队列来保存消息和安排每个消息的处理顺序。多个其他线程往UI线程发送消息,UI线程必须把这些消息保持到一个列表(它同一时间不能处理那么多任务),然后挨个拿出来处理,这种设计很简单,我们平时写代码其实也经常这么做。每一个Looper线程都会维护这样一个队列,而且仅此一个,这个队列的消息只能由该线程处理
boolean enqueueMessage(Message msg, long when) {
// msg 必须有target也就是必须有handler
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
//插入消息队列的时候需要做同步,因为会有多个线程同时做往这个队列插入消息
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
//when 表示这个消息执行的时间,队列是按照消息执行时间排序的
//如果handler 调用的是postDelay 那么when=SystemClock.uptimeMillis()+delayMillis
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// p==null 表示当前消息队列没有消息
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;
//将消息放到队列的确切位置,队列是按照msg的when 排序的,链表操作自己看咯,从链表头开始放消息,根据时间塞进去
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;
}
// 如果需要唤醒Looper线程,这里调用native的方法实现epoll机制唤醒线程,我们就不在深入探讨了
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
//两个版本!!!
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//这句话很重要,让消息持有当前Handler的引用,在消息被Looper线程轮询到的时候
//回调handler的handleMessage方法
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用MessageQueue 的enqueueMessage 方法把消息放入队列
return