日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android:异步处理之Handler+Thread的应用(一)

發(fā)布時(shí)間:2023/12/18 Android 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android:异步处理之Handler+Thread的应用(一) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

  很久很久以前就聽說了,每一個(gè)android的應(yīng)用程序都會(huì)分別運(yùn)行在一個(gè)獨(dú)立的dalvik虛擬機(jī)進(jìn)程中,而在每個(gè)虛擬機(jī)在啟動(dòng)時(shí)會(huì)運(yùn)行一個(gè)UI主線程(Main Thread),而為啥叫UI主線程而不是AI主線程或者是BI主線程呢?因?yàn)樗幚砣亢蚒I相關(guān)的事件;因?yàn)锳ndroid系統(tǒng)采用的是UI單線程模型,只能由UI主線程對(duì)其進(jìn)行UI操作,如果子線程抱著眾人拾柴火焰高的覺悟來幫忙UI主線程更新UI界面的話,對(duì)不起哦~Android系統(tǒng)就會(huì)報(bào)錯(cuò)的。粗俗點(diǎn)講就是:我們只能通過UI主線程來蹂躪UI界面,但是其他線程來的話會(huì)被告弓雖女干滴。。

  那么現(xiàn)在問題來了!鑒于近來挖掘機(jī)那么火,我也不好意思繼續(xù)問這個(gè)問題了。。。嗯嗯~網(wǎng)絡(luò)操作之類耗時(shí)操作就像挖掘機(jī)那樣,我們?cè)谙螺d文件的時(shí)候一樣跟挖掘機(jī)挖個(gè)大坑一樣需要一定的時(shí)間;當(dāng)挖掘機(jī)司機(jī)挖好一個(gè)大坑要找老板反饋工作完成一樣,我們下載好一個(gè)文件自然要馬上告訴屏幕前苦逼等待的用戶們,誰知道他們多著急想看**.avi呢;但是你在挖坑時(shí)好意思叫老板在旁邊看你嗎?老板分分鐘為幾千萬上下的事忙著呢~所以嘛同理,對(duì)于網(wǎng)絡(luò)操作,我們當(dāng)然也不能在UI主線程中進(jìn)行網(wǎng)絡(luò)操作,因?yàn)檫@樣會(huì)阻塞主線程造成界面卡死,也會(huì)造成ANR(應(yīng)用程序無響應(yīng))。我們應(yīng)該把文件下載、文件讀取諸如此類的耗時(shí)操作放到子線程中去進(jìn)行,等到子線程耗時(shí)操作完成時(shí)通知UI界面做出響應(yīng)。

不要在UI主線程中進(jìn)行耗時(shí)操作

  如果你不信邪一定要在UI主線程進(jìn)行下載文件、加載大文件之類的耗時(shí)操作。如下代碼:

private Button btn; //onCreate之類的生命周期的方法就是允許在UI主線程中 @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn = (Button) findViewById(R.id.btn);btn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {downLoad();//調(diào)用UI主線程的下載函數(shù)}}); }private void downLoad(){try {Thread.sleep(10000);//休眠10秒,模擬網(wǎng)絡(luò)文件下載耗時(shí)操作} catch (InterruptedException e) {e.printStackTrace();} }

你會(huì)發(fā)現(xiàn)界面卡主了10秒:(模擬下載操作的按鈕為深色,說明按鈕一直為按下狀態(tài))

如果這時(shí)候你手比較管不住的話,雖然點(diǎn)幾下界面,沒事~Androi系統(tǒng)會(huì)馬上送你一份ANR大禮哦,而且還不用998元耶!

小結(jié)一個(gè):不要在UI主線程中進(jìn)行耗時(shí)操作,你可能會(huì)疑問什么是UI主線程,UI主線程主要運(yùn)行的就是Activity、Service等里面的生命周期方法,所以不要在生命周期方法如onCreate()中進(jìn)行下載這些大事件。對(duì)于耗時(shí)操作,我們應(yīng)該新建一個(gè)子線程并交給他處理,但是還需要注意一點(diǎn)。

不要在子線程中更新UI界面

  既然我們說下載文件要在子線程中進(jìn)行,那么我們就新建一個(gè)子線程把下載操作放到里面進(jìn)行咯,代碼如下:

protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn = (Button) findViewById(R.id.btn);text = (TextView) findViewById(R.id.text);btn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {new Thread(){@Overridepublic void run() {//在子線程中進(jìn)行下載操作try {Thread.sleep(10000);//休眠10秒,模擬耗時(shí)操作} catch (InterruptedException e) {e.printStackTrace();}text.setText("下載完成");//設(shè)置TextView,通知UI界面下載完成}}.start();}}); }

10秒后,你覺得會(huì)在UI界面完美顯示“下載完成”么?一般,出現(xiàn)這個(gè)才符合Androi系統(tǒng)的一貫作風(fēng)

并且在Log中報(bào)錯(cuò)如下

小弟英語其實(shí)很廢柴,但是隱隱約約有人告訴我:這不是叫只能在主線程中更新UI嗎?不信,金山翻譯一下去呀。。。。

小結(jié)一個(gè):不要在子線程中更新UI界面,這樣會(huì)導(dǎo)致android系統(tǒng)報(bào)錯(cuò)、應(yīng)用崩潰退出。UI界面時(shí)單線程模式,我們只能通過UI主線程中對(duì)UI的界面進(jìn)行相關(guān)的更新,千萬不要越線辦事,你要記住的是~UI界面是UI主線程的老婆,你們這些子線程誰都別想動(dòng)!

利用Thread+Handler進(jìn)行異步處理

  那么問題來了,現(xiàn)在我們需要進(jìn)行耗時(shí)操作(例如下載文件)時(shí)不能在主線程執(zhí)行,我們又需要在UI界面通知用戶我們活干完了不能再子線程中執(zhí)行。這似乎是一個(gè)棘手的熱山芋呀,幸好谷歌給我們提供了一個(gè)救我們于危難之中的Handler,一個(gè)能讓主線程監(jiān)聽子線程發(fā)送來消息的東東,至于Handler的實(shí)現(xiàn)原理我會(huì)在后面的文章詳細(xì)介紹,現(xiàn)在我們只需要先了解Handler的用法。

private Button btn; private TextView text;private Handler handler = new Handler(){private int process = 0;@Overridepublic void handleMessage(Message msg) {switch(msg.what){case 0://更細(xì)下載進(jìn)度process += 1;text.setText("下載" + process + "%");//在主線程中更新UI界面break;case 1://提示下載完成text.setText("下載完成");//在主線程中更新UI界面break;default:break;}} }; //onCreate之類的生命周期的方法就是允許在UI主線程中 @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn = (Button) findViewById(R.id.btn);text = (TextView) findViewById(R.id.text);btn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {new Thread(){@Overridepublic void run() {//在子線程中進(jìn)行下載操作for(int i = 0; i < 100; i++){try {Thread.sleep(200);//休眠0.2秒,模擬耗時(shí)操作} catch (InterruptedException e) {e.printStackTrace();}handler.sendEmptyMessage(0);//發(fā)送消息到handler,通知下載進(jìn)度}handler.sendEmptyMessage(1);//發(fā)送消失到handler,通知主線程下載完成}}.start();}}); }

?這里來解釋一下Handler的使用方法:

1、我們?yōu)榱瞬蛔枞骶€程,將下載任務(wù)通過子線程來執(zhí)行。

new Thread(){@Overridepublic void run() {//在子線程中進(jìn)行下載操作for(int i = 0; i < 100; i++){try {Thread.sleep(200);//休眠0.2秒,模擬耗時(shí)操作} catch (InterruptedException e) {e.printStackTrace();}handler.sendEmptyMessage(0);//發(fā)送消息到handler,通知下載進(jìn)度}handler.sendEmptyMessage(1);//發(fā)送消失到handler,通知主線程下載完成} }.start();

2、當(dāng)子線程需要跟主線程交流時(shí),也就是當(dāng)子線程要跟UI主線程說:親,偶下載文件到80%了或者偶已經(jīng)把文件下載完成了!執(zhí)行這句代碼

handler.sendEmptyMessage(1);//發(fā)送消失到handler,通知主線程下載完成

3、當(dāng)發(fā)送空消息之后,在Handler將會(huì)收到子線程發(fā)來的消息,觸發(fā)回調(diào)方法handlerMessage(),我們就在這里對(duì)UI界面進(jìn)行更新,這個(gè)回調(diào)方法是運(yùn)行在UI主線程的

@Override public void handleMessage(Message msg) {switch(msg.what){case 0://更細(xì)下載進(jìn)度process += 1;text.setText("下載" + process + "%");//在主線程中更新UI界面break;case 1://提示下載完成text.setText("下載完成");//在主線程中更新UI界面break;default:break;} }

4、最后,UI界面更新成功!(圖嘛,我這里就不上了。。。。)

小結(jié)一個(gè):對(duì)于比較耗時(shí)間的任務(wù),我們一般需要放在子線程中執(zhí)行;當(dāng)子線程更新UI界面時(shí),子線程可以通過Handler來通知主線程更新,一般通過發(fā)送消息來觸發(fā)handlerMessage()這個(gè)回調(diào)方法來執(zhí)行UI界面的更新。

進(jìn)一步簡(jiǎn)略de操作:handler.post方法和view.post方法

  但是如果你覺得每次都要重寫handlerMessage()比較麻煩,我們完全可以用更加簡(jiǎn)略的方法來解決我們的需求,就是用handler中的post方法。代碼如下

new Thread(){@Overridepublic void run() {//在子線程中進(jìn)行下載操作try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}handler.post(new Runnable() {@Overridepublic void run() {text.setText("下載完成");}});//發(fā)送消失到handler,通知主線程下載完成} }.start();

  這樣處理的話我們就可以不用重寫handlerMessage()方法了,適合子線程與主線程進(jìn)行較為單一的交流。但在這里我們要強(qiáng)調(diào)的一點(diǎn)的是,post里面的Runnable還是在UI主線程中運(yùn)行的,而不會(huì)另外開啟線程運(yùn)行,千萬不要在Runnable的run()里面進(jìn)行耗時(shí)任務(wù),不然到時(shí)又ANR了可別找我哦。。

如果你有時(shí)候連handler都不想搞,還可以這樣寫代碼滴。

我們只需要把handler換成View組件進(jìn)行post,更新任務(wù)自然會(huì)加載到UI主線程中進(jìn)行處理。

text.post(new Runnable() {@Overridepublic void run() {text.setText("下載完成");} });//發(fā)送消失到handler,通知主線程下載完成

至于Handler機(jī)制以及這兩種post的原理,我將會(huì)在后面的博客文章中專題介紹,這里只提供一個(gè)使用方法而已。

?

終于寫完了睡覺,怎么沒有掌聲呢??雞蛋、啤酒瓶砸?guī)讉€(gè)上來~~~~哈哈

權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。 https://blog.csdn.net/fnhfire_7030/article/details/79518819前言:又到了一年一度的跳槽季,準(zhǔn)備跳槽的你在關(guān)于Android面試方面的知識(shí)都完全掌握了嗎?Android面試中經(jīng)常被問到的知識(shí)——Android消息機(jī)制即Handler有關(guān)的問題你都能解釋的清楚嗎?如果你對(duì)Android消息機(jī)制比較模糊或者能夠回答與Handler有關(guān)的問題但是不清楚其中的原理,那么你將會(huì)在本文得到你想要的答案。
閱讀本文后的收貨??閱讀本文后你將會(huì)有以下收獲:
清楚的理解Handler的工作原理理清Handler、Message、MessageQueue以及Looper之間的關(guān)系知道Looper是怎么和當(dāng)前線程進(jìn)行綁定的是否能在子線程中創(chuàng)建Handler獲得分析Handler源碼的思路要想有以上的收獲,就需要研究Handler的源碼,從源碼中來得到答案。
開始探索之路Handler的使用??先從Handler的使用開始。我們都知道Android的主線程不能處理耗時(shí)的任務(wù),否者會(huì)導(dǎo)致ANR的出現(xiàn),但是界面的更新又必須要在主線程中進(jìn)行,這樣,我們就必須在子線程中處理耗時(shí)的任務(wù),然后在主線程中更新UI。但是,我們?cè)趺粗雷泳€程中的任務(wù)何時(shí)完成,又應(yīng)該什么時(shí)候更新UI,又更新什么內(nèi)容呢?為了解決這個(gè)問題,Android為我們提供了一個(gè)消息機(jī)制即Handler。下面就看下Handler的常見使用方式,代碼如下
public class MainActivity extends AppCompatActivity implements View.OnClickListener {? ? private Button mStartTask;
? ? @SuppressLint("HandlerLeak")? ? private Handler mHandler = new Handler() {? ? ? ? @Override? ? ? ? public void handleMessage(Message msg) {? ? ? ? ? ? super.handleMessage(msg);? ? ? ? ? ? if (msg.what == 1) {? ? ? ? ? ? ? ? Toast.makeText(MainActivity.this, "刷新UI、", Toast.LENGTH_SHORT).show();? ? ? ? ? ? }? ? ? ? }? ? };
? ? @Override? ? protected void onCreate(Bundle savedInstanceState) {? ? ? ? super.onCreate(savedInstanceState);? ? ? ? setContentView(R.layout.activity_main);? ? ? ? initView();? ? }
? ? private void initView() {? ? ? ? mStartTask = findViewById(R.id.btn_start_task);? ? ? ? mStartTask.setOnClickListener(this);? ? }
? ? @Override? ? public void onClick(View v) {? ? ? ? switch (v.getId()) {? ? ? ? ? ? case R.id.btn_start_task:? ? ? ? ? ? ? ? new Thread(new Runnable() {? ? ? ? ? ? ? ? ? ? @Override? ? ? ? ? ? ? ? ? ? public void run() {? ? ? ? ? ? ? ? ? ? ? ? try {? ? ? ? ? ? ? ? ? ? ? ? ? ? Thread.sleep(1000);? ? ? ? ? ? ? ? ? ? ? ? ? ? mHandler.sendEmptyMessage(1);? ? ? ? ? ? ? ? ? ? ? ? } catch (InterruptedException e) {? ? ? ? ? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }).start();? ? ? ? ? ? ? ? break;? ? ? ? }? ? }}
12345678910111213141516171819202122232425262728293031323334353637383940414243444546可以看到在子線程中,讓線程睡了一秒,來模仿耗時(shí)的任務(wù),當(dāng)耗時(shí)任務(wù)處理完之后,Handler會(huì)發(fā)送一個(gè)消息,然后我們可以在Handler的handleMessage方法中得到這個(gè)消息,得到消息之后就能夠在handleMessage方法中更新UI了,因?yàn)閔andleMessage是在主線程中嘛。到這里就會(huì)有以下疑問了:
Handler明明是在子線程中發(fā)的消息怎么會(huì)跑到主線程中了呢?Handler的發(fā)送消息handleMessage又是怎么接收到的呢?帶著這兩個(gè)疑問,開始分析Handler的源碼。
Handler的源碼分析??先看下在我們實(shí)例化Handler的時(shí)候,Handler的構(gòu)造方法中都做了那些事情,看代碼
final Looper mLooper;? ? final MessageQueue mQueue;? ? final Callback mCallback;? ? final boolean mAsynchronous;
/**? ? ?* Default constructor associates this handler with the {@link Looper} for the? ? ?* current thread.? ? ?*? ? ?* If this thread does not have a looper, this handler won't be able to receive messages? ? ?* so an exception is thrown.? ? ?*/? ? public Handler() {? ? ? ? this(null, false);? ? }
/**? ? ?* Use the {@link Looper} for the current thread with the specified callback interface? ? ?* and set whether the handler should be asynchronous.? ? ?*? ? ?* Handlers are synchronous by default unless this constructor is used to make? ? ?* one that is strictly asynchronous.? ? ?*? ? ?* Asynchronous messages represent interrupts or events that do not require global ordering? ? ?* with respect to synchronous messages.? Asynchronous messages are not subject to? ? ?* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.? ? ?*? ? ?* @param callback The callback interface in which to handle messages, or null.? ? ?* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for? ? ?* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.? ? ?*? ? ?* @hide? ? ?*/? ? 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;? ? }12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152通過源碼可以看到Handler的無參構(gòu)造函數(shù)調(diào)用了兩個(gè)參數(shù)的構(gòu)造函數(shù),而在兩個(gè)參數(shù)的構(gòu)造函數(shù)中就是將一些變量進(jìn)行賦值。
??看下下面的代碼
?mLooper = Looper.myLooper();? ? ? ? if (mLooper == null) {? ? ? ? ? ? throw new RuntimeException(? ? ? ? ? ? ? ? "Can't create handler inside thread that has not called Looper.prepare()");? ? ? ? }12345這里是通過Looper中的myLooper方法來獲得Looper實(shí)例的,如果Looper為null的話就會(huì)拋異常,拋出的異常內(nèi)容翻譯過來就是
無法在未調(diào)用Looper.prepare()的線程內(nèi)創(chuàng)建handler
從這句話中,我們可以知道,在調(diào)用Looper.myLooper()之前必須要先調(diào)用Looper.prepare()方法,現(xiàn)在來看下prepare方法中的內(nèi)容,如下
?/** Initialize the current thread as a looper.? ? ? * This gives you a chance to create handlers that then reference? ? ? * this looper, before actually starting the loop. Be sure to call? ? ? * {@link #loop()} after calling this method, and end it by calling? ? ? * {@link #quit()}.? ? ? */? ? 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));? ? }12345678910111213141516從上面代碼中可以看到,prepare()方法調(diào)用了prepare(boolean quitAllowed)方法,prepare(boolean quitAllowed) 方法中則是實(shí)例化了一個(gè)Looper,然后將Looper設(shè)置進(jìn)sThreadLocal中,到了這里就有必要了解一下ThreadLocalle。
什么是ThreadLocalThreadLocal 為解決多線程程序的并發(fā)問題提供了一種新的思路。使用這個(gè)工具類可以很簡(jiǎn)潔地編寫出優(yōu)美的多線程程序。當(dāng)使用ThreadLocal 維護(hù)變量時(shí),ThreadLocal 為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本。
如果看完上面這段話還是搞不明白ThreadLocal有什么用,那么可以看下下面代碼運(yùn)行的結(jié)果,相信看下結(jié)果你就會(huì)明白ThreadLocal有什么作用了。
public class MainActivity extends AppCompatActivity {? ??? ? private static final String TAG = "MainActivity";? ? private ThreadLocal<Integer> mThreadLocal = new ThreadLocal<>();? ??? ? @SuppressLint("HandlerLeak")? ? private Handler mHandler = new Handler(){? ? ? ? @Override? ? ? ? public void handleMessage(Message msg) {? ? ? ? ? ? super.handleMessage(msg);? ? ? ? ? ? if (msg.what == 1) {? ? ? ? ? ? ? ? Log.d(TAG, "onCreate: "+mThreadLocal.get());? ? ? ? ? ? }? ? ? ? }? ? };
? ? @Override? ? protected void onCreate(Bundle savedInstanceState) {? ? ? ? super.onCreate(savedInstanceState);? ? ? ? setContentView(R.layout.activity_main);? ? ? ? mThreadLocal.set(5);
? ? ? ? Thread1 thread1 = new Thread1();? ? ? ? thread1.start();
? ? ? ? Thread2 thread2 = new Thread2();? ? ? ? thread2.start();
? ? ? ? Thread3 thread3 = new Thread3();? ? ? ? thread3.start();
? ? ? ? new Thread(new Runnable() {? ? ? ? ? ? @Override? ? ? ? ? ? public void run() {? ? ? ? ? ? ? ? try {? ? ? ? ? ? ? ? ? ? Thread.sleep(2000);? ? ? ? ? ? ? ? ? ? mHandler.sendEmptyMessage(1);? ? ? ? ? ? ? ? } catch (InterruptedException e) {? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }).start();? ? }
? ? class Thread1 extends Thread {
? ? ? ? @Override? ? ? ? public void run() {? ? ? ? ? ? super.run();? ? ? ? ? ? mThreadLocal.set(1);? ? ? ? ? ? Log.d(TAG, "mThreadLocal1: "+ mThreadLocal.get());? ? ? ? }? ? }
? ? class Thread2 extends Thread {
? ? ? ? @Override? ? ? ? public void run() {? ? ? ? ? ? super.run();? ? ? ? ? ? mThreadLocal.set(2);? ? ? ? ? ? Log.d(TAG, "mThreadLocal2: "+ mThreadLocal.get());? ? ? ? }? ? }
? ? class Thread3 extends Thread {
? ? ? ? @Override? ? ? ? public void run() {? ? ? ? ? ? super.run();? ? ? ? ? ? mThreadLocal.set(3);? ? ? ? ? ? Log.d(TAG, "mThreadLocal3: "+ mThreadLocal.get());? ? ? ? }? ? }}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475看下這段代碼運(yùn)行之后打印的log


可以看到雖然在不同的線程中對(duì)同一個(gè)mThreadLocal中的值進(jìn)行了更改,但最后仍可以正確拿到當(dāng)前線程中mThreadLocal中的值。由此我們可以得出結(jié)論ThreadLocal.set方法設(shè)置的值是與當(dāng)前線程進(jìn)行綁定了的。
??知道了ThreadLocal.set方法的作用,則Looper.prepare方法就是將Looper與當(dāng)前線程進(jìn)行綁定(當(dāng)前線程就是調(diào)用Looper.prepare方法的線程)。
??文章到了這里我們可以知道以下幾點(diǎn)信息了
在對(duì)Handler進(jìn)行實(shí)例化的時(shí)候,會(huì)對(duì)一些變量進(jìn)行賦值。對(duì)Looper進(jìn)行賦值是通過Looper.myLooper方法,但在調(diào)用這句代碼之前必須已經(jīng)調(diào)用了Looper.prepare方法。Looper.prepare方法的作用就是將實(shí)例化的Looper與當(dāng)前的線程進(jìn)行綁定。這里就又出現(xiàn)了一個(gè)問題:在調(diào)用Looper.myLooper方法之前必須必須已經(jīng)調(diào)用了Looper.prepare方法,即在實(shí)例化Handler之前就要調(diào)用Looper.prepare方法,但是我們平常在主線程中使用Handler的時(shí)候并沒有調(diào)用Looper.prepare方法呀!這是怎么回事呢?
??其實(shí),在主線程中Android系統(tǒng)已經(jīng)幫我們調(diào)用了Looper.prepare方法,可以看下ActivityThread類中的main方法,代碼如下
?public static void main(String[] args) {? ? ? ? Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
? ? ? ? // 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");? ? }123456789101112131415161718192021222324252627282930313233343536373839上面的代碼中有一句
Looper.prepareMainLooper();1這句話的實(shí)質(zhì)就是調(diào)用了Looper的prepare方法,代碼如下
?public static void prepareMainLooper() {? ? ? ? prepare(false);//這里調(diào)用了prepare方法? ? ? ? synchronized (Looper.class) {? ? ? ? ? ? if (sMainLooper != null) {? ? ? ? ? ? ? ? throw new IllegalStateException("The main Looper has already been prepared.");? ? ? ? ? ? }? ? ? ? ? ? sMainLooper = myLooper();? ? ? ? }? ? }123456789到這里就解決了,為什么我們?cè)谥骶€程中使用Handler之前沒有調(diào)用Looper.prepare方法的問題了。
??讓我們?cè)倩氐紿andler的構(gòu)造方法中,看下
mLooper = Looper.myLooper();1myLooper()方法中代碼如下
/**? ? ?* Return the Looper object associated with the current thread.? Returns? ? ?* null if the calling thread is not associated with a Looper.? ? ?*/? ? public static @Nullable Looper myLooper() {? ? ? ? return sThreadLocal.get();? ? }1234567其實(shí)就是從當(dāng)前線程中的ThreadLocal中取出Looper實(shí)例。
??再看下Handler的構(gòu)造方法中的
mQueue = mLooper.mQueue;1這句代碼。這句代碼就是拿到Looper中的mQueue這個(gè)成員變量,然后再賦值給Handler中的mQueue,下面看下Looper中的代碼
?final MessageQueue mQueue;? ??private Looper(boolean quitAllowed) {? ? ? ? mQueue = new MessageQueue(quitAllowed);? ? ? ? mThread = Thread.currentThread();? ? }123456同過上面的代碼,我們可以知道m(xù)Queue就是MessageQueue,在我們調(diào)用Looper.prepare方法時(shí)就將mQueue實(shí)例化了。
Handler的sendMessage方法都做了什么??還記得文章開始時(shí)的兩個(gè)問題嗎?
Handler明明是在子線程中發(fā)的消息怎么會(huì)跑到主線程中了呢?Handler的發(fā)送消息handleMessage又是怎么接收到的呢?下面就分析一下Handler的sendMessage方法都做了什么,看代碼
public final boolean sendMessage(Message msg)? ? {? ? ? ? return sendMessageDelayed(msg, 0);? ? }
public final boolean sendMessageDelayed(Message msg, long delayMillis)? ? {? ? ? ? if (delayMillis < 0) {? ? ? ? ? ? delayMillis = 0;? ? ? ? }? ? ? ? return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);? ? }
/**? ? ?* Enqueue a message into the message queue after all pending messages? ? ?* before the absolute time (in milliseconds) <var>uptimeMillis</var>.? ? ?* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>? ? ?* Time spent in deep sleep will add an additional delay to execution.? ? ?* You will receive it in {@link #handleMessage}, in the thread attached? ? ?* to this handler.? ? ?*?? ? ?* @param uptimeMillis The absolute time at which the message should be? ? ?*? ? ? ? ?delivered, using the? ? ?*? ? ? ? ?{@link android.os.SystemClock#uptimeMillis} time-base.? ? ?*? ? ? ? ?? ? ?* @return Returns true if the message was successfully placed in to the?? ? ?*? ? ? ? ?message queue.? Returns false on failure, usually because the? ? ?*? ? ? ? ?looper processing the message queue is exiting.? Note that a? ? ?*? ? ? ? ?result of true does not mean the message will be processed -- if? ? ?*? ? ? ? ?the looper is quit before the delivery time of the message? ? ?*? ? ? ? ?occurs then the message will be dropped.? ? ?*/? ? 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);? ? }123456789101112131415161718192021222324252627282930313233343536373839404142由上面的代碼可以看出,Handler的sendMessage方法最后調(diào)用了sendMessageAtTime這個(gè)方法,其實(shí),無論時(shí)sendMessage、sendEmptyMessage等方法最終都是調(diào)用sendMessageAtTime。可以看到sendMessageAtTime這個(gè)方法最后返回的是*enqueueMessage(queue, msg, uptimeMillis);*下面看下這個(gè)方法,代碼如下
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {? ? ? ? msg.target = this;? ? ? ? if (mAsynchronous) {? ? ? ? ? ? msg.setAsynchronous(true);? ? ? ? }? ? ? ? return queue.enqueueMessage(msg, uptimeMillis);? ? }1234567這里有一句代碼非常重要,
?msg.target = this;1這句代碼就是將當(dāng)前的Handler賦值給了Message中的target變量。這樣,就將每個(gè)調(diào)用sendMessage方法的Handler與Message進(jìn)行了綁定。
enqueueMessage方法最后返回的是**queue.enqueueMessage(msg, uptimeMillis);**也就是調(diào)用了MessageQueue中的enqueueMessage方法,下面看下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;? ? }1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253上面的代碼就是將消息放進(jìn)消息隊(duì)列中,如果消息已成功放入消息隊(duì)列,則返回true。失敗時(shí)返回false,而失敗的原因通常是因?yàn)樘幚硐㈥?duì)列正在退出。代碼分析到這里可以得出以下兩點(diǎn)結(jié)論了
Handler在sendMessage時(shí)會(huì)將自己設(shè)置給Message的target變量即將自己與發(fā)送的消息綁定。Handler的sendMessage是將Message放入MessageQueue中。到了這里已經(jīng)知道Handler的sendMessage是將消息放進(jìn)MessageQueue中,那么又是怎樣從MessageQueue中拿到消息的呢?想要知道答案請(qǐng)繼續(xù)閱讀。
怎樣從MessageQueue中獲取Message??在文章的前面,貼出了ActivityThread類中的main方法的代碼,不知道細(xì)心的你有沒有注意到,在main方法的結(jié)尾處調(diào)用了一句代碼
Looper.loop();1好了,現(xiàn)在可以看看*Looper.loop();*這句代碼到底做了什么了loop方法中的代碼如下
/**? ? ?* Run the message queue in this thread. Be sure to call? ? ?* {@link #quit()} to end the loop.? ? ?*/? ? public static void loop() {? ? ? ? final Looper me = myLooper();//通過myLooper方法拿到與主線程綁定的Looper? ? ? ? if (me == null) {? ? ? ? ? ? throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");? ? ? ? }? ? ? ? final MessageQueue queue = me.mQueue;//從Looper中得到MessageQueue
? ? ? ? // 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();
? ? ? ? //開始死循環(huán)? ? ? ? for (;;) {? ? ? ? ? ? //從消息隊(duì)列中不斷取出消息? ? ? ? ? ? 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 {? ? ? ? ? ? ? ? //這句代碼是重點(diǎn)? ? ? ? ? ? ? ? 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();? ? ? ? }? ? }12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576上面的代碼,我已經(jīng)進(jìn)行了部分注釋,這里有一句代碼非常重要
?msg.target.dispatchMessage(msg);1執(zhí)行到這句代碼,說明已經(jīng)從消息隊(duì)列中拿到了消息,還記得msg.target嗎?就是Message中的target變量呀!也就是發(fā)送消息的那個(gè)Handler,所以這句代碼的本質(zhì)就是調(diào)用了Handler中的dispatchMessage(msg)方法,代碼分析到這里是不是有點(diǎn)小激動(dòng)了呢!穩(wěn)住!下面看下dispatchMessage(msg)這個(gè)方法,代碼如下
/**? ? ?* Handle system messages here.? ? ?*/? ? public void dispatchMessage(Message msg) {? ? ? ? if (msg.callback != null) {? ? ? ? ? ? handleCallback(msg);? ? ? ? } else {? ? ? ? ? ? if (mCallback != null) {? ? ? ? ? ? ? ? if (mCallback.handleMessage(msg)) {? ? ? ? ? ? ? ? ? ? return;? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? ? ? handleMessage(msg);? ? ? ? }? ? }123456789101112131415現(xiàn)在來一句句的來分析上面的代碼,先看下這句
if (msg.callback != null) {? ? ? ? ? ? handleCallback(msg);? ? ? ? }?123msg.callback就是Runnable對(duì)象,當(dāng)msg.callback不為null時(shí)會(huì)調(diào)用 handleCallback(msg)方法,先來看下 handleCallback(msg)方法,代碼如下
?private static void handleCallback(Message message) {? ? ? ? message.callback.run();? ? }123上面的代碼就是調(diào)用了Runnable的run方法。那什么情況下**if (msg.callback != null)**這個(gè)條件成立呢!還記得使用Handler的另一種方法嗎?就是調(diào)用Handler的post方法呀!這里說明一下,使用Handler其實(shí)是有兩種方法的
使用Handler的sendMessage方法,最后在handleMessage(Message msg)方法中來處理消息。使用Handler的post方法,最后在Runnable的run方法中來處理,代碼如下public class MainActivity extends AppCompatActivity implements View.OnClickListener {? ? private Button mTimeCycle,mStopCycle;? ? private Runnable mRunnable;
? ? @Override? ? protected void onCreate(Bundle savedInstanceState) {? ? ? ? super.onCreate(savedInstanceState);? ? ? ? setContentView(R.layout.activity_main);? ? ? ? initView();? ? }
? ? private void initView() {? ? ? ? mTimeCycle = findViewById(R.id.btn_time_cycle);? ? ? ? mTimeCycle.setOnClickListener(this);? ? ? ? mStopCycle = findViewById(R.id.btn_stop_cycle);? ? ? ? mStopCycle.setOnClickListener(this);

? ? ? ? mRunnable = new Runnable() {? ? ? ? ? ? @Override? ? ? ? ? ? public void run() {? ? ? ? ? ? ? ? Toast.makeText(MainActivity.this, "正在循環(huán)!!!", Toast.LENGTH_SHORT).show();? ? ? ? ? ? ? ? mHandler.postDelayed(mRunnable, 1000);? ? ? ? ? ? }? ? ? ? };? ? }
? ? @Override? ? public void onClick(View v) {? ? ? ? switch (v.getId()) {? ? ? ? ? ? case R.id.btn_time_cycle:? ? ? ? ? ? ? ? mHandler.post(mRunnable);? ? ? ? ? ? ? ? break;? ? ? ? ? ? case R.id.btn_stop_cycle:? ? ? ? ? ? ? ? mHandler.removeCallbacks(mRunnable);? ? ? ? ? ? ? ? break;? ? ? ? }? ? }}123456789101112131415161718192021222324252627282930313233343536373839第一種方法,我們已經(jīng)分析了,下面來分析一下第二種使用方式的原理,先看下Handler的post的方法做了什么,代碼如下
/**? ? ?* Causes the Runnable r to be added to the message queue.? ? ?* The runnable will be run on the thread to which this handler is?? ? ?* attached.?? ? ?*??? ? ?* @param r The Runnable that will be executed.? ? ?*?? ? ?* @return Returns true if the Runnable was successfully placed in to the?? ? ?*? ? ? ? ?message queue.? Returns false on failure, usually because the? ? ?*? ? ? ? ?looper processing the message queue is exiting.? ? ?*/? ? public final boolean post(Runnable r)? ? {? ? ? ?return? sendMessageDelayed(getPostMessage(r), 0);? ? }
?private static Message getPostMessage(Runnable r) {? ? ? ? Message m = Message.obtain();? ? ? ? m.callback = r;? ? ? ? return m;? ? }123456789101112131415161718192021由上面的代碼不難看出,post方法最終也是將Runnable封裝成消息,然后將消息放進(jìn)MessageQueue中。下面繼續(xù)分析dispatchMessage方法中的代碼
else {? ? //if中的代碼其實(shí)是和if (msg.callback != null) {handleCallback(msg);}?? ? //原理差不多的,只不過mCallback是Handler中的成員變量。? ? ? ? ? ? if (mCallback != null) {? ? ? ? ? ? ? ? if (mCallback.handleMessage(msg)) {? ? ? ? ? ? ? ? ? ? return;? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? //當(dāng)上面的條件都不成立時(shí),就會(huì)調(diào)用這句代碼? ? ? ? ? ? handleMessage(msg);? ? ? ? }1234567891011上面的代碼就不分析了,我已經(jīng)在代碼中進(jìn)行了注釋,下面再看下**handleMessage(msg)**這個(gè)方法,代碼如下
/**? ? ?* Subclasses must implement this to receive messages.? ? ?*/? ? public void handleMessage(Message msg) {? ? }12345其實(shí),他就是一個(gè)空方法,具體的代碼讓我們自己重寫這個(gè)方法進(jìn)行處理。代碼分析到這里,已經(jīng)可以給出下面問題的答案了。
Handler明明是在子線程中發(fā)的消息怎么會(huì)跑到主線程中了呢?Handler的發(fā)送消息handleMessage又是怎么接收到的呢?在子線程中Handler在發(fā)送消息的時(shí)候已經(jīng)把自己與當(dāng)前的message進(jìn)行了綁定,在通過Looper.loop()開啟輪詢message的時(shí)候,當(dāng)獲得message的時(shí)候會(huì)調(diào)用 與之綁定的Handler的**handleMessage(Message msg)**方法,由于Handler是在主線程創(chuàng)建的,所以自然就由子線程切換到了主線程。
總結(jié)??上面已經(jīng)嗯將Handler的源碼分析了一遍,現(xiàn)在來進(jìn)行一些總結(jié):
1、Handler的工作原理??在使用Handler之前必須要調(diào)用Looper.prepare()這句代碼,這句代碼的作用是將Looper與當(dāng)前的線程進(jìn)行綁定,在實(shí)例化Handler的時(shí)候,通過Looper.myLooper()獲取Looper,然后再獲得Looper中的MessageQueue。在子線程中調(diào)用Handler的sendMessage方法就是將Message放入MessageQueue中,然后調(diào)用Looper.loop()方法來從MessageQueue中取出Message,在取到Message的時(shí)候,執(zhí)行 **msg.target.dispatchMessage(msg);**這句代碼,這句代碼就是從當(dāng)前的Message中取出Handler然后執(zhí)行Handler的handleMessage方法。
2、Handler、Message、MessageQueue以及Looper之間的關(guān)系??在介紹它們之間的關(guān)系之前,先說一下它們各自的作用。
Handler:負(fù)責(zé)發(fā)送和處理消息。Message:用來攜帶需要的數(shù)據(jù)。MessageQueue:消息隊(duì)列,隊(duì)列里面的內(nèi)容就是Message。Looper:消息輪巡器,負(fù)責(zé)不停的從MessageQueue中取Message。它們的關(guān)系如下圖(圖片來源于網(wǎng)上)


3、在子線程中使用Handler??在子線程中使用Handler的方式如下
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();? ? }}123456789101112上面的代碼來自官方的源碼。
結(jié)束語??本文將Handler的機(jī)制詳細(xì)講解了一遍,包括在面試中有關(guān)Handler的一些問題,在文章中也能找到答案。順便說下閱讀代碼應(yīng)該注意的地方,在分析源碼之前應(yīng)該知道你分析代碼的目的,就是你為了得到什么答案而分析代碼;在分析代碼時(shí)切記要避輕就重,不要想著要搞懂每句代碼做了什么,要找準(zhǔn)大方向。文中的代碼已上傳到GitHub,可以在這里獲取,與Handler有關(guān)的源碼在我上傳的源碼的handler包中。
ps: 歷史文章中有干貨哦!
轉(zhuǎn)載請(qǐng)注明出處:www.wizardev.cn---------------------?作者:wizardev?來源:CSDN?原文:https://blog.csdn.net/fnhfire_7030/article/details/79518819?版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!

轉(zhuǎn)載于:https://www.cnblogs.com/Jeely/p/10949274.html

總結(jié)

以上是生活随笔為你收集整理的Android:异步处理之Handler+Thread的应用(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。