您的位置:首页 > 移动开发 > Android开发

android 消息机制

2018-03-15 10:44 369 查看
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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  handler handler源码