android 消息机制

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("");
        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 消息机制)