您好,登錄后才能下訂單哦!
有些同學(xué)老問(wèn)我,快要校招了 今年的金三銀四退成了金四銀五了,我改準(zhǔn)備哪些知識(shí)點(diǎn)去面試復(fù)習(xí),然而要準(zhǔn)備的知識(shí)點(diǎn)很龐大,今天要講的知識(shí)其中之一。一篇關(guān)于HandlerThread
的解析
文檔筆記~
知識(shí)匯總的PDF相關(guān)內(nèi)容后續(xù)GitHub更新,想沖擊金三銀四的小伙伴可以找找看看,歡迎star
(順手留下GitHub鏈接,需要獲取相關(guān)面試等內(nèi)容的可以自己去找)
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)
HandlerThread
是Google幫我們封裝好的,可以用來(lái)執(zhí)行多個(gè)耗時(shí)操作,而不需要 多次開(kāi)啟線程,里面是采用Handler
和Looper
實(shí)現(xiàn)的。
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called
HandlerThread
?HandlerThread
的實(shí)例對(duì)象 HandlerThread handlerThread = new HandlerThread("myHandlerThread" );
該參數(shù)表示線程的名字,可以隨便選擇。
HandlerThread
線程 handlerThread.start();
將我們的handlerThread
與Handler綁定在一起。 還記得是怎樣將Handler與線 程對(duì)象綁定在一起的嗎?其實(shí)很簡(jiǎn)單,就是將線程的looper
與Handler
綁定在一 起,代碼如下:
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
checkForUpdate();
if(isUpdate){
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
}
};
注意必須按照以上三個(gè)步驟來(lái),下面在講解源碼的時(shí)候會(huì)分析其原因
public class MainActivity extends AppCompatActivity {
private static final int MSG_UPDATE_INFO = 0x100;
Handler mMainHandler = new Handler();
private TextView mTv;
private Handler mThreadHandler;
private HandlerThread mHandlerThread;
private boolean isUpdate = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTv = (TextView) findViewById(R.id.tv);
initHandlerThread();
}
private void initHandlerThread() {
mHandlerThread = new HandlerThread("xujun");
mHandlerThread.start();
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
checkForUpdate();
if (isUpdate) {
mThreadHandler.sendEmptyMessage(MSG_UPDATE_I NFO);
}
}
};
}
/**
* 模擬從服務(wù)器解析數(shù)據(jù)
*/
private void checkForUpdate() {
try {
//模擬耗時(shí)
Thread.sleep(1200);
mMainHandler.post(new Runnable() {
@Override
public void run() {
String result = "實(shí)時(shí)更新中,當(dāng)前股票行情:<font color='red'>%d</font>";
result = String.format(result, (int) (Math.r andom() * 5000 + 1000));
mTv.setText(Html.fromHtml(result));
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void onResume() {
isUpdate = true;
super.onResume();
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
@Override
protected void onPause() {
super.onPause();
isUpdate = false;
mThreadHandler.removeMessages(MSG_UPDATE_INFO);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit();
mMainHandler.removeCallbacksAndMessages(null);
}
}
運(yùn)行以上測(cè)試代碼,將可以看到如下效果圖(例子不太恰當(dāng),主要使用場(chǎng)景是在 handleMessage
中執(zhí)行耗時(shí)操作)
HandlerThread
源碼分析官方源代碼如下,是基于sdk23的,可以看到,只有一百多行代碼而已。
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if nee ded to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
//持有鎖機(jī)制來(lái)獲得當(dāng)前線程的Looper對(duì)象
synchronized (this) {
mLooper = Looper.myLooper();
//發(fā)出通知,當(dāng)前線程已經(jīng)創(chuàng)建mLooper對(duì)象成功,這里主要是通知g etLooper方法中的wait notifyAll();
}
//設(shè)置線程的優(yōu)先級(jí)別
Process.setThreadPriority(mPriority);
//這里默認(rèn)是空方法的實(shí)現(xiàn),我們可以重寫(xiě)這個(gè)方法來(lái)做一些線程開(kāi)始之前 的準(zhǔn)備,方便擴(kuò)展
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 直到線程創(chuàng)建完Looper之后才能獲得Looper對(duì)象,Looper未創(chuàng)建成功 ,阻塞 synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid( ).
*/
public int getThreadId() {
return mTid;
}
}
1)首先我們先來(lái)看一下它的構(gòu)造方法
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
有兩個(gè)構(gòu)造方法,一個(gè)參數(shù)的和兩個(gè)參數(shù)的,name代表當(dāng)前線程的名稱(chēng),priority
為線程的優(yōu)先級(jí)別
2)接著我們來(lái)看一下run()
方法,在run方法里面我們 可以看到我們會(huì)初始化一個(gè)Looper
,并設(shè)置線程的優(yōu) 先級(jí)別
public void run() {
mTid = Process.myTid();
Looper.prepare();
//持有鎖機(jī)制來(lái)獲得當(dāng)前線程的Looper對(duì)象
synchronized (this) {
mLooper = Looper.myLooper();
//發(fā)出通知,當(dāng)前線程已經(jīng)創(chuàng)建mLooper對(duì)象成功,這里主要是通知getLo oper方法中的wait notifyAll();
}
//設(shè)置線程的優(yōu)先級(jí)別
Process.setThreadPriority(mPriority);
//這里默認(rèn)是空方法的實(shí)現(xiàn),我們可以重寫(xiě)這個(gè)方法來(lái)做一些線程開(kāi)始之前的準(zhǔn)備 ,方便擴(kuò)展
onLooperPrepared();
Looper.loop();
mTid = -1;
}
HandlerThread
的時(shí)候必須調(diào)用 start() 方 法,接著才可以將我們的HandlerThread
和我們的handler綁定在一起嗎?其實(shí) 原因就是我們是在 run()
方法才開(kāi)始初始化我們的looper
,而我們調(diào)用 HandlerThread
的 start()
方法的時(shí)候,線程會(huì)交給虛擬機(jī)調(diào)度,由虛擬機(jī) 自動(dòng)調(diào)用run方法
mHandlerThread.start();
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
checkForUpdate();
if(isUpdate){
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
}
};
notifyAll()
;,原因我們可以 從 getLooper()
方法中知道
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 直到線程創(chuàng)建完Looper之后才能獲得Looper對(duì)象,Looper未創(chuàng)建成功,阻塞
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
總結(jié): 在獲得mLooper
對(duì)象的時(shí)候存在一個(gè)同步的問(wèn)題,只有當(dāng)線程創(chuàng)建成功并且 Looper
對(duì)象也創(chuàng)建成功之后才能獲得mLooper
的值。這里等待方法wait和run方法 中的notifyAll
方法共同完成同步問(wèn)題。
3)接著我們來(lái)看一下quit
方法和quitSafe
方法
//調(diào)用這個(gè)方法退出Looper消息循環(huán),及退出線程
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
//調(diào)用這個(gè)方法安全地退出線程
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
跟蹤這兩個(gè)方法容易知道只兩個(gè)方法最終都會(huì)調(diào)用MessageQueue
的 quit(boolean safe)
方法
void quit(boolean safe) {
if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
//安全退出調(diào)用這個(gè)方法
if (safe) {
removeAllFutureMessagesLocked();
} else {//不安全退出調(diào)用這個(gè)方法
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previou sly false.nativeWake(mPtr);
}
}
不安全的會(huì)調(diào)用 removeAllMessagesLocked()
; 這個(gè)方法,我們來(lái)看這個(gè)方法是 怎樣處理的,其實(shí)就是遍歷Message
鏈表,移除所有信息的回調(diào),并重置為null。
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
安全地會(huì)調(diào)用 removeAllFutureMessagesLocked()
; 這個(gè)方法,它會(huì)根據(jù) Message.when
這個(gè)屬性,判斷我們當(dāng)前消息隊(duì)列是否正在處理消息,沒(méi)有正在處 理消息的話,直接移除所有回調(diào),正在處理的話,等待該消息處理處理完畢再退出該循環(huán)。因此說(shuō) quitSafe()
是安全的,而 quit()
方法是不安全的,因?yàn)閝uit方 法不管是否正在處理消息,直接移除所有回調(diào)。
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
//判斷當(dāng)前隊(duì)列中的消息是否正在處理這個(gè)消息,沒(méi)有的話,直接移除所有 回調(diào)
if (p.when > now) {
removeAllMessagesLocked();
} else {//正在處理的話,等待該消息處理處理完畢再退出該循環(huán)
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
文檔筆記~
知識(shí)匯總的PDF相關(guān)內(nèi)容后續(xù)GitHub更新,想沖擊金三銀四的小伙伴可以找找看看,歡迎star
(順手留下GitHub鏈接,需要獲取相關(guān)面試等內(nèi)容的可以自己去找)
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。