可以將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源碼中得知:
- 每一個(gè)Thread都有一個(gè)ThreadLocalMap類型的成員變量:threadLocals
- 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源碼中得知:
- ThreadLocal并不是一個(gè)Thread,只不過(guò)Thread使用了ThreadLocal中定義的ThreadLocalMap迫卢。
- 由createMap方法可知倚搬,ThreadLocal負(fù)責(zé)創(chuàng)建當(dāng)前線程對(duì)應(yīng)Thread對(duì)象的ThreadLocalMap
- getMap方法獲取的是當(dāng)前線程對(duì)應(yīng)Thread對(duì)象的threadLocals屬性
- 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源碼中得知:
- Looper的構(gòu)造函數(shù)私有化,不能通過(guò)new創(chuàng)建Looper對(duì)象金刁,需要使用
Looper.prepare()
來(lái)創(chuàng)建Looper對(duì)象 - 從
sThreadLocal.set(new Looper(quitAllowed));
得知可以在當(dāng)前線程的其他地方使用sThreadLocal.get()
獲取Looper對(duì)象 -
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源碼可知:
- 我們?cè)谑褂肏andler的時(shí)候媳友,一般是new一個(gè)Handler,然后重寫(xiě)
handleMessage
方法产捞,該方法是由dispatchMessage
來(lái)調(diào)用醇锚,而dispatchMessage
是在Looper對(duì)象的loop()
方法中的死循環(huán)中執(zhí)行。 -
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源碼可知:
- 成員變量target是一個(gè)Handler對(duì)象
- 可以通過(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源碼可知:
-
makeText()
會(huì)調(diào)用Toast的構(gòu)造函數(shù)紧阔,構(gòu)造函數(shù)中調(diào)用getLooper()
坊罢,此時(shí)looper為空,會(huì)執(zhí)行checkNotNull(Looper.myLooper(), "...")
方法擅耽,通過(guò)方法名可以直接得出該方法用于判空活孩,為空則拋出異常。 - 從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ū)別:
- 同步(sync)就是一個(gè)一個(gè)的來(lái),處理完一個(gè)再處理下一個(gè)边琉;異步(async)就是可以同時(shí)處理多個(gè)属百,不需要等待。
- 同步Message艺骂,就是按照順序執(zhí)行的Message诸老,默認(rèn)情況下,我們通過(guò)Handler對(duì)象的sendMessage()方法發(fā)送的Message就是同步Message钳恕。
- 異步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的源碼可知:
-
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
-
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);
-
unscheduleTraversals()
中調(diào)用了MessageQueue對(duì)象的removeSyncBarrier()
方法锉桑,移除了同步屏障 -
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é):
- 要使用異步Message,需要發(fā)送兩個(gè)Message。一個(gè)Message的
target=null
较店,用于告訴MessageQueue士八,當(dāng)前隊(duì)列中有異步消息;另一個(gè)Message才是真正的消息載體梁呈,并且要設(shè)置setAsynchronous(true)
婚度,用于標(biāo)記Message的異步屬性。 - 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()
的原因
- 從Message的源碼可知,Message有一個(gè)Handler類型的屬性target秽荞,說(shuō)明Message持有一個(gè)Handler
- 從上述結(jié)論可知Handler持有MainActivity對(duì)象
- 如果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)部如何保證線程安全乖订?
答:
- 從Looper的源碼可知扮饶,MessageQueue對(duì)象在Looper的構(gòu)造函數(shù)中創(chuàng)建。即每一個(gè)線程對(duì)應(yīng)一個(gè)MessageQueue對(duì)象
- 從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)卡死眠饮?
答:
- ActivityThread是應(yīng)用程序的入口元潘,該類中的
main
函數(shù)是整個(gè)Java程序的入口,main
函數(shù)的主要作用就是做消息循環(huán)君仆,一旦main
函數(shù)執(zhí)行完畢,循環(huán)結(jié)束牲距,那么應(yīng)用也就可以退出了返咱。 - 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ì)等待萤晴。 -
loop()
的阻塞,是指MessageQueue中沒(méi)有Message胁后,此時(shí)釋放CPU執(zhí)行權(quán)店读,等待喚醒。 - 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趴久。