Android中的消息机制

Android 中的消息机制其实就是指的是 Handler 消息机制以及附带的 LooperMessageQueue 的工作流程。

1.Android 为什么提供Handler?

  • 解决子线程不能访问 UI 的问题
    ViewRootImpl 中有一个checkThread() 方法:
void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

2. 为什么子线程不能访问UI呢?

  • AndroidUI 控件不是线程安全的,如果在多线程中并发访问 UI 控件会导致 UI 控件处于不可预期的状态

3.那为什么不给UI控件加上锁机制呢?

  • 加锁会增加UI逻辑复杂性
  • 锁机制会降低线程访问UI的效率

4.Handler 的工作原理

5.ThreadLocal

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

ThreadLocal.java

class Thread{
  /* ThreadLocal values pertaining to this thread. This map is       maintained
       * by the ThreadLocal class. */
      ThreadLocal.ThreadLocalMap threadLocals = null;
}

threadLocals 属于 Thread 里的一个变量

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
public void set(T value) {
        //获取当前的线程对象
        Thread t = Thread.currentThread();
        //getMap(t) 我们就知道了就是获取当前线程里的    
       //threadLocals 变量 ,
      //类型是ThreadLocal.ThreadLocalMap类型
        ThreadLocalMap map = getMap(t);
      //判断取出的是否为空,第一次应该为空
        if (map != null)
            //第二次不为空,将key:ThreadLocal;value:Looper存入
            map.set(this, value);
        else
            //为null时,创建ThreadLocalMap对象,并且将值与线程存入
            createMap(t, value);
    }

那么我们需要知道 ThreadLocalMap 是如何存储的?

//ThreadLocal 静态内部类
static class ThreadLocalMap {

    static class Entry extends WeakReference> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

 /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;

     xxx ....
 ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
    
/**
         * Set the value associated with key.
         *
         * @param key the thread local object
         * @param value the value to be set
         */
        private void set(ThreadLocal key, Object value) {

            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.

            Entry[] tab = table;
            int len = tab.length;
            //取出那个下标
            int i = key.threadLocalHashCode & (len-1);

            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
}

6. 主线程 的 Looper 是何时创建的?

ActivityThreadmain 方法中创建的,一起来看下:

 public static void main(String[] args) {

    Looper.prepareMainLooper();
    xxx....
   Looper.loop();
}

Looper.prepareMainLooper();

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

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

prepare 方法中又创建threadLocalLooper的关联 , 而且加了判断,说明一个线程只能有一个Looper,而后创建了 Looper 的实例。
Looper 构造方法

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

创建了 MessageQueue 实例,获取当前的线程。

而在Handler 的创建构造函数中:

public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

直接取出当前线程的 Looper--mLooperLooper 中的 MessageQueue 对象。后面就是发消息,消息入队,Looper取出消息,进而调用 handlerhandleMessage 方法。

看书,随笔记。如有问题,可指出。

你可能感兴趣的:(Android中的消息机制)