美文网首页
android 消息机制

android 消息机制

作者: 古宇强 | 来源:发表于2018-10-26 17:42 被阅读9次
    1.android 消息机制主要是指Handler的运行机制,Handler可以轻松地把一个任务切换到指定地的线程(后面源码分析中可以发现,其实是Handler中依赖的Looper所在的线程)中执行。最常见的一个应用场景就是在工作线程中获取到数据后,通过主线程的Handler更新UI。
    2.一个异常引发的思考

    在主线程中执行 testHandlerCreate() 方法时,发现报了一个运行时异常,此运行时异常表示:如果线程内没有调用 Looper.prepare() 就不能创建 Handler 。


    思考: 报错的代码是指向82行代码处(在工作线程中创建 Handler对象),但又可以看到77行直接创建 Handler 对象,说明在主线程内的某个地方调用了 Looper.prepare() 方法,Looper 这个类是干嘛的?Looper.prepare()是干嘛的呢?是否可以找到主线程初始化时的代码查看下是否有调用这个方法呢?
    3.Looper.prepare() 是干嘛的呢?
    Looper.java 源码

    可以看到最终调用的方法是

    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));
        }
    
    

    这个方法创建一个Looper对象并设置给成员变量 ThreadLocal

    4.主线程的初始化(main() 方法的执行)

    main() 方法代码来自于 ActivityThread.class 源码

    public static void main(String[] args) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
            CloseGuard.setEnabled(false);
    
            Environment.initForCurrentUser();
            EventLogger.setReporter(new EventLoggingReporter());
            final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
            TrustedCertificateStore.setDefaultUserDirectory(configDir);
            Process.setArgV0("<pre-initialized>");
            Looper.prepareMainLooper();
            long startSeq = 0;
            if (args != null) {
                for (int i = args.length - 1; i >= 0; --i) {
                    if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                        startSeq = Long.parseLong(
                                args[i].substring(PROC_START_SEQ_IDENT.length()));
                    }
                }
            }
            ActivityThread thread = new ActivityThread();
            thread.attach(false, startSeq);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }       
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            Looper.loop();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    

    注意到 main() 中执行的方法中与 Looper 相关的有 Looper.prepareMainLooper() 和 Looper.loop()

    ①Looper.prepareMainLooper():从上面的Looper.java源码图上,发现该方法里边调用的还是 Looper 的 prepare() 方法

    ②Looper.loop():myLooper()方法从ThreadLocal变量中获得 Looper 对象(就是 prepare() 方法中设置的Looper对象),然后开启一个死循环不断地调用 Looper 对象中的消息队列 MessageQueue 的 next() 方法取出消息,然后调用 msg.target.dispatchMessage(msg)

     public static void loop() {
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            final MessageQueue queue = me.mQueue;
    
            Binder.clearCallingIdentity();
            final long ident = Binder.clearCallingIdentity();
    
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    return;
                } 
             ...此处省略部分代码...
                try {
                    msg.target.dispatchMessage(msg);
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                ...此处省略部分代码...
                msg.recycleUnchecked();
            }
        }
    
      public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    
    5.Looper 是干嘛的呢?

    根据上面分析的prepare() 和 loop() 方法,可以总结以下 Looper 的作用:创建自身实例对象并设置给 成员变量ThreadLocal(那么这个变量又是干嘛的呢?继续往下看就知道了),创建死循环不断从成员变量MessageQueue 中取出消息,然后执行任务。

    6.ThreadLocal

    在上面分析中发现,Looper的prepare()和loop()分别调用到了 ThreadLocal的set() 和 get() 方法。那么查看z这两个方法的源码:

       public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }
      
        private T setInitialValue() {
            T value = initialValue();
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
            return value;
        }
    
        public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
       ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    

    T 表示泛型,这里指的是 Looper。set() 以调用它的ThreadLocal对象为key,把 Looper对象为value值保存在当前线程的成员变量ThreadLocalMap(ThreadLocal的内部类)中。get() 方法则是以调用它的ThreadLocal对象为key,从当前线程的成员变量ThreadLocalMap取出Looper对象。这里涉及到的几个类的关系。


    6.Message
    1. 实现了Parcelable接口,可序列化
    2. 几个需要注意的变量
      int 类型的what,arg1,arg2 ,Object类型的obj,Hanlder类型的target,Runnable类型的callback,Bundle 类型的data
    3. 构建方法:
      1. 构造方法
      2. 静态方法obtain(可能实现复用,推荐)
    7.Handler
    1. Handler的创建时所在线程一定要有Looper对象,也就是要先调用Looper.prepare()方法。



      红框中就是前面说到报运行时异常的地方

    2. 需要注意的几个方法:

      1. handleMessage(Message msg)是一个空实现,在最后dispatchMessage()中是最后执行的。
      2. dispatchMessage(),注意if条件以及执行顺序


      3. post,send,remove,has开头的方法
      4. obtainMessage 可能复用message
    8.主要涉及到的类以及他们之间的关系

    总结:

    相关文章

      网友评论

          本文标题:android 消息机制

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