概述
之所以说通信利器,是因为在整个android源码里面进程和线程间的通信主要是通过binder和handler来完成的,android由大量的消息驱动的方式来进行交互,比如上一节讲到的activty的启动流程,所以从某种意义上来说android是一个以消息驱动的系统,handler的消息机制设计 Handler\MessageQueue\Looper\Message.
- Message :消息分为硬件消息(比如触摸和滑动等)和软件生成的消息(我们主动new Message 发送出的)。
- MessageQueue: 消息队列,主要的功能是向消息池投递消息和取走消息池里面的消息。
- Handler: 消息辅助类,主要功能向消息池发送各种消息(Handler.sendMessage)和处理相应各种消息(Handler.handleMessage);
- Looper :不断循环,按照分发机制,将消息分给目标处理者。
下面我们写一个自定义消息handler消息的线程,我们Activty就有一个这样的loop线程用来handle消息
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
//TODO 定义消息处理逻辑.
}
};
Looper.loop();
}
}
Looper
-
prepare()
对于无参的情况下表示,默认调用prepare(true)
,表示的是这个looper运行完退出,而对于false表示当前looper不运行退出,并且每个线程只被允许执行一次** prepare()**方法.
private static void prepare(boolean quitAllowed) {
//每个线程只允许执行一次该方法,第二次执行时线程的TLS已有数据,则会抛出异常。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建Looper对象,并保存到当前线程的TLS区域
sThreadLocal.set(new Looper(quitAllowed));
}
上面的ThreadLocal 是线程本地存储区(Thread Local Storage),每个线程都有自己的私有本地线程存储区,这个存储区(TLS)不允许被其他的线程访问,TLS的常见操作通过set/get 转的参数为泛型,
public void set(T value) {
Thread currentThread = Thread.currentThread(); //获取当前线程
Values values = values(currentThread); //查找当前线程的本地储存区
if (values == null) {
//当线程本地存储区,尚未存储该线程相关信息时,则创建Values对象
values = initializeValues(currentThread);
}
//保存数据value到当前线程this
values.put(this, value);
}
public T get() {
Thread currentThread = Thread.currentThread(); //获取当前线程
Values values = values(currentThread); //查找当前线程的本地储存区
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1]; //返回当前线程储存区中的数据
}
} else {
//创建Values对象
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this); //从目标线程存储区没有查询是则返回null
}
我们看到 sThreadLocal.set(new Looper(quitAllowed)); 是传了 Looper类型参数,而且我们看到了 Looper.prepare(),会创建一个looper对象.
而在Looper类的构造方法中会是实例化一个MessageQueue对象。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); /
mThread = Thread.currentThread(); //记录当前线程.
}
另外与prepare() 相似的方法,prepareMainLooper()方法,该方法主要在ActivityThread类中使用。
- loop() Looper 在prepare 之后 就需要调用 loop方法,它里面实现是
public static void loop() {
final Looper me = myLooper(); //获取TLS存储的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //获取Looper对象中的消息队列
Binder.clearCallingIdentity();
//确保在权限检查时基于本地进程,而不是基于最初调用进程。
final long ident = Binder.clearCallingIdentity();
for (;;) { //进入loop的主循环方法
Message msg = queue.next(); //可能会阻塞
if (msg == null) { //没有消息,则退出循环
return;
}
Printer logging = me.mLogging; //默认为null,可通过setMessageLogging()方法来指定输出,
用于debug功能,这个是调试代码不用管
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg); //用于分发Message
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity(); //确保分发过程中identity不会损坏
if (ident != newIdent) {
//打印identity改变的log,在分发消息过程中是不希望身份被改变的。
}
msg.recycleUnchecked(); //将Message放入消息池
}
}
loop()进入循环模式,不断重复下面的操作,直到没有消息时退出循环,可以看到上面有i 个for循环,它做的事情是
- 读取MessageQueue的下一条Message;
- 把Message分发给相应的target;
- 再把分发后的Message回收到消息池,以便重复利用。
上面的myLooper()方法,是用于获取TLS存储的Looper对象,
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
如何退出? quit ()方法
public void quit() {
mQueue.quit(false); //消息移除
}
public void quitSafely() {
mQueue.quit(true); //安全地消息移除
}
MessageQueue.quit()
void quit(boolean safe) {
// 当mQuitAllowed为false,表示不运行退出,强行调用quit()会抛出异常
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) { //防止多次执行退出操作
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked(); //移除尚未触发的所有消息
} else {
removeAllMessagesLocked(); //移除所有的消息
}
//mQuitting=false,那么认定为 mPtr != 0
nativeWake(mPtr);
}
}
- 当safe =true时,只移除尚未触发的所有消息,对于正在触发的消息并不移除;
- 当safe =flase时,移除所有的消息
Handler
构造方法
- 无参 方式
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
//匿名类、内部类或本地类都必须申明为static,
否则会警告可能出现内存泄露,关于这个问题可以google一下网上有更加详细的解释
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.prepare(),才能获取Looper对象,否则为null.
mLooper = Looper.myLooper(); //从当前线程的TLS中获取Looper对象
if (mLooper == null) {
throw new RuntimeException("");
}
mQueue = mLooper.mQueue; //消息队列,来自Looper对象
mCallback = callback; //回调方法
mAsynchronous = async; //设置消息是否为异步处理方式
}
对于Handler的无参构造方法,默认采用当前线程TLS中的Looper对象,并且callback回调方法为null,且消息为同步处理方式。只要执行的Looper.prepare()方法,那么便可以获取有效的Looper对象。
- 带参数的构造方法
/**
* Use the provided {@link Looper} instead of the default one.
*
* @param looper The looper, must not be null.
*/
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看出通过带參的方式,可以指定looper ,也可以设置是同步还是异步。
当然还有,(可以看源码中,提供了其他几种构造方法)
/**
* Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Callback callback) {
this(callback, false);
}
### dispatchMessage
你的handleMessage是如何收到消息的?上面我们提到了 在Looper.loop(),中当发现有消息时,调用消息的目标handler,执行dispatchMessage()方法来分发消息。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//当Message存在回调方法,回调msg.callback.run()方法;
handleCallback(msg);
} else {
if (mCallback != null) {
//当Handler存在Callback成员变量时,回调方法handleMessage();
if (mCallback.handleMessage(msg)) {
return;
}
}
//Handler自身的回调方法handleMessage()
handleMessage(msg);
}
}
对于很多情况下,消息分发后的处理方法是最后面那种情况,即Handler.handleMessage(),一般地往往通过覆写该方法从而实现自己的业务逻辑。
### sendMessage
消息发送的调用链关系图如下,最终都是调用MessageQueue.enqueueMessage();
![消息调用链](https://img.haomeiwen.com/i2043394/1fc95744fc8532f0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- sendEmptyMessage
public final boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what, 0);
}
- sendEmptyMessageDelayed
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
- sendMessageDelayed
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
- sendMessageAtTime
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
- sendMessageAtFrontOfQueue
public final boolean sendMessageAtFrontOfQueue(Message msg) {
MessageQueue queue = mQueue;
if (queue == null) {
return false;
}
return enqueueMessage(queue, msg, 0);
}
**post()方法**
post方法也是用于发送消息,并设置消息的callback,用于处理消息。这个方法是在Handler.java中
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
**obtainMessage**
获取消息
public final Message obtainMessage()
{
return Message.obtain(this); 【见5.2】
}
Handler.obtainMessage()方法,最终调用Message.obtainMessage(this),其中this为当前的Handler对象。
**removeMessages**
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null); 【见 4.5】
}```
Handler类似于辅助类,更多的实现都是MessageQueue, Message中的方法。Handler的目的是为了更加方便的使用消息机制。
MessageQueue
MessageQueue是消息机制的Java层和C++层的连接纽带,大部分核心方法都交给native层来处理,其中MessageQueue类中涉及的native方法如下:
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
Message
每个消息用Message表示,Message主要包含以下内容:
数据类似 | 成员变量 | 解释 |
---|---|---|
int | what | 消息类别 |
long | when | 消息触发时间 |
int | arg1 | 参数1 |
int | arg2 | 参数2 |
object | object | 消息内容 |
Handler | target | 消息相应放方 |
Runnable | callback | 回调方法 |
用markdown写表格真不方便呀,
最后就是整个消息发送和接收的流程图:
消息流程图解
- Handler通过sendMessage()发送Message到MessageQueue队列;
- Looper通过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理;
- 经过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理。
- 将Message加入MessageQueue时,处往管道写入字符,可以会唤醒loop线程;
- 如果MessageQueue中没有Message,并处于Idle状态,则会执行IdelHandler接口中的方法,往往用于做一些清理性地工作。
消息分发的优先级:
- Message的回调方法:message.callback.run(),优先级最高;
- Handler的回调方法:Handler.mCallback.handleMessage(msg),优先级仅次于1;
- Handler的默认方法:Handler.handleMessage(msg),优先级最低。
我们平时在activity的子线程通知更新主线程(MainThread)的view时很多都是通过handler的方式,也是因为在ActivityThread中有这么一个Looper存在
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();
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();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
End
最后喝水不忘挖井人,很多都是对着大神的博客然后自己对android的源码来写了.
网友评论