美文网首页
Handler,Looper,Thread,Message,Me

Handler,Looper,Thread,Message,Me

作者: jackzhang1990 | 来源:发表于2017-05-18 18:06 被阅读17次

    【1】Looper如何和Thread联系起来?

    答:以主线程为例解释:

    在ActivityThread类中的程序的入口,即main方法,该方法中调用了:
    Looper.prepareMainLooper();
    Step:接下来我们解析Looper类中的该方法:
    public static void prepareMainLooper() {
    prepare(false); synchronized (Looper.class) {
    if (sMainLooper != null) {
    throw new IllegalStateException("The main Looper has already been prepared."); }
    sMainLooper = myLooper(); }
    }
    官方对该方法的解释:

    (1)将当前线程初始化为looper,将其标记为应用程序的主looper。
    
    (2)应用程序的主looper是由Android环境创建的,所以你应该永远不要自己调用该方法。
    

    Step:接下来我们解析:
    prepare(false);
    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));}
    官方对该方法的解释:

    (1)该方法让你有机会创建handlers,然后引用这个looper,然后才开始真正循环(loop)。
    (2)调用此方法后一定要调用loop(),通过调用quit()来结束它。

    Step:接下来我们解析:sThreadLocal.set(new Looper(quitAllowed));

    (1)首先我们关注创建Looper对象:Looper looper = new Looper(quitAllowed)
    private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}
    这里主要是:
    (1)创建了MessageQueue的对象mQueue;
    (2)创建了Thread的对象mThread;

    Thread.currentThread()方法可以获取到当前的线程,当应用程序启动的时候,系统会为该应用程序创建一个线程,我们叫它主线程。

    (2)其次我们关注Looper对象的存储:sThreadLocal.set(looper)

    我们看看sThreadLocal是什么东东?
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    官方对ThreadLocal的解释:

    用来实现线程本地存储,即每个线程的变量有自己对应的值。
    所有的线程共享相同的ThreadLocal对象,但是每个线程访问它时都会看到不同的值,并且有一个线程更改不会影响其他线程。

    Step:接下来我们解析:set(looper)
    public void set(T value) {
    Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) {
    values = initializeValues(currentThread); }
    values.put(this, value);}
    官方对该方法的解释:

    为当前线程设置此变量的值。

    这里不对ThreadLocal类进行深入的了解,到这里我们知道使用该类可以实现存储Thread和Looper就可以了,类似于(key-value)。

    因为Thread默认没有与它关联的消息循环,(Thread默认不能进行消息循环)
    要创建一个,我们在运行循环的线程中调用prepare(),
    然后调用loop()方法让他处理消息,
    最后调用quit()方法推出循环

    Looper最重要的方法以及顺序:prepare() -> loop() ->quit()

    实际开发中的使用:参考HandlerThread

    【1】extends Thread
    【2】Looper.prepare() //将Thread初始化为looper
    【3】创建一个或者多个Handler,用来处理message
    【4】Looper.loop() //分发message,直到loop被告知quit()
    【2】Handler如何和Thread联系起来?

    主要从分析Handler源码来解析:

    Step:先看默认构造函数:
    public Handler() {
    this(null, false);}
    官方对该构造函数的解释:
    默认构造函数将该Handler与当前线程的Looper关联起来。如果此线程没有looper,该Handler将无法接收Message,因此会抛出异常。

    Step:再看看另一个构造函数:
    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;}
    

    官方对该构造函数的解释:
    对于具有指定的回调接口的当前Thread使用Looper,并设置该Handler是否应该是异步的。
    Handler默认情况下是同步的,除非此构造函数用于创建严格异步的。

    mLooper:在Looper中通过prepare()方法创建的,这样Handler就和Looper联系起来了,【同时和Thread联系起来。】
    mQueue:Handler中的MessageQueue和Looper中的MessageQueue是一致的。

    Step:接着看Handler的两个主要用途:
    【1】调度message 和 runnable ,在未来的某个点执行。
    【2】在与自己不同的线程上执行某个事件。

    调度消息可以通过如下方法完成,大致有两类型:post 和 send
    post方式允许在接收到消息时将Runnable对象入队;
    send方式允许在接收到消息时将Message对象入队;

    Step:关于post方式做如下说明:
    public final boolean post(Runnable r) {
    return sendMessageDelayed(getPostMessage(r), 0);}
    官方解释:
    将Runnable添加到MessageQueue.Runnable将在Handler所在的Thread中运行。

    Step:再看看getPostMessage(r):
    private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain(); m.callback = r; return m;}
    将Runnable用Message包裹起来,以Message的形式发送出去。

    记住:无论是post方式,还是send方式,最终都只调用一个方法:
    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);}
    官方解释:
    将Message置入MessageQueue中。(这里时间先不做解释了)

    Step:再看看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 = this;这是非常重要的,因为Looper中的loop() 方法会用到它,即:msg.target.dispatchMessage(msg);
    目的是要求发送Message的Handler和处理Message的Handler是一致的。

    Step:接下来重点就是MessageQueue的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;}
    

    至此,Message已经放入MessageQueue中了;

    Step:接下来就是从MessageQueue中取Message了,这时候就需要发挥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 traceTag = me.mTraceTag;        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));        }
        try {
            msg.target.dispatchMessage(msg);        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);            }
        }
    
        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();    }
    

    }
    这里重点看:msg.target.dispatchMessage(msg)
    public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
    handleCallback(msg); } else {
    if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
    return; }
    }
    handleMessage(msg); }
    }
    这里我们看到它分情况处理了,msg.callback是否为null。

    如果msg.callback不为null,说明传递过来的是Runnable,然后进行handleCallback(msg)
    private static void handleCallback(Message message) {
    message.callback.run();}
    如果msg.callback为null,说明传递过来的是Message,然后进行handleMessage(msg)
    public void handleMessage(Message msg) {
    }
    我们需要在run()方法和handleMessage()方法中写自己的实现。

    【3】通过对【1】和【2】的分析,我发现:

    我们主要了解Thread,Looper,Handler它们三个之间的关系就可以了,Message和MessageQueue可以把它们当作媒介就可以。

    出场顺序是这样的:Thread -> Looper -> Handler

    Thread创建后,就需要对Looper进行操作,主要是执行两个方法:prepare(),loop(), 这是准备工作。
    然后就是对Handler的使用,创建Handler对象,并将其和Looper联系起来,并和Thread联系起来,
    Handler就可以发挥它强大的功能。

    【4】思考:
    UI线程是如何创建的?
    应用启动时,系统会为应用创建一个名为主线程的执行线程。主线程负责将事件分派给相应的用户界面小部件,其中包括绘图事件。
    此外,UI线程也是应用与Android UI工具包组件进行交互的线程。

    UI线程才能处理UI相关的操作,为什么?
    答:Android UI工具包不是线程安全的。因此,单线程模型确保UI不能被不同的线程同时修改。

    Android关于线程的详细说明:https://developer.android.com/guide/components/processes-and-threads.html

    【5】Handler的工作原理:

    Handler创建时会采用当前线程的Looper来构建内部的消息循环系统。接下来看Handler的运行机制

    【6】ThreadLocal:

    在不同的线程中访问的是同一个ThreadLocal对象,但是他们通过ThreadLocal获取到的值是不同的。

    why? ->不同线程访问同一个ThreadLocal的get()方法,ThreadLocal内部会从各自的线程中取出一个数组,

    相关文章

      网友评论

          本文标题:Handler,Looper,Thread,Message,Me

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