1. 線程的定義
線程(thread)是操作系統(tǒng)能夠進(jìn)行運算調(diào)度的最小單位。它被包含在進(jìn)程之中,是進(jìn)程中的實際運作單位。
一個進(jìn)程中可以并發(fā)多個線程,每條線程并行執(zhí)行不同的任務(wù)。每個線程都會分配一個私有內(nèi)存區(qū)域,該區(qū)域主要用于在執(zhí)行過程中存儲方法、局部變量和參數(shù)。一旦線程終止私有內(nèi)存區(qū)將會被銷毀。更多關(guān)于進(jìn)程與線程的區(qū)別和聯(lián)系請參照上一篇文章關(guān)于Android進(jìn)程的那些事兒,在此不再重復(fù)。
2. Android的單線程模式
在Android的系統(tǒng)啟動流程和應(yīng)用啟動流程這篇文章中有提到,Android應(yīng)用啟動時,zygote進(jìn)程fork自身,開啟一個Linux進(jìn)程和一個主線程(Main Thread),主線程負(fù)責(zé)UI顯示、更新、控件交互,因此主線程又叫做UI線程。
Android中的單線程模式必須遵循兩條規(guī)則:
不要阻塞 UI 線程
原因:
由于UI繪制和事件分發(fā)采用單線程模式,當(dāng)UI線程中執(zhí)行耗時操作(網(wǎng)絡(luò)訪問或數(shù)據(jù)庫查詢)時,UI線程會被阻塞,從而出現(xiàn)ANR(application not responding)或者類似NetworkOnMainThreadException之類的錯誤。不要在 UI 線程之外訪問 Android UI 工具包
原因:
UI的顯示、更新和控件交互主要通過Android UI 工具包(Android UI toolkit,包括android.widget和android.view)來實現(xiàn)的,而Android UI toolkit并非線程安全的工具包,如果我們在工作線程中刷新UI的時候,主線程也恰好在刷新UI,就會出現(xiàn)沖突。因此Android不允許在UI線程之外進(jìn)行UI的相關(guān)操作,否則會出現(xiàn)CalledFromWrongThreadException的錯誤。
3. 工作線程和UI線程(主線程)
為了遵循上述單線程模式的兩條規(guī)則,我們必須將耗時操作放在工作線程中,將UI操作放在UI線程中,同時有需要兩個線程之間的切換。
3.1 主線程中開啟工作線程
Java中提供了兩種方法:Thread和Runnable
Thread
繼承Thread類覆寫run()然后thread.start()Runnable
實現(xiàn)Runnable接口復(fù)寫run()然后New Thread(Runnable).start()。
但是,在Android中這兩種方法是不值得推薦的。最好使用Android自帶的Handler,AsyncTask,IntentService, Loader,Service等方式來實現(xiàn)。下面會詳細(xì)說明。
3.2 工作線程中訪問UI線程
下面三個方法可以訪問UI線程:
- Activity.runOnUIThread(Runable)
- View.post(Runable)
- View.postDelayed(Runable,long)
但是如果主進(jìn)程和工作線程之間需要更復(fù)雜的交互和操作,上面的方法就無能為力了,現(xiàn)在輪到Handler, AsyncTask, HandlerThread上場了,下面會展開詳述。
4. 線程間通信
4.1 Handler
由于Android單線程模式下的兩條原則的存在,線程間的通信顯得尤為重要,Handler正是用來解決這個問題的。消息(Message)的傳遞需要Handler,MessageQueue, Looper共同來完成。其中Looper在Handler和MessageQueue之間起到橋梁和紐帶的作用。在此之前,我們有必要了解一下相關(guān)概念
4.1.1 相關(guān)概念
Message
Message類實現(xiàn)了Parcelable接口,因此它可以包含任意的數(shù)據(jù)對象,并把它傳遞給Handler。盡管Message的構(gòu)造方法是public,但是我們還是推薦使用Message.obtain()或者M(jìn)essage.obtainMessage()來得到一個Message實例。-
MessageQueue
MessageQueue(消息隊列)顧名思義,Message組成的一個List,但是Message并不是直接加入到MessageQueue的,而是通過與Looper綁定的Handler來實現(xiàn)的。獲取當(dāng)前線程的MessageQueue的方法是Looper.MyQueue()。
Looper
Looper類用來操作一個線程的消息循環(huán)。用法如下:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();//創(chuàng)建looper
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();//
}
}
-
Handler
handler跟一個線程和該線程的messagequeue綁定的,當(dāng)我們新建一個Handler的時候,它就會與當(dāng)前線程,和線程的消息隊列綁定在一起。Handler負(fù)責(zé)傳遞Message或者Runnable對象給MessageQueue,并當(dāng)他們從消息隊列出來的時候執(zhí)行相關(guān)操作。
Handler有兩個用途:
(1)安排message和runnable在將來的某個時刻得到執(zhí)行
常用實現(xiàn)方法有
post(Runnable)
[postAtTime(Runnable, long)](https://developer.android.com/reference/android/os/Handler.html#postAtTime(java.lang.Runnable, long))
[postDelayed(Runnable, long)](https://developer.android.com/reference/android/os/Handler.html#postDelayed(java.lang.Runnable, long))
sendEmptyMessage(int)
sendMessage(Message)
[sendMessageAtTime(Message, long)](https://developer.android.com/reference/android/os/Handler.html#sendMessageAtTime(android.os.Message, long))
[sendMessageDelayed(Message, long)](https://developer.android.com/reference/android/os/Handler.html#sendMessageDelayed(android.os.Message, long))
其中,Message的處理是通過Handler的 handleMessage(Message)來實現(xiàn)的(需要繼承Handler的基類)。
(2)在其他線程中執(zhí)行某些操作。
Handler可以在線程間傳遞消息,工作原理如下
4.1.2 工作機(jī)制
Handler用來發(fā)送和處理Message或者Runnable對象,每個Handler實例都會綁定到一個線程和這個線程的MessageQueue。當(dāng)創(chuàng)建Handler的時候,他會默認(rèn)綁定到創(chuàng)建它的線程上,他會給MessageQueue發(fā)送message和runnable,在Looper輪訓(xùn)到該條消息的時候,回調(diào)創(chuàng)建該消息的Handler的handlerMessage方法,處理從message queue出來的message和runnable。
具體流程如下圖:

當(dāng)應(yīng)用啟動時,新的進(jìn)程被創(chuàng)建的時候,它的主線程會運行一個message queue來管理上層應(yīng)用對象(Activity,Broadcast Receiver)和創(chuàng)建的窗口。也可以創(chuàng)建工作線程,然后創(chuàng)建Handler,在工作線程中調(diào)用postRunnable或者sendMessage類方法來傳遞消息給主線程。這時message或者Runnable對象將會在handler的message queue中排隊并在它出站時得到執(zhí)行。

4.2 AsyncTask
4.2.1 使用方法
首先,繼承AsyncTask類,至少重寫doInBackground這個方法。比如
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
@Override
protected Long doInBackground(URL... urls) {//這個方法必須被重寫
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
@Override
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
@Override
protected void onPreExecute() {
}
@Override
protected void onPostExecute(Long result) {//doInBackground執(zhí)行完成之后調(diào)用UI線程
showDialog("Downloaded " + result + " bytes");
}
}
然后在主線程中執(zhí)行:
new DownloadFilesTask().execute(url1, url2, url3);
4.2.2 執(zhí)行過程
一個異步任務(wù)的4步走:
onPreExecute
doInBackground
onProgressUpdate
onPostExecute
從方法名可以理解這四個方法的功能和執(zhí)行順序,不再贅述。
4.2.3 注意事項
- 上述四個方法不可以手動調(diào)用
- 每個task的加載,創(chuàng)建,執(zhí)行都必須在UI線程中執(zhí)行。
- 該任務(wù)只能執(zhí)行一次,第二次執(zhí)行會拋異常
- AsyncTask開啟線程的方法asyncTask.execute()默認(rèn)是也是開啟一個線程和一個隊列的,不過也可以通過asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0)開啟一個含有5個新線程的線程池,也就是說有個5個隊列了,假如說你執(zhí)行第6個耗時任務(wù)時,除非前面5個都還沒執(zhí)行完,否則任務(wù)是不會阻塞的,這樣就可以大大減少耗時任務(wù)延遲的可能性。
除了常用的Handler和AsyncTask之外,IntentService和HandlerThread也為線程間的通信提供了極大地便利
4.3 IntentService
有了intentService就可以不用自己啟動和結(jié)束線程了,IntentService內(nèi)部會自動執(zhí)行線程的開啟和銷毀。
4.3.1 使用步驟
(1)繼承IntentService類,并重寫onHandleIntent()方法,這個方法中,執(zhí)行耗時操作
public class myIntentService extends IntentService {
public myIntentService() {
super("myIntentService");
// 注意構(gòu)造函數(shù)參數(shù)為空,這個字符串就是worker thread的名字
}
@Override
protected void onHandleIntent(Intent intent) {
//根據(jù)Intent的不同進(jìn)行不同的事務(wù)處理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
Log.i("myIntentService", "do task1");
break;
case "task2":
Log.i("myIntentService", "do task2");
break;
default:
break;
}
}
//--------------------用于打印生命周期--------------------
@Override
public void onCreate() {
Log.i("myIntentService", "onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("myIntentService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("myIntentService", "onDestroy");
super.onDestroy();
}
}
然后通過startService(Intent i)啟動服務(wù),Intent 會攜帶相關(guān)數(shù)據(jù),onHandleIntent會對這些數(shù)據(jù)進(jìn)行識別,。
//同一服務(wù)只會開啟一個worker thread,在onHandleIntent函數(shù)里依次處理intent請求。
Intent i = new Intent("cn.scu.finch");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
4.3.2 執(zhí)行過程
onCreate--->onStartCommand-->onHandleIntent-->onDestroy
intentservice的oncreate方法中會通過HandlerThread 開啟新線程,創(chuàng)建對應(yīng)的Looper,和MessageQueue。
通過onStartCommand()傳遞給服務(wù)intent被依次插入到工作隊列中。工作隊列又把intent逐個發(fā)送給onHandleIntent()。
通過在onHandleIntent((Intent)msg.obj)中調(diào)用你的處理程序.
處理完后會自動停止自己的服務(wù)Ondestroy
4.3.3 注意事項
- IntentService 會創(chuàng)建一個線程,來處理所有傳給onStartCommand()的Intent請求。
- 對于startService()請求執(zhí)行onHandleIntent()中的耗時任務(wù),會生成一個消息隊列,每次只有一個Intent傳入onHandleIntent()方法并執(zhí)行。也就是同一時間只會有一個耗時任務(wù)被執(zhí)行,其他的請求還要在后面排隊, onHandleIntent()方法不會多線程并發(fā)執(zhí)行。
- 當(dāng)所有startService()請求被執(zhí)行完成后,IntentService 會自動銷毀,所以不需要自己寫stopSelf()或stopService()來銷毀服務(wù)。
- 提供默認(rèn)的onBind()實現(xiàn) ,即返回null,不適合綁定的 Service。采用StartService來啟動,即使啟動它的Activity被銷毀也不會影響service的生命周期
- 提供默認(rèn)的 onStartCommand() 實現(xiàn),將intent傳入等待隊列中,然后到onHandleIntent()的實現(xiàn)。
- Service等四大組件默認(rèn)是運行在主線程上的!沒有界面不代表不再UI線程。
4.4. HandlerThread
HandlerThread是Thread的子類,可以創(chuàng)建一個帶有l(wèi)ooper的線程,并創(chuàng)建與這個looper綁定的Handler,代碼如下
HandlerThread mThread = new HandlerThread("message");
mThread.start();
Handler mHandler = new Handler(mThread.getLooper())
{
@Override
public void handleMessage(Message msg)
{
// to do sth
}
};
可以看出,跟Handler非常類似,只是新建HandlerThread時,與該線程綁定的looper也被創(chuàng)建了,所以不再需要我們自己去調(diào)用Looper.prepare(),Loop.loop()。直接通過thread.getLooper()就可以獲取到當(dāng)前線程的looper,類似于根據(jù)looper獲取MesssageQueue的方法:Looper.MyQueue()。
5. 總結(jié)
由于Android系統(tǒng)的單線程模式,線程之間的通信必不可少,必須掌握Handler,AsyncTask, IntentService, HandlerThread的工作原理并加以熟練使用。其中Handler跟HandlerThread工作原理是一樣的,很多情況下使用HandlerThread可以簡化很多工作。