android的消息机制,这肯定是被大家源码解析,分析坏了的一部分。首先这个消息机制用的比较频繁,主要用在子线程更新UI,再一个就是这个源码比较少,也是很好理解的,我感觉每个人都会来一篇消息机制的文章。
Android消息机制概述
说到Android的消息机制,大家肯定会想到Handler。是的,Android的消息机制主要是指Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程。当我们工作的时候我们只要接触到Handler就可以了。
为什么要有Android消息机制?
我们知道Handler的主要作用是将一个任务切换到某个指定的线程去执行,比如Android规定访问UI只能在主线程中进行,如果在子线程中访问那么程序会抛异常,如下所示:
void checkThread(){
if(mThread != Thread.currentThread()){
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
为什么系统不允许在子线程中访问UI呢?这是因为Android的UI控件不是线程安全的,如果在多线程中访问UI控件则会导致不可预期的状态。那为什么不对UI控件访问加锁呢?缺点有两个:首先加锁会让UI控件的访问的逻辑变的复杂;其次,锁机制会降低UI的访问效率。那我们不用线程来操作不就行了吗?但这是不可能的,因为Android的主线程不能执行耗时操作,否则会出现ANR。所以,从各方面来说,Android消息机制是为了解决在子线程中无法访问UI的矛盾。
在这里就不进行MessageQueue和Looper的分析了,大家如果想看可以百度,或者自己去看下源码就都可以看懂的。我们接下来主要分析Handler处理消息的几种方式,以及他们在执行时的顺序。
我们都知道主线程是可以直接new Handler()的,而在子线程中却是不可以的,我们去看下这是为什么?
if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); }
如果在子线程中会报这么一个错误,这是因为在子线程中没有Looper对象。如果子线程也想创建Handler,可以去参考HandlerThread这个类的实现。为什么主线程是可以的,那就要去找一个ActivityThread的类了,这是在应用启动的时候开启的一个线程也是传说中的UI线程或主线程,源码:
public static void main(String[] args) { ... //这个地方就是创建一个Looper,并且放在ThreadLocal里。下面会有源码 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")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); // Loop.loop()是一个死循环,但是在没有消息的时候会堵塞 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
public static void prepareMainLooper() { // 这里就是创建Looper的方法 prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } // 这个地方就是把在prepare(false)中存入的Looper取出来 sMainLooper = myLooper(); } }
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //创建一个Looper放到ThreadLocal里面 sThreadLocal.set(new Looper(quitAllowed)); }
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }这个地方用到了ThreadLocal,大家可以自己去自行百度。
好了这个地方已经解释了UI线程里面可以new Handler的原因。下面就是我们要解释Handler出来消息的几种方式。
在这我就默认大家都是知道Message在处理的时候是调用msg.target.dispatchMessage()方法来处理,这个msg.target就是我们Handler。下面我们来看下这个dispatchMessage方法
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { // 处理消息的第一种方式 handleCallback(msg); } else { if (mCallback != null) { // 处理消息的第二种方式 if (mCallback.handleMessage(msg)) { return; } } // 处理消息的第三种方式 handleMessage(msg); } }
在这里可以看出处理消息有三种方式,并且彼此不会重叠,都是执行一次。
第一种方式:
private static void handleCallback(Message message) { message.callback.run(); }这个地方message.callback是什么地方给的呢?
handler.post(new Runnable() { @Override public void run() { } });
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
private static Message getPostMessage(Runnable r) { //这个地方就是把Runnable赋值给Message的callback了,也就有我们上面代码的调用了 Message m = Message.obtain(); m.callback = r; return m; }第二种方式:Handler为了防止在创建是都要写一个子类,所以提供了一个接口,也就是Handler.Callback
public interface Callback { public boolean handleMessage(Message msg); }Handler的创建方式:
Handler handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { return false; } });
这种方式处理完Message不要忘记返回true。
第三种方式:调用handleMessage方法
Handler的创建方式:
Handler handler2 = new Handler(){ @Override public void handleMessage(Message msg) { } };就会调用我们写的handleMessage方法,进行处理Message。