1.概述

​ Android的消息机制主要是指Hanlder的运行机制及其附带的MessageQueue和Looper的工作过程。三者作为一个整体来实现消息机制。

  • Handler的主要作用是将一个任务切换到某个指定的线程中去执行,最主要的一个场景我们开启一个子线程来处理一些耗时的任务,在耗时任务结束后返回主线程去更新ui。那为什么我们不能直接在子线程中去更新ui呢,这是因为Android规定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.");        }    }
  • 为什么系统不允许在子线程访问UI?

    这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问控件可能会导致UI控件处于不可预期状态。so,那为什么系统不对控件加上安全锁的机制?缺点有两个:首先控件加上锁机制会导致UI访问逻辑复杂;其次锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。

    因此,比较好的方案就是采用单线程的模型来处理UI,开发者只需要切换一下线程去执行ui操作即可。

  • Handler另外一个作用是可以发送一个延时消息

2.原理概述

​ Handler的使用方法大致如下:

private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what) {                case 0:                    mTestTV.setText("This is handleMessage");//更新UI                    break;            }        }    };      new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络                    mHandler.sendEmptyMessage(0);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }).start();
  • 大致原理:

    Handler与其内部的LooperMessageQueue一起协同工作。首先通过Handler的post方法将一个Runnable投递到Handler内部的MessageQueue中,也可以用Handler的send方法发送一个Message,其实post方法最终也是通过send方法实现的。

    ​ 当Handler的send方法被调用时,会调用MessageQueue的enqueueMessage方法将这个消息放入消息队列中,然后Looper会一直检查MessageQueue中的消息,当发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或者Handler中的handleMessage方法会被调用来处理具体的工作。

3.ThreadLocal工作原理

3.1 ThreadLocal的使用场景

​ 早在JDK 1.2的版本中就提供java.lang.ThreadLocalThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

ThreadLocal是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储以后只可以在当前线程中获取到存储的数据,对于其他线程来说是无法获取到的。

主要的应用场景是:

  • 以线程为作用域并且不同的线程具有不同的数据副本。这种情况对应与我们要讲的Handler和Looper。

  • 在复杂逻辑下的对象传递,如监听器的传递。比如监听器的传递,有时候一个线程中的任务过于复杂,可能表现为函数调用栈比较深或者代码入口的多样性,在这种情况下,我们一般的作法有大致两种:

    • 第一种方法是将监听器通过参数的形式在函数调用栈中进行传递,这个方法的局限性在于函数调用栈很深时,通过函数传递会让程序看起来很混乱糟糕。
    • 第二种方法是将监听器作为静态变量供线程访问,这个方法的局限性是缺乏扩充性。比如如果需要有两个线程并发执行,那就需要提供两个静态的监听器对象。如果有10个监听器对象,提供10个监听器对象?这是无法接受的

    而采用ThreadLocal就会很简单,只需要维护一个ThreadLocal,就可以实现不同线程存储自己的监听器对象,互不干扰,代码逻辑也会很简洁。

3.2 ThreadLocal的使用方法

private ThreadLoacl mBooleanThreadLocal = new ThreadLocal;mBooleanThreadLocal.set(true);Log.d(TAG,"[ThreadMain] mBooleanThreadLocal = :" + mBooleanThreadLocal.get());new Thread("Thread#1") {  @Override  public void run () {    mBooleanThreadLocal.set(false);    Log.d(TAG,"[Thread#1] mBooleanThreadLocal = :" + mBooleanThreadLocal.get());  }}new Thread("Thread#2") {  @Override  public void run () {    Log.d(TAG,"[Thread#2] mBooleanThreadLocal = :" + mBooleanThreadLocal.get());  }}

运行结果:

[ThreadMain] mBooleanThreadLocal = : true[Thread#1] mBooleanThreadLocal = : false[Thread#2] mBooleanThreadLocal = : null

​ 没有为ThreadLocal设置的情况下默认get()到的会是null。

3.3 ThreadLocal原理

​ ThreadLocal 是一个泛型类,定义为 public class ThreadLocal

​ 在Java源码中有一个ThreadLocal.java,在Android源码中也有一个ThreadLocal.java。两者的主要区别在于他内部的实现方式不同。

3.3.1 java中的ThreadLocal

​ ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。

​ ThreadLocal的set方法:

ThreadLocal.javapublic void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }

​ 在set方法中首先获取到当前Thread的对象。然后去获取ThreadLocalMap,这个ThreadLocalMap它是在每一个Thread中都有一份,可以看下getMap(t).

ThreadLocalMap getMap(Thread t) {  return t.threadLocals;}

​ 代码很简单就是返回当前线程中的threadLocals 对象。可以看下Thread.java中的定义。

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

​ 接着看set()方法,如果得到的map不为空就会把当前的ThreadLocal本身作为Key值,需要保存的value值作为Value

​ 到这里可以大致理解如下:在每一个Thread中都维护一个ThreadLocalMap,这个ThreadLocalMap 的Key为ThreadLocal对象,Value为保存的值,这样一个线程中就存了多个ThreadLocal对应的值。

​ 如果得到的ThreadLocalMap为空,就去createMap,实现如下:

void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }

​ 可以看下ThreadLocalMap的构造方法:

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

​ 通过变量命名及实现,可以知道这个构造方法就是一个初始化并put一个键值对。

​ 接着看下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;    }    return setInitialValue();}

​ 首先去获取到线程的ThreadLocalMap,如果ThreadLocalMap不为空,那么就去获取当前线程对应的value值。否则化会去调用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;}

​ 首先initialValue()方法是一个空的实现,默认返回null,可以通过重写该方法来返回默认的value。后面的步骤就是去初始化了。

public class TestThreadLocal {    private static final ThreadLocal value = new ThreadLocal() {        @Override        protected Integer initialValue() {            return Integer.valueOf(1);//默认返回1        }    };}
  • 内存泄露问题

    下图是本文介绍到的一些对象之间的引用关系图,实线表示强引用,虚线表示弱引用:

​ 然后网上就传言,ThreadLocal会引发内存泄露,他们的理由是这样的:
如上图,ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用引用他,那么系统gc的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
永远无法回收,造成内存泄露。

​ 首先从ThreadLocal的直接索引位置(通过ThreadLocal.threadLocalHashCode & (len-1)运算得到)获取Entry e,如果e不为null并且key相同则返回e;
​ 如果e为null或者key不一致则向下一个位置查询,如果下一个位置的key和当前需要查询的key相等,则返回对应的Entry,否则,如果key值为null,则擦除该位置的Entry,否则继续向下一个位置查询
​ 在这个过程中遇到的key为null的Entry都会被擦除,那么Entry内的value也就没有强引用链,自然会被回收。仔细研究代码可以发现,set操作也有类似的思想,将key为null的这些Entry都删除,防止内存泄露。
​ 但是光这样还是不够的,上面的设计思路依赖一个前提条件:要调用ThreadLocalMap的getEntry函数或者set函数。这当然是不可能任何情况都成立的,所以很多情况下需要使用者手动调用ThreadLocal的remove函数,手动删除不再需要的ThreadLocal,防止内存泄露。所以JDK建议将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。

3.3.2 Android 中的ThreadLocal

​ set()方法:

public void set(T value) {    Thread currentThread = Thread.currentThread();    Values values = values(currentThread);    if (values == null) {        values = initializeValues(currentThread);    }    values.put(this, value);}

​ 这里新增了一个Values的对象。这个对象定义在ThreadLocal.Values。 每一个Thread中都有一个这样的变量localValues。

Values values(Thread current) {        return current.localValues;    }
Values initializeValues(Thread current) {        return current.localValues = new Values();    }
void put(ThreadLocal<?> key, Object value) {    cleanUp();    // Keep track of first tombstone. That's where we want to go back    // and add an entry if necessary.    int firstTombstone = -1;    for (int index = key.hash & mask;; index = next(index)) {        Object k = table[index];        if (k == key.reference) {            // Replace existing entry.            table[index + 1] = value;            return;        }        if (k == null) {            if (firstTombstone == -1) {                // Fill in null slot.                table[index] = key.reference;                table[index + 1] = value;                size++;                return;            }            // Go back and replace first tombstone.            table[firstTombstone] = key.reference;            table[firstTombstone + 1] = value;            tombstones--;            size++;            return;        }        // Remember first tombstone.        if (firstTombstone == -1 && k == TOMBSTONE) {            firstTombstone = index;        }    }}

​ 这里的算法不去深入探索,但是可以看到,在table数组中存储value的地方。

table[index] = key.reference;table[index + 1] = value;

​ 接着看下get()方法

 public T get() {        // Optimized for the fast path.        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values != null) {            Object[] table = values.table;            int index = hash & values.mask;            if (this.reference == table[index]) {                return (T) table[index + 1];            }        } else {            values = initializeValues(currentThread);        }        return (T) values.getAfterMiss(this);    }

​ 这里就比较好理解了,返回index+1位置的值。

3.4Handler中的ThreadLocal

​ 具体到Handler中来说,对于每一个Handler它都需要一个唯一对应的Looper。通过ThreadLocal就可以实现在Looper中维护一份ThreadLocal就可以满足不同的线程对应不同的Looper.

 Looper.java static final ThreadLocal sThreadLocal = new ThreadLocal();public static void prepare() {        prepare(true);   }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));   }

​ 如果不这样做,那么系统就必须提供一个全局的哈希表供Handler查找指定线程的Looper,这就势必会增加一个类似于LooperManager的类了,这种方法繁琐复杂。系统没有采用这种方案而是采用了ThreadLocal,这就是ThreadLocal的好处。

4. MessageQueue

​ MessageQueue主要包含两个操作enqueneMessage() 和 next()。enqueneMessage作用是向消息队列中插入一条消息,而next则是从消息队列中取出一条消息,同时将其从消息队列中移除。

​ 尽管MessageQueue 叫消息队列,但是它的实现并不是队列,而是一个单链表的数据结构。

  • enqueneMessage

       boolean enqueueMessage(Message msg, long when) {        if (msg.target == null) {            throw new IllegalArgumentException("Message must have a target.");        }        if (msg.isInUse()) {            throw new IllegalStateException(msg + " This message is already in use.");        }        synchronized (this) {            if (mQuitting) {                IllegalStateException e = new IllegalStateException(                        msg.target + " sending message to a Handler on a dead thread");                Log.w(TAG, e.getMessage(), e);                msg.recycle();                return false;            }            msg.markInUse();            msg.when = when;            Message p = mMessages;            boolean needWake;            if (p == null || when == 0 || when < p.when) {                // New head, wake up the event queue if blocked.                msg.next = p;                mMessages = msg;                needWake = mBlocked;            } else {                // Inserted within the middle of the queue.  Usually we don't have to wake                // up the event queue unless there is a barrier at the head of the queue                // and the message is the earliest asynchronous message in the queue.                needWake = mBlocked && p.target == null && msg.isAsynchronous();                Message prev;                for (;;) {                    prev = p;                    p = p.next;                    if (p == null || when < p.when) {                        break;                    }                    if (needWake && p.isAsynchronous()) {                        needWake = false;                    }                }                msg.next = p; // invariant: p == prev.next                prev.next = msg;            }            // We can assume mPtr != 0 because mQuitting is false.            if (needWake) {                nativeWake(mPtr);            }        }        return true;    }

    ​ 主要实现就是一个链表的插入操作。

  • next

        Message next() {        // Return here if the message loop has already quit and been disposed.        // This can happen if the application tries to restart a looper after quit        // which is not supported.        final long ptr = mPtr;        if (ptr == 0) {            return null;        }        int pendingIdleHandlerCount = -1; // -1 only during first iteration        int nextPollTimeoutMillis = 0;        for (;;) {            if (nextPollTimeoutMillis != 0) {                Binder.flushPendingCommands();            }            nativePollOnce(ptr, nextPollTimeoutMillis);            synchronized (this) {                // Try to retrieve the next message.  Return if found.                final long now = SystemClock.uptimeMillis();                Message prevMsg = null;                Message msg = mMessages;                if (msg != null && msg.target == null) {                    // Stalled by a barrier.  Find the next asynchronous message in the queue.                    do {                        prevMsg = msg;                        msg = msg.next;                    } while (msg != null && !msg.isAsynchronous());                }                if (msg != null) {                    if (now < msg.when) {                        // Next message is not ready.  Set a timeout to wake up when it is ready.                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                    } else {                        // Got a message.                        mBlocked = false;                        if (prevMsg != null) {                            prevMsg.next = msg.next;                        } else {                            mMessages = msg.next;                        }                        msg.next = null;                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);                        msg.markInUse();                        return msg;                    }                } else {                    // No more messages.                    nextPollTimeoutMillis = -1;                }                // Process the quit message now that all pending messages have been handled.                if (mQuitting) {                    dispose();                    return null;                }                // If first time idle, then get the number of idlers to run.                // Idle handles only run if the queue is empty or if the first message                // in the queue (possibly a barrier) is due to be handled in the future.                if (pendingIdleHandlerCount < 0                        && (mMessages == null || now < mMessages.when)) {                    pendingIdleHandlerCount = mIdleHandlers.size();                }                if (pendingIdleHandlerCount <= 0) {                    // No idle handlers to run.  Loop and wait some more.                    mBlocked = true;                    continue;                }                if (mPendingIdleHandlers == null) {                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];                }                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);            }            // Run the idle handlers.            // We only ever reach this code block during the first iteration.            for (int i = 0; i < pendingIdleHandlerCount; i++) {                final IdleHandler idler = mPendingIdleHandlers[i];                mPendingIdleHandlers[i] = null; // release the reference to the handler                boolean keep = false;                try {                    keep = idler.queueIdle();                } catch (Throwable t) {                    Log.wtf(TAG, "IdleHandler threw exception", t);                }                if (!keep) {                    synchronized (this) {                        mIdleHandlers.remove(idler);                    }                }            }            // Reset the idle handler count to 0 so we do not run them again.            pendingIdleHandlerCount = 0;            // While calling an idle handler, a new message could have been delivered            // so go back and look again for a pending message without waiting.            nextPollTimeoutMillis = 0;        }    }

    ​ next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法就会一直堵塞在这里,只有新消息到来时,next方法才会返回这条消息并将其从单链表中移除、

5 Looper

  • Looper的构造方法:
    Looper.java        public static void prepare() {        prepare(true);    }    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));    }    private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }

​ 在Prepare中会检查当前ThreadLocal是否已经有了looper对象,如果有了就会抛出异常,也就是说prepare()方法一个线程中一个生命周期内只能调用一次。

​ 在构造方法内会new一个MessageQueue,用作消息队列。

​ Looper除了prepare方法外,还提供了prepareMainLooper()方法,这个方法主要是用来给ActivityThread创建Looper使用的,其本质也是调用prepare方法来实现的。但是这个Looper不可以退出。

  • Looper是可以退出的,可以通过两种方式:

    • quit: 直接退出Looper

    • quitSafely:会设定一个退出的标记,然后把消息队列中的已有消息都处理完毕后才会安全的退出。但是delay的消息不会被保证结束前执行。

      MessageQueue  void quit(boolean safe) {    if (!mQuitAllowed) {        throw new IllegalStateException("Main thread not allowed to quit.");    }    synchronized (this) {        if (mQuitting) {            return;        }        mQuitting = true;        if (safe) {            removeAllFutureMessagesLocked();        } else {            removeAllMessagesLocked();        }        // We can assume mPtr != 0 because mQuitting was previously false.        nativeWake(mPtr);    }}

    在子线程中,我们手动创建了Looper后,在所有任务结束后应该调用quit方法来终止消息循环,否则这个子线程就会一直处于消息等待的状态。而如果退出Looper之后,这个线程也会立刻终止。因此在不需要Looper的时候应该手动终止它。

  • loop方法

       /**     * Run the message queue in this thread. Be sure to call     * {@link #quit()} to end the loop.     */    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;        // 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 msg = queue.next(); // might block            if (msg == 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            Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            msg.target.dispatchMessage(msg);            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();        }    }
    • looper方法是一个死循环,唯一跳出循环的方式是MessageQueue返回了null,也就是表明MessageQueue正在quit。当Looper调用了quit或者quitsafely时,会调用MessageQueue的quit或者quitsafely。当消息队列MessageQueue被标记为退出状态时,也就是isQuitting 标志,enqueueMessage 不在接受新消息,并返回false。此时当当前队列中的消息处理完毕后,会判断标志位,如果是即将退出的标志,那么直接返回null,Looper也会停止死循环。
    • 当正常的接收消息时,会调用msg.target.dispatchMessage(msg);来分发消息,但是Handler中dispatchMessage方法是在创建Handler时所使用的线程中执行的。这样就完成切换逻辑。

6.Handler

  • 常用的新消息发送方法

    Handler.java public final boolean post(Runnable r) {  return  sendMessageDelayed(getPostMessage(r), 0);}public final boolean postDelayed(Runnable r, long delayMillis) {  return sendMessageDelayed(getPostMessage(r), delayMillis);}private static Message getPostMessage(Runnable r) {  Message m = Message.obtain();  m.callback = r;  return m;}----------public final boolean sendMessage(Message msg){  return sendMessageDelayed(msg, 0);}public final boolean sendMessageDelayed(Message msg, long delayMillis){  if (delayMillis < 0) {    delayMillis = 0;  }  return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}public final boolean sendEmptyMessage(int what){  return sendEmptyMessageDelayed(what, 0);}public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {  Message msg = Message.obtain();  msg.what = what;  return sendMessageDelayed(msg, 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);}
  • 以上post类方法允许你排列一个Runnable对象到主线程队列中,最终会调用到sendMessage

  • sendMessage类方法, 会发送一个带数据的Message对象到队列中。

  • Looper会通过messageQueue的next方法拿到消息进行处理,最后交给Handler处理,即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);        }    }
  • 会首先判断Message的callBack是否存在,如果存在,就会调用callback

   private static void handleCallback(Message message) {        message.callback.run();    }
  • 接着会去判断mCallBack,mCallBack是我们在构造函数传入的

    public Handler(Looper looper, Callback callback, boolean async) {        mLooper = looper;        mQueue = looper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

这里的CallBack对象是什么:

/**     * Callback interface you can use when instantiating a Handler to avoid     * having to implement your own subclass of Handler.     *     * @param msg A {@link android.os.Message Message} object     * @return True if no further handling is desired     */    public interface Callback {        public boolean handleMessage(Message msg);    }

这里提示了一种新的构造方法:

Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {      @Override      public boolean handleMessage(Message msg) {        return false;      }    });Handler smsHandler = new Handler(){      public void handleMessage(Message msg) {        return false;      };    };Message m = Message.obtain(h, new Runnable() {          @Override          public void run() {                  //做一些事情,这个run方法是主线程调用的          }  });  Handler Handler = new Handler();handler.sendMessage(m);  

​ 如果没有mCallBack,那么最后就会调用handleMessage方法.

  • 这里的优先级

run(callback) > mCallBack > handleMessage

7.主线程的消息循环

​ Android的主线程是ActivityThread,主线程的入口方法为main方法。在main方法系统会通过Looper。prepareMainLooper来创建主线程的Looper及MessageQueue,并通过Looper.loop来开启循环。

public static void main(String[] args) {          .....        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.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

MainLooper也只能prepare一次。

  /**     * Initialize the current thread as a looper, marking it as an     * application's main looper. The main looper for your application     * is created by the Android environment, so you should never need     * to call this function yourself.  See also: {@link #prepare()}     */    public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }

因为主线程的Looper比较特殊,所有Looper提供了一个getMainLooper的方法来获取。

/**  * Returns the application's main looper, which lives in the main thread of the application. */public static Looper getMainLooper() {    synchronized (Looper.class) {        return sMainLooper;    }}

上述会在ActivityThread中开启Looper,并在Looper中初始化MessAgeQueue,这个时候还缺少一个Handler.

Handler是定义在ActivityThread.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;    ...         String codeToString(int code) {            if (DEBUG_MESSAGES) {                switch (code) {                    case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";                    case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY";                    case PAUSE_ACTIVITY_FINISHING: return "PAUSE_ACTIVITY_FINISHING";                    case STOP_ACTIVITY_SHOW: return "STOP_ACTIVITY_SHOW";                    ......                    }           } }

​ ActivityThread 通过ApplicationThread与AMS进程通信,AMS以进程间通信方试完成ActivityThread的请求后会回调ApplicationThread的Binder方法,然后ApplicationThread会通过H发送消息,H收到消息后会将逻辑切换到ActivityThread中即主线程中进行。这就是主线程的消息循环模型。

8. PS

  • 在开发的过程中会碰到一个棘手的问题,调用Activity.finish函数Acitivity没有执行生命周期的ondestory函数,是因为有一个handler成员,因为它有一个delay消息没有处理,调用Activity.finish,Activity不会马上destory,所以记得在Ativity finish前清理一下handle中的未处理的消息,这样Activity才会顺利的destory。

  • 主线程的Handler和 Looper为什么不会阻塞卡死

    1. epoll模型
      当没有消息的时候会epoll.wait,等待句柄写的时候再唤醒,这个时候其实是阻塞的。
    2. 所有的ui操作都通过handler来发消息操作。
      比如屏幕刷新16ms一个消息,你的各种点击事件,所以就会有句柄写操作,唤醒上文的wait操作,所以不会被卡死了。
  • 内存泄漏

    Handler handler = new Handler() {            @Override          public void handleMessage(Message msg) {              dosomething();            }      };  

    ​ 它默认是可以使用外部类的成员变量的,这样也佐证了我们所说的它会隐式的持有外部类的引用;这时候如果子线程使用handler将message消息发送到messageQueue中并等待执行的过程过长,这时候activity已经执行finish方法;那么我们希望的是activity被执行onDestory方法,然后activity相关的各种资源,组件都被销毁掉,但是由于handler隐式的持有activity的引用,那么activity就无法被回收,activity相关的资源与组件也无法被回收--即内存已经泄露。

    ​ 解决方案:

    • 使用static 静态变量定义handler

      static Handler handler = new Handler() {            @Override          public void handleMessage(Message msg) {              dosomething();            }      };  

      这样的话,handler对象由于是静态类型无法持有外部activity的引用,但是这样Handler不再持有外部类的引用,导致程序不允许在Handler中操作activity中的对象了,这时候我们需要在Handler对象中增加一个队activity的弱引用;

      static class MyHandler extends Handler {      WeakReference mActivityReference;      MyHandler(Activity activity) {          mActivityReference= new WeakReference(activity);      }      @Override      public void handleMessage(Message msg) {          final Activity activity = mActivityReference.get();          if (activity != null) {              mImageView.setImageBitmap(mBitmap);          }      }  }  
    • 通过代码逻辑判断

      在activity执行onDestory时,判断是否有handler已经执行完成,否则做相关逻辑操作。

  • 主线程往子线程发消息

    在子线程中创建Handler,需要手动初始化Looper。

  • Handler常见的两个异常

    在使用Handler时,经常会出现以下两个异常:

    (1)CalledFromWrongThreadException:这种异常是因为尝试在子线程中去更新UI,进而产生异常。

    (2)Can't create handle inside thread that ha not called Looper.prepared:是因为我们在子线程中去创建Handler,而产生的异常。

  • 为什么在有些时候子线程中是可以直接更新UI的:

    为了回答这个问题,我们需要先通过看源码去了解下面这三个问题:

    (1)Android是如何检测非UI线程去更新UI的

    (2)ViewRootImp是什么?

    (3)ViewRootImp是在哪里创建的?

    源码我就不贴出来了,这里我只是总结一下。

    答案:

    非UI线程真的不能更新UI吗? 是可以的。

    解释:

    在线程中更新UI时会调用ViewParent.invalidateChild()方法检查当前的thread是否是Mainthread。

    ​ 但是,ViewRootImpl这个类是在activity的onResume()方法中创建的。就算在子线程中更新UI,只要在ViewRootImpl创建之前更新UI(比如,程序在执行onCreate方法时,我就去执行setText方法区更新UI),就可以逃避掉checkThread()的检查。

    关于本题,给出以下链接大家去细读一下源码吧:

    Android更新Ui进阶精解(一):

    http://www.jianshu.com/p/6de0a42a44d6

    为什么我们可以在非UI线程中更新UI:

    http://blog.csdn.net/aigestudio/article/details/43449123

更多相关文章

  1. Android动态加载JAR包的实现方法
  2. JavaScript与Android原生相互调用并传参
  3. 使用ActionBar,调整Theme时遇到You need to use a Theme.AppCompa
  4. Android中3种全屏方法及3种去掉标题栏的方法
  5. Android(安卓)关于Bitmap对象于内存管理
  6. Android的线程详解(几种实现方法及区别)
  7. Android中TextView首行缩进处理办法
  8. 【腾讯Bugly干货分享】Android内存泄漏的简单检查与分析方法
  9. android log 分析(一)

随机推荐

  1. ImageView 缩放
  2. ubuntu10.04系统android开发环境配置
  3. 介绍本人的一个Android项目(附源代码)
  4. android快递跟踪进度条
  5. android中的数据库操作
  6. SDK下载地址
  7. Android(安卓)异步从网络上下载图片
  8. Android(安卓)SDK更新失败及其解决办法
  9. Android(安卓)创建与解析XML(六)—— 比较
  10. android页面跳转