Android消息机制深入(一)

作者: 王世军Steven | 来源:发表于2017-04-13 08:37 被阅读78次

    Android消息机制介绍

    Android的消息机制主要是指Handler的运行机制, Handler需要底层的MessageQueue, 和 Looper 的支撑.

    Handler 机制理解图
    1. MessageQueue : 消息队列, 它的内部可以存储一组消息,并且提供了添加和删除小的接口.虽然名为消息队列但是内部并不是一个队列,而是一个单链表.
    2. Looper : 中文翻译为循环, 由于 MessageQueue 只能保存消息并不能处理消息, Looper就是用来出来处理消息的. Looper会以无限循环的方式去查询MessageQueue中是否有消息.如果有消息就直接处理,否则就一直等待.

    注意一 :ThreadLocal 可以在每一个线程中存储数据, 它可以在多个个线程中互不干扰得存储并提供数据. 通过ThreadLocal 可以轻松的获取每个线程的Looper.

    注意二 : 新创建的线程默认是没有Looper的.如果需要关联Handler就必须为当期那线程创建Looper.

    注意三 : 主线程(UI线程), 他就是ActivityThread, ActivityThread创建的时候就会初始化Looper.因此在主线程中可以直接使用Handler.

    为什么要引入消息机制(Handler)

    Handler的作用实质上就是将一个任务切换到指定的线程中去执行 . Android提供这一功能主要基于以下几点 :

    • Android规定智能在主线程中更新UI,如果在子线程中更新UI程序便会崩溃. ViewRootImpl类中的checkThread()方法进行了这个工作.源码如下 :
    // 检查是否是在主线程中更新UI
    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }
    
    • Android 不建议在主线程中进行耗时操作, 否则会引发 ANR .

    基于以上两点我们就需要在子线程中进行耗时任务,比如:联网获取数据等,然后再跳转到UI线程中更新UI.

    1. 其实Android不允许在子线程中更新UI的一个重要原因是Android的UI控件是线程不安全的.如果多个线程同时操作UI会造成不可预知的后果 .
    2. Android采用锁机制来保证UI控件的线程安全有有两个原因 :

      2.1 上锁会让UI访问的逻辑变的复杂.

      2.2 上锁会降低UI的访问效率.

    ThreadLocal 类

    ThreadLocal 类是一个线程内的数据存储类. 通过他可以在指定的线程存储数据, 存储以后只能在指定的线程中才可以获取到存储的数据,对于其他线程则无法获取到数据.

    测试实例

    /**
     * 定义一个Boolean类型的的ThreadLocal.
     */
    private ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<>();
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 1. 在主线程中设置为True
        mThreadLocal.set(true);
        Log.d(TAG, "MainThread : " + mThreadLocal.get());
        // 2. 在线程一种进行操作
        new Thread(new Runnable() {
            @Override
            public void run() {
                mThreadLocal.set(false);
                Log.d(TAG, "Thread-1 : " + mThreadLocal.get());
            }
        }).start();
        // 3. 在线程2中操作
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "Thread-2 : " + mThreadLocal.get());
            }
        }).start();
    }
    // 输出
    // D/MainActivity: MainThread : true
    // D/MainActivity: Thread-1 : false
    // D/MainActivity: Thread-2 : null
    

    从上面的例子中可以看出,在不同线程中设置同一个ThreadLoacal但是结果不一样.这就实现了线程范围内的数据约束.

    • set()方法源码
    public void set(T value) {
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取该线程中的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null)
            // 如果已经有了则直接保存
            map.set(this, value);
        else
            createMap(t, value);// 创建当前线程的ThreadLocalMap对象
    }
    
    • get()方法源码
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        // 创建当前线程的ThreadLocal并且,设置初始值,默认是null.
        return setInitialValue();
    }
    

    从ThreadLocal的set和get方法可以看出, 他们操作的对象都是当前线程绑定的那个ThreadLocal副本, 因此在不同的线程中访问同一个ThreadLocal的get和set方法,它们都是在操作当前线程中的ThreadLocal副本.

    MessageQueue的工作原理

    消息队列在Android中主要是指 MessageQueue ,主要操作如下:

    1. enqueueMessage() : 往消息队列中插入一条消息.就是单链表的插入操作
    2. next() 从消息队列中取出一条消息并将它从消息队列删除. next()方法是一个死循环,如果消息队列中没有消息则next() 方法会一直阻塞在这, 当有消息到来时, next() 方法就会返回这条消息将消息从单链表中删除.

    具体实现而已参照源码

    Looper的工作原理

    Looper 会不停的从MessageQueue中查询是否有消息, 如果有消息就会立即处理,否则就会一直阻塞在那.

    Looper构造方法

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    

    创建和启动Looper

    new Thread(new Runnable() {
        @Override
        public void run() {
            // 1. 创建Looper
            Looper.prepare();
            // 2. 操作
            Handler handler = new Handler();
            // 3. 开启Looper, 此方法会阻塞.
            Looper.loop();
        }
    }).start();
    

    退出Looper
    Looper提供了两个退出方法:

    1. quit : 直接退出Looper.
    2. quitSafely : 设置退出标志,一旦消息处理完则退出.

    Looper 的 loop() 方法

     public static void loop() {
         // 获取当前Looper
         final Looper me = myLooper();
         if (me == null) {
             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
         }
         // 获取当前Looper的MessageQueue
         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的next() 方法,该方法是一个阻塞方法.
             Message msg = queue.next(); // might block
             if (msg == null) {
                 // 当MessageQueue退出时会返回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.traceBegin(traceTag, msg.target.getTraceName(msg));
             }
             try {
                 // msg.target : 是指定的Handler 对象.
                 // dispatchMessage : 方法会到指定的线程中执行.讲逻辑切换到指定的线程中.
                 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();
         }
     }
    

    Handler的工作原理

    Handler的工作主要包含消息的发送和接收过程, 消息的发送可以通过postXXX和sendXXX方法, 其实postXXX 是通过 sendXXX 的一系列方法来实现的.

    postXXX 是通过sendXXX 实现的,源码如下

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    public final boolean postAtTime(Runnable r, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
    

    分析Handler消息发送部分的源码

    public final boolean sendMessage(Message msg)
    {
        // 调用sendDelayed
        return sendMessageDelayed(msg, 0);
    }
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    

    通过源码我们可以最终是调用了MessageQueue的enqueueMessage()也就是网消息队列中插入一个消息.

    通过前面对Looper的loop()方法的分析我们知道最终会调用Handler的dispatchMessage,分析下源码

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        // 1. 判断msg 是否提供回调方法
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            // 2. 判断Handler是否有回调 
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 3. 执行Handler的handleMessage
            handleMessage(msg);
        }
    }
    
    • 首先会检查Message的callback 是否为null.

      如果不为null , 就通过 handleCallback 来处理消息. Message的callback是一个Runnable对象,在Message中定义如下 :
    Runnable callback;
    



    handleCallback源码

    private static void handleCallback(Message message) {
         // 调用run() 方法.
         message.callback.run();
    }
    
    • 其次检查mCallBack是否为null,通过mCallBack的handleMessage(msg)来处理消息,CallBack是一个接口定义如下 :
    public interface Callback {
        public boolean handleMessage(Message msg);
    }
    

    通过CallBack可以通过如下方式创建Handler对象, Handler handler = new Handler(callback);

    Handler消息机制处理流程
    • 最后通过Handler的handleMessage(msg)方法处理消息.

    主线程的消息循环

    Android的主线程就是ActivityThread, 主线程的入口方法是main如下.

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        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());
        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
        Process.setArgV0("<pre-initialized>");
        // 创建主线程的Looper
        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);
        // 启动主线程的Looper.
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    
    • 通过源码发现有进行如下两步操作
      • 调用 Looper.prepareMainLooper();创建主运行循环.
      • 调动 Looper.loop();开启消息循环.

    在ActivityThread中的Handler就是ActivityThread.H 在H中定义了很多消息.主要包括了四大组件的停止和运行等.

    private class H extends Handler {
       public static final int LAUNCH_ACTIVITY         = 100;
       public static final int PAUSE_ACTIVITY          = 101;
       public static final int PAUSE_ACTIVITY_FINISHING= 102;
       public static final int STOP_ACTIVITY_SHOW      = 103;
       public static final int STOP_ACTIVITY_HIDE      = 104;
       public static final int SHOW_WINDOW             = 105;
       public static final int HIDE_WINDOW             = 106;
       public static final int RESUME_ACTIVITY         = 107;
       public static final int SEND_RESULT             = 108;
       public static final int DESTROY_ACTIVITY        = 109;
       public static final int BIND_APPLICATION        = 110;
       public static final int EXIT_APPLICATION        = 111;
       public static final int NEW_INTENT              = 112;
       public static final int RECEIVER                = 113;
       public static final int CREATE_SERVICE          = 114;
       public static final int SERVICE_ARGS            = 115;
       public static final int STOP_SERVICE            = 116;
       public static final int CONFIGURATION_CHANGED   = 118;
       public static final int CLEAN_UP_CONTEXT        = 119;
       public static final int GC_WHEN_IDLE            = 120;
       public static final int BIND_SERVICE            = 121;
       public static final int UNBIND_SERVICE          = 122;
       public static final int DUMP_SERVICE            = 123;
       public static final int LOW_MEMORY              = 124;
       public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;
       public static final int RELAUNCH_ACTIVITY       = 126;
       public static final int PROFILER_CONTROL        = 127;
       public static final int CREATE_BACKUP_AGENT     = 128;
       public static final int DESTROY_BACKUP_AGENT    = 129;
       public static final int SUICIDE                 = 130;
       public static final int REMOVE_PROVIDER         = 131;
       public static final int ENABLE_JIT              = 132;
       public static final int DISPATCH_PACKAGE_BROADCAST = 133;
       public static final int SCHEDULE_CRASH          = 134;
       public static final int DUMP_HEAP               = 135;
       public static final int DUMP_ACTIVITY           = 136;
       public static final int SLEEPING                = 137;
       public static final int SET_CORE_SETTINGS       = 138;
       public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;
       public static final int TRIM_MEMORY             = 140;
       public static final int DUMP_PROVIDER           = 141;
       public static final int UNSTABLE_PROVIDER_DIED  = 142;
       public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143;
       public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
       public static final int INSTALL_PROVIDER        = 145;
       public static final int ON_NEW_ACTIVITY_OPTIONS = 146;
       public static final int CANCEL_VISIBLE_BEHIND = 147;
       public static final int BACKGROUND_VISIBLE_BEHIND_CHANGED = 148;
       public static final int ENTER_ANIMATION_COMPLETE = 149;
       public static final int START_BINDER_TRACKING = 150;
       public static final int STOP_BINDER_TRACKING_AND_DUMP = 151;
       public static final int MULTI_WINDOW_MODE_CHANGED = 152;
       public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
       public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
    }
    

    ActivityThread通过ApplicationThread和AMS进行进程间通信, AMS以进程间通信的方式完成ActivityThread的请求后回调ApplicationThread的Binder方法, 然后ApplicationThread会想H发送消息,将逻辑从ApplicationThread切换到ActivityThread中.这就是主线程的消息循环类型

    相关文章

      网友评论

        本文标题:Android消息机制深入(一)

        本文链接:https://www.haomeiwen.com/subject/pwnvattx.html