Android 带你彻底理解跨线程通信之Handler机制

声明:原创文章,转载请注明出处https://www.jianshu.com/p/48976893bd11

我们知道在App中一般多会有多个线程,多线程之间难免需要进行通信。在我们平时开发中线程通信用的最多的就是Handler,例如子线程进行数据处理,在主线程中进行UI更新。当然了除了Handler这种通信方式外,线程间的通信还有其他几种方式:管道Pip、共享内存、通过文件及数据库等。这里由于篇幅有限,我们主要来看下Handler以及其实现原理。

相信做过Android的朋友对Handler一定不陌生,我们先来回顾下Handler的用法:

public class MainActivity extends AppCompatActivity {
    private Handler mHanlde;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHanlde = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if (msg.obj instanceof String){
                    Log.d("主线程接受到的消息:",(String)msg.obj);
                }
            }
        };
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = new Message();
                message.obj = "这是从子线程发送的消息";
                mHanlde.sendMessage(message);
            }
        }).start();
    }
}

可以看到我们在主线程中建了一个Handler,又创建了一个子线程,并在子线程中利用主线程创建的Handler发送了一条消息。运行起来看下打印日志:

06-12 16:08:31.618 25807-25807/simplae.handledemo D/主线程接受到的消息:: 这是从子线程发送的消息

可以看到子线程中的消息被顺利发送到了主线程。

接下来我们来尝试下将主线程中的消息发送到子线程中去,注意Handler都需要创建在接受消息的线程,这一点在下面源码分析的地方会提到:

public class MainActivity extends AppCompatActivity {
    private Handler mHanlde;
    private Handler subThreadHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                subThreadHandler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.obj instanceof String) {
                            Log.d("子线程接受到的消息:",(String)msg.obj);
                        }
                    }
                };
            }
        }).start();
        SystemClock.sleep(1000);
        Message message = new Message();
        message.obj = "这是从主线程发送的消息";
        subThreadHandler.sendMessage(message);
    }
}

这里我在发送消息前延迟了1秒,主要是防止发送消息时这个子线程还没初始化好,导致拿到的handler为空,这里是为了演示方面,实际开发中切记不要在主线程中加这么多骚操作。运行下看下:

    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:203)
        at android.os.Handler.<init>(Handler.java:117)
        at simplae.handledemo.MainActivity$1$1.<init>(MainActivity.java:22)
        at simplae.handledemo.MainActivity$1.run(MainActivity.java:22)
        at java.lang.Thread.run(Thread.java:764)

我们发现程序抛异常了,说是在线程中如果不调用Looper.prepare()就无法创建Handler。
那么Looper对象是什么,是干嘛的呢?一般源码中在类的开头都会有对类的功能作用做一个简单的说明,我们打开Looper这个类,可以看到开头有这样一段注释:


/**
  * Class used to run a message loop for a thread.  Threads by default do
  * not have a message loop associated with them; to create one, call
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  *
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  *
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>
  */

可以看到这里第一句就对Looper的作用做了说明:

Class used to run a message loop for a thread. 

即为一个线程做消息循环的一个类,说白了Looper就是一个调度器,可以简单理解为负责消息的存取。然后第二句也很重要:

Threads by default do not have a message loop associated with them; 
to create one, call prepare in the thread that is to run the loop,
and then loop to have it process messages until the loop is stopped.

意思是线程默认是没有Looper的,可以在线程中调用prepare方法来创建,之后可以通过它来循环处理消息直到Looper退出,稍后会对其进行详细的分析。这样我们在创建Handler之前先添加这句话试下:

...
Looper.prepare();
subThreadHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        if (msg.obj instanceof String) {
            Log.d("子线程接受到的消息:",(String)msg.obj);
        }
    }
};
...

运行后,果然不报错了,不过子线程并未接受到主线程发送的消息。这里如果我们在子线程中创建Handler,创建完之后还需调用Looper.loop()方法,完整代码如下:

public class MainActivity extends AppCompatActivity {
    private Handler mHanlde;
    private Handler subThreadHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                subThreadHandler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.obj instanceof String) {
                            Log.d("子线程接受到的消息:",(String)msg.obj);
                        }
                    }
                };
                Looper.loop();
            }
        }).start();
        SystemClock.sleep(1000);
        Message message = new Message();
        message.obj = "这是从主线程发送的消息";
        subThreadHandler.sendMessage(message);
    }
}

这样我们就成功接受到消息了。可以看到我们在子线程中创建Handler问题还是很多的,相比主线程,子线程在创建Handler前后分别得调用Looper的prepare和loop方法,这到底是怎么回事?不要着急接下来我们就一起来看下它背后究竟隐藏着怎样不为人知的秘密。

注意分析源码的时候一定要按程序执行流程往下看但是也不能太扣细节,要从整体上来把控,而且一定要自己亲自去看代码,如果只是去网上看别人的源码分析文章,自己没有实践,那是很难看懂的。即使看懂了,过几天就没印象了。所以接下来我分析源码的时候,还是建议你自己在编辑器中跟着一起查看源代码,这里只是起到一个引导的作用。

好了言归正传,我们就从第一句Looper.prepare()开始吧

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

点开Looper.prepare()方法,我们发现里面就只有一句代码,调用了prepare(true)的重载方法,再点进去我们发现代码很简单主要是给ThreadLocal设置了一个Looper对象,这里出现了一个新的对象就是ThreadLocal,这个对象对于Handler可以说是非常重要了。所以要想接下去分析Handler还是先把这个理解下。

ThreadLocal说白了就是用来存放数据的,不过它有个与众不同的特点,就是它的存取是和当前线程关联的,换句话说,用ThreadLocal存放的数据是放在调用ThreadLocal的线程中的,其他线程是访问不到的。这样说可能还是有点抽象,我们不妨看下他的源码,你会豁然开朗,我们点开它的set方法:

    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方法是怎么获取这个 ThreadLocalMap对象:

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

额。。有没有一种被耍了的感觉,原来这个对象就是直接从当前线程中来的。到这里你大概就明白了,原来通过ThreadLocal的set方法保存的数据就是直接存放在当前线程中的threadLocals变量中,这个变量是一个ThreadLocalMap 对象,而ThreadLocalMap 对象具体是怎么保存数据的,可以点开map.set(this, value)
方法看下:

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

可以看到数据其实是以数组的形式保存的,具体的存储细节大家可以好好看看代码,这里就不过多讲解了。

知道了ThreadLocal如何存数据后,我们再来看下如何取数据,读取数据只用调用ThreadLocal.get()方法:

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

很简单,就是获取当前的线程的ThreadLocalMap对象,然后获取ThreadLocalMap中保存的数据。

到这里,我们可以对ThreadLocal下个结论了:通过ThreadLocal保存的数据,其实就是保存在当前线程中,其他线程是无法访问到的。当你在某个线程中调用ThreadLocal的get方法,获取的数据也就是该线程中的数据。

好了,对ThreadLocal有了一个大致的了解,我们再回过头来看下Looper.prepare()的源码:

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

可以看到通过ThreadLocal的set方法,在当前线程保存了一个Looper对象,我们进入Looper的构造方法看下:

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

Looper的构造方法中实例化了一个MessageQueue对象,这个MessageQueue对象其实就是一个队列,你可以看成是一根水管,我们的消息就像里面的水,只能从一端流入,从另一端流出,在水管里的消息都是等待处理的,而消息的流入和流出都是由Looper这个对象控制的。

这样Looper.prepare()我们简单归纳下,主要做如下几件事情:

  • 1.在当前线程保存了一个Looper对象
  • 2.在Looper对象中创建了一个消息队列

好了,对Looper.prepare()了解了后,我们再来看下下面的代码:

 subThreadHandler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.obj instanceof String) {
                            Log.d("子线程接受到的消息:",(String)msg.obj);
                        }
                    }
                };

主要就是对Handler对象的实例化,我们进去看下:

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> 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.myLooper();方法获取一个Looper对象,我们看下这个方法内部:

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

。。。就是通过ThreadLocal的get方法,来获取当前线程保存的Looper对象,也就是要获取前面通过Looper的Looper.prepare()方法保存的Looper对象。接下来就是判空,如果获取的Looper对象为空则直接抛出一个异常:

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

是不是很熟悉,就是我们文章开头在子线程中没有调用Looper.myLooper()就是直接创建Handler抛出的异常。
接下来通过 mQueue = mLooper.mQueue;来获取Looper中的消息队列,至于其他代码我们暂且放下,因为先理清主要流程,不必太拘泥于细节。

好了,创建完Handler实例,接下来我们来看下接下来的Looper.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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

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

在这个方法里可以看到,主要做了这么这么几件事,先通过myLooper()方法获取当前线程保存的Looper对象,然后获取该Looper对象中的消息队列MessageQueue ,之后会调用该消息队列的next()方法,调用这个方法线程就进入阻塞状态,一直判断这个消息队列中有无消息,有消息就取出来,并且执行如下语句来处理该消息,如果没有消息,则一直处于阻塞状态

   msg.target.dispatchMessage(msg);

这里有个疑问msg的target是什么,调用完 Looper.loop()之后,我们知道线程现在一直处于阻塞状态,如果消息队列中有消息就取出来处理。那么接下来就是等着消息进入到这个队列。很显然这肯定和消息的发送有关,我们赶紧来看下,消息是如何发送的,这个消息到底跑哪去了,我们点开Handler的sendMessage方法,结果发现最终都会执行这个方法:

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

注意这里的mQueue就是Handler实例化时从当前线程获取到的Looper中的消息队列,最终会调用enqueueMessage方法来使发送的消息进入这个消息队列。我们再进入到这个方法看下:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

第一句就解决了我们上面的疑惑,原来这个msg的target就是Handler本身,再接下来就是调用这个队列的
enqueueMessage方法来使消息入队:

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

这里其实就是队列的算法实现,这里由于篇幅有限就不深入分析具体的细节了。

这样消息队列中就有消息了,我们之前一直阻塞的线程就可以获取到消息来处理了,其处理的代码就是这句;

 msg.target.dispatchMessage(msg);

刚才也看到了这个target就是Handler本身,我们赶紧来看下这个Handler的dispatchMessage方法:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

可以看到里面做了一些非空判断,当msg.callback和mCallback 都为空的时候会执行handleMessage方法,这个方法进入看下:

    public void handleMessage(Message msg) {
    }

我们发现是空的,如果要处理需要Handler的子类实现,也就是我们上面新建Handler时覆写的handleMessage方法。可能有的朋友会疑惑这里的msg.callback和mCallback 什么时候不为空呢?

首先我们来看下msg.callback这种情况,说这种情况之前我们先来回顾下,还有哪些方式我们可以在子线程中更新UI,这里我们一般用的比较多的就是如下几个:

  • 1、调用主线程中Handler的post或postDelayed等方法:
mMainHandler.post(new Runnable() {
    @Override
    public void run() {
                    
    }
});
  • 2、直接调用Activity的runOnUiThread方法:
runOnUiThread(new Runnable() {
    @Override
    public void run() {

    }
});

我们先来看第一种,我们进入Handler的post方法:

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

原来还是发送一条消息,这里是通过getPostMessage方法把Runnable 封装成一条消息,我们进入看下:

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

相信已经不用做过多解释了,这个Message的callback就是一个Runnable 对象,这里Message的callback也就被赋值了,回到上面当msg.callback不为空的时候,会调用handleCallback(msg);方法,我们进入看下:

    private static void handleCallback(Message message) {
        message.callback.run();
    }

非常简单,就是调用通过post方法传进去的Runnable 对象的run方法。接下来我们看下runOnUiThread内部代码:

    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }

可以看到也是很简单,如果当前线程不是UI线程,就调用Handler的post方法把Runnable发送到UI线程,如果是主线程就直接调用Runnable 的run方法。

好了到这里我们简单分析了Message的callback不为空的情况,接下来我们看下Handler的mCallback什么时候不为空,这个Callback是一个接口:

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

里面定义了处理消息的方法,它在什么时候被赋值的呢,Handler除了空参数的构造方法外还有其他几个构造方法,这些构造方法允许你直接传入这个callback的实现,也就是对消息的处理方法,这样就不用重新再像文章开头那样覆写Handler的handleMessage方法。比如:

Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        return false;
    }
});

这里我们创建Hanlder时就传入了一个CallBack,直接在这里就可以处理消息。

好了到这里我们就随着流程,分析了下消息传递的原理,可能大家对流程还不是非常清晰,不过没关系,我把有关的对象画成一个图:


关系图

上面的图可以很直观的反应消息传递时有关对象之间的关系,可以看到,在需要接受消息的线程A中创建了一个Handler,这个Handler对象中有一个Looper对象,而这个Looper对象中有个消息队列,里面存了待处理的消息。然后在需要发消息的线程B中调用线程A中HanlderA的sendMessage方法,这样线程B中的消息就已经被传入线程A了。这里需要注意的是,每个线程只关联一个单例的Looper对象,这个Looper对象是通过Looper.prepare创建,如果没有调用这句就直接创建Handler就会初始化失败而报错,这样就实现了线程的通信,怎么样是不是很巧妙!

可能有些朋友还有疑惑,为什么在子线程创建Handler前后还要分别调用Looper.prepare()和Looper.loop(),而在主线程中却不用调用,其实并不是主线程不用调用,而且在主线程一启动就已经调用了,我们来看下主线程中的代码:


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

有两句代码很明显:

...
Looper.prepareMainLooper();
...
Looper.loop();
...

可以看到在主线程开始的时候,Looper就已经初始化好了,并且从Looper.loop()也可以看到,主线程在阻塞状态,等待处理消息。如果需要在主线程接受消息就只需要创建一个Handler就可以了。

到这里有关Handler的机制就分析的差不多了,如果看得还不是很懂,还是建议你自己在编辑器中阅读源代码,因为只有自己亲自看了,敲了才会印象深刻。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266