Android Handler詳解(附面試題)

Handler模型.png

可以將Handler模型理解為:生產(chǎn)者—消費(fèi)者 模型。
該模型中蒂誉,生產(chǎn)者在子線程中生產(chǎn)Message,調(diào)用Handler對(duì)象的sendMessage()等方法冠骄,將Message加入到MessageQueue中攒读;Looper.loop()死循環(huán)從MessageQueue中取出Message,然后調(diào)用Handler對(duì)象的handleMessage()方法在主線程中消耗掉Message寨腔。

1.源碼分析

想要弄清楚Handler速侈,得先理解Thread和ThreadLocal

1.1 Thread和ThreadLocal

public class Thread implements Runnable {
    ...
    ThreadLocalMap threadLocals = null;
    ...
}

從Thread源碼中得知:

  1. 每一個(gè)Thread都有一個(gè)ThreadLocalMap類型的成員變量:threadLocals
  2. ThreadLocalMap定義在ThreadLocal中
public class ThreadLocal<T> {

    public ThreadLocal() {
    }
    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();
    }
    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;
    }
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    static class ThreadLocalMap {
        ...
    }
}

從ThreadLocal源碼中得知:

  1. ThreadLocal并不是一個(gè)Thread,只不過(guò)Thread使用了ThreadLocal中定義的ThreadLocalMap迫卢。
  2. 由createMap方法可知倚搬,ThreadLocal負(fù)責(zé)創(chuàng)建當(dāng)前線程對(duì)應(yīng)Thread對(duì)象的ThreadLocalMap
  3. getMap方法獲取的是當(dāng)前線程對(duì)應(yīng)Thread對(duì)象的threadLocals屬性
  4. set方法是將某一個(gè)對(duì)象添加到當(dāng)前線程對(duì)應(yīng)Thread對(duì)象的threadLocals中,get方法同理乾蛤。

測(cè)試ThreadLocal:

public class MyTest {
    /**
     * 多個(gè)線程可以共用一個(gè)ThreadLocal
     */
    private static ThreadLocal<Person> mThreadLocal = new ThreadLocal<>();
    public static void main(String[] args) {
        new MyThread("張三").start();
        new MyThread("李四").start();
    }

    static class MyThread extends Thread {
        private Person mPerson;
        public MyThread(String name) {
            mPerson = new Person(name);
        }
        @Override
        public void run() {
            super.run();
            // ThreadLocal的set每界、get方法操作的是當(dāng)前線程對(duì)應(yīng)Thread對(duì)象的threadLocals屬性
            mThreadLocal.set(mPerson);
            Person person = mThreadLocal.get();
            System.out.println(Thread.currentThread() + "----" + person.getName());
        }
    }

    static class Person {
        private String mName;
        public Person(String name) {
            mName = name;
        }
        public String getName() {
            return mName;
        }
        public void setName(String name) {
            this.mName = name;
        }
    }
}

測(cè)試結(jié)果:

Thread[Thread-0,5,main]----張三
Thread[Thread-1,5,main]----李四

ThreadLocal總結(jié):
ThreadLocal提供了線程局部變量,每個(gè)線程都可以通過(guò)set和get方法來(lái)對(duì)這個(gè)局部變量進(jìn)行操作家卖,且不會(huì)和其他線程的局部變量沖突眨层,實(shí)現(xiàn)了線程的數(shù)據(jù)隔離。

1.2 Looper上荡、MessageQueue趴樱、Message、Handler

Looper和Handler都持有MessageQueue的引用酪捡,那么是誰(shuí)創(chuàng)建的MessageQueue叁征?
查看Looper的源碼,從Looper的構(gòu)造函數(shù)可知沛善,MessageQueue是由Looper創(chuàng)建的航揉。

public final class Lopper {
    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    @UnsupportedAppUsage
    final MessageQueue mQueue;
    final Thread mThread;
    
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    
    public static void prepare() {
        prepare(true);
    }

    @Deprecated
    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));
    }
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    public static void loop() {
        final Looper me = myLooper();
        ...
        me.mInLoop = true;
        final MessageQueue queue = me.mQueue;
        ...
        for (;;) {
            ...
            Message msg = queue.next(); // might block
            ...
            try {
                msg.target.dispatchMessage(msg);
            }
        }
    }
    public void quit() {
        mQueue.quit(false);
    }
}

從Looper源碼中得知:

  1. Looper的構(gòu)造函數(shù)私有化,不能通過(guò)new創(chuàng)建Looper對(duì)象金刁,需要使用Looper.prepare()來(lái)創(chuàng)建Looper對(duì)象
  2. sThreadLocal.set(new Looper(quitAllowed));得知可以在當(dāng)前線程的其他地方使用sThreadLocal.get()獲取Looper對(duì)象
  3. loop()方法里面寫(xiě)了一個(gè)死循環(huán)帅涂,不斷的調(diào)用MessageQueue的next()方法獲取Message對(duì)象,并調(diào)用Handler的dispatchMessage(msg)方法(msg.target指的就是Handler對(duì)象)
public class Handler {
    // Handler的構(gòu)造函數(shù)有很多個(gè)尤蛮,這里只展示其中的兩個(gè)
    
    public Handler(@Nullable Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
    // Handler的子類必須實(shí)現(xiàn)handleMessage方法
    public void handleMessage(@NonNull Message msg) {
    }
    
    public void dispatchMessage(@NonNull Message msg) {
        ...
        handleMessage(msg);
        ...
    }
    
    /**
     * sendMessage/sendEmptyMessage/sendEmptyMessageDelayed/sendMessageDelayed/...
     * 如上所有的發(fā)送消息方法最終都執(zhí)行的是sendMessageAtTime
     */
    public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

}

從Handler源碼可知:

  1. 我們?cè)谑褂肏andler的時(shí)候媳友,一般是new一個(gè)Handler,然后重寫(xiě)handleMessage方法产捞,該方法是由dispatchMessage來(lái)調(diào)用醇锚,而dispatchMessage是在Looper對(duì)象的loop()方法中的死循環(huán)中執(zhí)行。
  2. sendMessage、sendEmptyMessage焊唬、sendMessageDelayed等發(fā)送消息的方法恋昼,實(shí)際上調(diào)用的是MessageQueue對(duì)象的enqueueMessage(msg, uptimeMillis)方法,將Message對(duì)象放入MessageQueue中
public final class MessageQueue { {
    // True if the message queue can be quit.
    @UnsupportedAppUsage
    private final boolean mQuitAllowed;
    
    @UnsupportedAppUsage
    Message mMessages;
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
    
    @UnsupportedAppUsage
    Message next() {
        // Message是一個(gè)單鏈表結(jié)構(gòu)赶促,有一個(gè)屬性next指向了后一個(gè)Message
    }
    
    boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {
            // 時(shí)間參數(shù)when表示Message的優(yōu)先級(jí)
            // 1.根據(jù)時(shí)間參數(shù)when來(lái)判斷當(dāng)前Message(假設(shè)為curr_msg)要插入到哪一個(gè)Message(假設(shè)為pre_msg)的后面
            // 2.如果pre_msg的next不為空液肌,則用一個(gè)臨時(shí)變量記錄:temp_msg = pre_msg.next
            // 3.將pre_msg.next = curr_msg,然后將curr_msg.next = temp_msg
        }
    }
    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        ...
    }
}

從MessageQueue的源碼可知:
mQuitAllowed表示MessageQueue是否可以銷毀鸥滨。
我們?cè)趧?chuàng)建Looper時(shí)嗦哆,一般調(diào)用的是Looper.prepare(),該方法最終調(diào)用的是Looper的構(gòu)造方法婿滓,會(huì)將mQuitAllowed設(shè)置為true老速,表示可以銷毀。

public final class ActivityThread extends ClientTransactionHandler {
    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        ...
        Looper.loop();
        ...
    }
}

從ActivityThread源碼可知:
啟動(dòng)一個(gè)app時(shí)凸主,會(huì)在app的主線程創(chuàng)建一個(gè)Looper橘券,而此時(shí)調(diào)用的是Looper.prepareMainLooper(),會(huì)將mQuitAllowed設(shè)置為false秕铛,表示不可以銷毀约郁。

public final class Message implements Parcelable {
    @UnsupportedAppUsage
    /*package*/ Handler target;
    
    @UnsupportedAppUsage
    /*package*/ Message next;

    /** @hide */
    public static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    
    public void setTarget(Handler target) {
        this.target = target;
    }
    
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    public static Message obtain(Message orig) {
        Message m = obtain();
        m.what = orig.what;
        m.arg1 = orig.arg1;
        m.arg2 = orig.arg2;
        m.obj = orig.obj;
        m.replyTo = orig.replyTo;
        m.sendingUid = orig.sendingUid;
        m.workSourceUid = orig.workSourceUid;
        if (orig.data != null) {
            m.data = new Bundle(orig.data);
        }
        m.target = orig.target;
        m.callback = orig.callback;

        return m;
    }
}

從Message源碼可知:

  1. 成員變量target是一個(gè)Handler對(duì)象
  2. 可以通過(guò)Message.obtain()來(lái)創(chuàng)建Message對(duì)象,這種方式可以復(fù)用已經(jīng)創(chuàng)建過(guò)的Message但两,從而避免頻繁的創(chuàng)建鬓梅、銷毀Message,達(dá)到優(yōu)化內(nèi)存和性能的目的谨湘。

1.3 Toast

不知道大家有沒(méi)有在子線程中使用過(guò)Toast绽快,如果有,那么你可能碰到過(guò)在子線程中直接調(diào)用Toast的makeText()方法會(huì)報(bào)錯(cuò)Can't toast on a thread that has not called Looper.prepare()

public class Toast {
    
    public Toast(Context context) {
        this(context, null);
    }

    public Toast(@NonNull Context context, @Nullable Looper looper) {
        ...
        looper = getLooper(looper);
        ...
    }
    
    private Looper getLooper(@Nullable Looper looper) {
        if (looper != null) {
            return looper;
        }
        return checkNotNull(Looper.myLooper(),
                "Can't toast on a thread that has not called Looper.prepare()");
    }
    public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
        return makeText(context, null, text, duration);
    }

    public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
            @NonNull CharSequence text, @Duration int duration) {
        if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
            Toast result = new Toast(context, looper);
            result.mText = text;
            result.mDuration = duration;
            return result;
        } else {
            Toast result = new Toast(context, looper);
            View v = ToastPresenter.getTextToastView(context, text);
            result.mNextView = v;
            result.mDuration = duration;

            return result;
        }
    }
}

從Toast源碼可知:

  1. makeText()會(huì)調(diào)用Toast的構(gòu)造函數(shù)紧阔,構(gòu)造函數(shù)中調(diào)用getLooper()坊罢,此時(shí)looper為空,會(huì)執(zhí)行checkNotNull(Looper.myLooper(), "...")方法擅耽,通過(guò)方法名可以直接得出該方法用于判空活孩,為空則拋出異常。
  2. 從Looper的源碼可知乖仇,Looper.myLooper()獲取的是當(dāng)前線程的Looper對(duì)象憾儒,而此時(shí)子線程的Looper對(duì)象為空,導(dǎo)致了異常乃沙。

子線程中使用Toast的方法:
第一步起趾,調(diào)用Looper.prepare()給子線程創(chuàng)建一個(gè)Looper對(duì)象。
第二步警儒,調(diào)用Toast.makeText(...).show()向子線程的MessageQueue插入Message
第三步训裆,調(diào)用Looper.loop(),從MessageQueue中取出Message

1.4 ThreadHandler


1.5 同步屏障

同步Message和異步Message的區(qū)別:

  1. 同步(sync)就是一個(gè)一個(gè)的來(lái),處理完一個(gè)再處理下一個(gè)边琉;異步(async)就是可以同時(shí)處理多個(gè)属百,不需要等待。
  2. 同步Message艺骂,就是按照順序執(zhí)行的Message诸老,默認(rèn)情況下,我們通過(guò)Handler對(duì)象的sendMessage()方法發(fā)送的Message就是同步Message钳恕。
  3. 異步Message,類似于屏幕點(diǎn)擊事件的Message蹄衷,就是異步Message忧额。

什么是同步屏障?
字面意思理解愧口,就是設(shè)置了一道屏障睦番,讓同步Message不可以通過(guò),從而優(yōu)先處理異步Message

那么同步屏障如何設(shè)置耍属?我們來(lái)看ViewRootImpl的源碼:

public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    ...
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

    void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            mChoreographer.removeCallbacks(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }
    ...
}

從ViewRootImpl的源碼可知:

  1. scheduleTraversals()中調(diào)用了MessageQueue對(duì)象的postSyncBarrier()方法托嚣,設(shè)置了同步屏障,設(shè)置同步屏障的代碼如下:
public final class MessageQueue {
    @UnsupportedAppUsage
    @TestApi
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }
}

如上代碼可知厚骗,設(shè)置同步屏障就是在MessageQueue隊(duì)列中新增了一個(gè)Message對(duì)象示启,并將該Message對(duì)象插入到隊(duì)列的最前面。當(dāng)我們調(diào)用Handler對(duì)象的sendMessage()方法時(shí)领舰,會(huì)將Message對(duì)象的target屬性設(shè)置為Handler對(duì)象夫嗓,而此處postSyncBarrier()方法沒(méi)有設(shè)置Message對(duì)象的target,說(shuō)明target=null

  1. scheduleTraversals()中調(diào)用了mChoreographer.postCallback()冲秽,追蹤源碼可知調(diào)用的是Handler對(duì)象的sendMessageAtTime()發(fā)送Message舍咖,但是在發(fā)送Message之前執(zhí)行了調(diào)用了Message對(duì)象的setAsynchronous(true),將Message設(shè)置為了異步消息
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
  1. unscheduleTraversals()中調(diào)用了MessageQueue對(duì)象的removeSyncBarrier()方法锉桑,移除了同步屏障
  2. unscheduleTraversals()中調(diào)用了mChoreographer.removeCallbacks()排霉,追蹤源碼可知調(diào)用的是Handler對(duì)象的removeMessages(),就是將Message從MessageQueue中移除民轴。

再來(lái)看MessageQueue的next()方法:

    @UnsupportedAppUsage
    Message next() {
        ...
            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());
                }
            }
        ...
    }

next()方法中的if語(yǔ)句中攻柠,判斷了Message對(duì)象的target屬性是否為空。如果為空杉武,則說(shuō)明MessageQueue隊(duì)列中有需要優(yōu)先處理的異步Message辙诞,此時(shí)執(zhí)行do-while循環(huán),去查找隊(duì)列中的異步Message轻抱,找到異步Message之后返回給Looper飞涂。判斷一個(gè)Message是否為異步Message:msg.isAsynchronous()

同步屏障總結(jié):

  1. 要使用異步Message,需要發(fā)送兩個(gè)Message。一個(gè)Message的target=null较店,用于告訴MessageQueue士八,當(dāng)前隊(duì)列中有異步消息;另一個(gè)Message才是真正的消息載體梁呈,并且要設(shè)置setAsynchronous(true)婚度,用于標(biāo)記Message的異步屬性。
  2. Looper的死循環(huán)中官卡,會(huì)調(diào)用MessageQueue的next()方法從消息隊(duì)列中獲取消息蝗茁,而 MessageQueue的next()方法會(huì)優(yōu)先返回異步消息。

2.Handler常見(jiàn)面試題

2.1 一個(gè)線程有幾個(gè)Handler寻咒?

答:一個(gè)線程可以有多個(gè)Handler哮翘,需要時(shí),通過(guò)new Handler()的方式創(chuàng)建Handler

2.2 一個(gè)線程有幾個(gè)Looper毛秘,如何保證饭寺?

答:一個(gè)線程只有一個(gè)Looper,從Looper的源碼中可知叫挟,Looper的構(gòu)造私有化了艰匙,需要通過(guò)Looper.prepare()創(chuàng)建Looper,創(chuàng)建的Looper會(huì)使用ThreadLocal存起來(lái)抹恳,再次調(diào)用Looper.prepare()會(huì)檢查T(mén)hreadLocal中是否有Looper员凝,如果有則拋出異常。

2.3 Handler內(nèi)存泄漏的原因适秩?

答:假設(shè)在Activity中使用匿名內(nèi)部類的方式創(chuàng)建了一個(gè)Handler:

public class MainActivity extends Activity {

    private TextView mTextView;

    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 等價(jià)于:MainActivity.this.mTextView.setText("hello")
            mTextView.setText("hello");
            if (msg.what == 100) {
                // do something...
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new Thread(){
            @Override
            public void run() {
                super.run();
                Message message = new Message();
                message.what = 100;
                // 延遲一小時(shí)發(fā)送
                mHandler.sendMessageDelayed(message, 1000 * 60 * 60);
            }
        }.start();
    }
}

結(jié)論:匿名內(nèi)部類默認(rèn)會(huì)持有外部類對(duì)象的引用绊序,這也是為什么能直接調(diào)用mTextView.setTex()的原因

  1. 從Message的源碼可知,Message有一個(gè)Handler類型的屬性target秽荞,說(shuō)明Message持有一個(gè)Handler
  2. 從上述結(jié)論可知Handler持有MainActivity對(duì)象
  3. 如果Message得不到釋放骤公,則Handler也得不到釋放,那么MainActivity也得不到釋放

上述代碼中扬跋,將Message設(shè)置為延遲一小時(shí)后再發(fā)送阶捆,如果一小時(shí)內(nèi)需要finish掉MainActivity跳轉(zhuǎn)到其他Activity,則此時(shí)會(huì)發(fā)生內(nèi)存泄漏钦听,因?yàn)镚C無(wú)法回收MainActivity洒试。
可以使用靜態(tài)內(nèi)部類 + 弱引用的方式解決:

public class MainActivity extends Activity {

    private TextView mTextView;

    Handler mHandler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new MyHandler(this);
        new Thread(){
            @Override
            public void run() {
                super.run();
                Message message = new Message();
                message.what = 100;
                // 延遲一小時(shí)發(fā)送
                mHandler.sendMessageDelayed(message, 1000 * 60 * 60);
            }
        }.start();
    }

    private static class MyHandler extends Handler {

        private WeakReference<MainActivity> mMainReference;
        public MyHandler(MainActivity mainActivity) {
            mMainReference = new WeakReference<MainActivity>(mainActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity mainActivity = mMainReference.get();
            mainActivity.mTextView.setText("hello");
            if (msg.what == 100) {
                // do something...
            }
        }
    }
}

2.4 為何主線程中可以new Handler,在子線程中new Handler要做什么準(zhǔn)備朴上?

答:先調(diào)用Looper.prepare()垒棋,再調(diào)用Looper.loop()

2.5 子線程中維護(hù)的Looper,當(dāng)MessageQueue中無(wú)消息時(shí)痪宰,該如何處理叼架?

答:調(diào)用Looper對(duì)象的quit()方法畔裕,最終調(diào)用的是MessageQueue的quit()方法

2.6 不同線程的Handler往MessageQueue中添加Message,Handler內(nèi)部如何保證線程安全乖订?

答:

  1. 從Looper的源碼可知扮饶,MessageQueue對(duì)象在Looper的構(gòu)造函數(shù)中創(chuàng)建。即每一個(gè)線程對(duì)應(yīng)一個(gè)MessageQueue對(duì)象
  2. 從MessageQueue的enqueueMessage(...)方法可知乍构,該方法中的synchronized(this)持有的是this對(duì)象鎖甜无,當(dāng)前MessageQueue對(duì)象其他使用synchronized(this)的地方都會(huì)等待

以上兩點(diǎn),保證了線程安全哥遮。

2.7 使用Message時(shí)應(yīng)該如何創(chuàng)建它岂丘?

答:從Message源碼可知,應(yīng)該使用Message.obtain()來(lái)創(chuàng)建Message對(duì)象

2.8 為什么主線程不會(huì)因?yàn)?Looper.loop() 里的死循環(huán)卡死眠饮?

答:

  1. ActivityThread是應(yīng)用程序的入口元潘,該類中的main函數(shù)是整個(gè)Java程序的入口,main函數(shù)的主要作用就是做消息循環(huán)君仆,一旦main函數(shù)執(zhí)行完畢,循環(huán)結(jié)束牲距,那么應(yīng)用也就可以退出了返咱。
  2. ActivityThread的main函數(shù)中調(diào)用了Looper.loop()開(kāi)啟了一個(gè)死循環(huán),loop()方法中調(diào)用了MessageQueue的next()方法牍鞠,該方法是一個(gè)阻塞方法(這里涉及到Linux中的pipe咖摹,不做贅述),如果MessageQueue中沒(méi)有Message难述,則會(huì)等待萤晴。
  3. loop()的阻塞,是指MessageQueue中沒(méi)有Message胁后,此時(shí)釋放CPU執(zhí)行權(quán)店读,等待喚醒。
  4. ANR:Application Not Responding攀芯。導(dǎo)致ANR的情況比如主線程中訪問(wèn)網(wǎng)絡(luò)屯断、操作數(shù)據(jù)庫(kù)等耗時(shí)操作,此時(shí)也會(huì)往MessageQueue中發(fā)送Message侣诺,然后loop()循環(huán)中獲取到了Message殖演,但是在處理Message時(shí)耗時(shí)過(guò)長(zhǎng),導(dǎo)致短時(shí)間內(nèi)無(wú)法響應(yīng)屏幕點(diǎn)擊事件等操作(屏幕被點(diǎn)擊也會(huì)發(fā)送Message到MessageQueue中)年鸳,然后就出現(xiàn)了ANR趴久。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市搔确,隨后出現(xiàn)的幾起案子彼棍,更是在濱河造成了極大的恐慌灭忠,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滥酥,死亡現(xiàn)場(chǎng)離奇詭異更舞,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)坎吻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)缆蝉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人瘦真,你說(shuō)我怎么就攤上這事刊头。” “怎么了诸尽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵原杂,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我您机,道長(zhǎng)穿肄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任际看,我火速辦了婚禮咸产,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘仲闽。我一直安慰自己脑溢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布赖欣。 她就那樣靜靜地躺著屑彻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪顶吮。 梳的紋絲不亂的頭發(fā)上社牲,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天,我揣著相機(jī)與錄音云矫,去河邊找鬼膳沽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛让禀,可吹牛的內(nèi)容都是我干的挑社。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼巡揍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼痛阻!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起腮敌,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤阱当,失蹤者是張志新(化名)和其女友劉穎俏扩,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體弊添,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡录淡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了油坝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫉戚。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖澈圈,靈堂內(nèi)的尸體忽然破棺而出彬檀,到底是詐尸還是另有隱情,我是刑警寧澤瞬女,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布窍帝,位于F島的核電站,受9級(jí)特大地震影響诽偷,放射性物質(zhì)發(fā)生泄漏坤学。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一报慕、第九天 我趴在偏房一處隱蔽的房頂上張望拥峦。 院中可真熱鬧,春花似錦卖子、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至突梦,卻和暖如春诫舅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宫患。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工刊懈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人娃闲。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓虚汛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親皇帮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子卷哩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容