一文徹底了解Android中的線程和線程池
前言
從用途上來說Android的線程主要分為主線程和子線程兩類,主線程主要處理和界面相關的工作,子線程主要處理耗時操作。除Thread之外,Android中還有其他扮演線程的角色如AsyncTask、IntentService、HandleThread,其中AsyncTask的底層用到了線程池,IntentService和HandleThread的底層直接使用了線程。
AsyncTask內部封裝了線程池和Handler主要是為了方便開發(fā)者在在線程中更新UI;HandlerThread是一個具有消息循環(huán)的線程,它的內部可以使用Handler;IntentService是一個服務,系統(tǒng)對其進行了封裝使其可以更方便的執(zhí)行后臺任務,IntentService內部采用HandleThread來執(zhí)行任務,當任務執(zhí)行完畢后IntentService會自動退出。IntentService是一個服務但是它不容易被系統(tǒng)殺死因此它可以盡量的保證任務的執(zhí)行。
1.主線程和子線程
主線程是指進程所擁有的的線程,在Java中默認情況下一個進程只能有一個線程,這個線程就是主線程。主線程主要處理界面交互的相關邏輯,因為界面隨時都有可能更新因此在主線程不能做耗時操作,否則界面就會出現(xiàn)卡頓的現(xiàn)象。主線程之外的線程都是子線程,也叫做工作線程。
Android沿用了Java的線程模型,也有主線程和子線程之分,主線程主要工作是運行四大組件及處理他們和用戶的交互,子線程的主要工作就是處理耗時任務,例如網(wǎng)絡請求,I/O操作等。Android3.0開始系統(tǒng)要求網(wǎng)絡訪問必須在子線程中進行否則就會報錯,NetWorkOnMainThreadException
2.Android中的線程形態(tài)
2.1 AsyncTask
AsyncTask是一個輕量級的異步任務類,它可以在線程池中執(zhí)行異步任務然后把執(zhí)行進度和執(zhí)行結果傳遞給主線程并在主線程更新UI。從實現(xiàn)上來說AsyncTask封裝了Thread和Handler,通過AsyncTask可以很方便的執(zhí)行后臺任務以及主線程中訪問UI,但是AsyncTask不適合處理耗時任務,耗時任務還是要交給線程池執(zhí)行。
AsyncTask的四個核心類如下:
- onPreExecute():主要用于做一些準備工作,在主線程中執(zhí)行異步任務執(zhí)行之前
- doInBackground(Params ... params):在線程池執(zhí)行,此方法用于執(zhí)行異步任務,params表示輸入的參數(shù),在此方法中可以通過publishProgress方法來更新任務進度,publishProgress會調用onProgressUpdate
- onProgressUpdate(Progress .. value):在主線程執(zhí)行,當任務執(zhí)行進度發(fā)生改變時會調用這個方法
- onPostExecute(Result result):在主線程執(zhí)行,異步任務之后執(zhí)行這個方法,result參數(shù)是返回值,即doInBackground的返回值。
2.2 AsyncTask的工作原理
2.3 HandleThread
HandleThread繼承自Thread,它是一種可以使用Handler的Thread,它的實現(xiàn)在run方法中調用Looper.prepare()來創(chuàng)建消息隊列然后通過Looper.loop()來開啟消息循環(huán),這樣在實際使用中就可以在HandleThread中創(chuàng)建Handler了。
@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
HandleThread和Thread的區(qū)別是什么?
- Thread的run方法中主要是用來執(zhí)行一個耗時任務;
- HandleThread在內部創(chuàng)建了一個消息隊列需要通過Handler的消息方式來通知HandleThread執(zhí)行一個具體的任務,HandlerThread的run方法是一個無限循環(huán)因此在不使用是調用quit或者quitSafely方法終止線程的執(zhí)行。HandleTread的具體使用場景是IntentService。
2.4 IntentService
IntentService繼承自Service并且是一個抽象的類因此使用它時就必須創(chuàng)建它的子類,IntentService可用于執(zhí)行后臺耗時的任務,當任務執(zhí)行完畢后就會自動停止。IntentService是一個服務因此它的優(yōu)先級要比線程高并且不容易被系統(tǒng)殺死,因此可以利用這個特點執(zhí)行一些高優(yōu)先級的后臺任務,它的實現(xiàn)主要是HandlerThread和Handler,這點可以從onCreate方法中了解。
//IntentService#onCreate @Override public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); }
當IntentService第一次被啟動時回調用onCreate方法,在onCreate方法中會創(chuàng)建HandlerThread,然后使用它的Looper創(chuàng)建一個Handler對象ServiceHandler,這樣通過mServiceHandler把消息發(fā)送到HandlerThread中執(zhí)行。每次啟動IntentService都會調用onStartCommand,IntentService在onStartCommand中會處理每個后臺任務的Intent。
//IntentService#onStartCommand @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } //IntentService#onStart @Override public void onStart(@Nullable Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); } private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); stopSelf(msg.arg1); } }
onStartCommand是如何處理外界的Intent的?
在onStartCommand方法中進入了onStart方法,在這個方法中IntentService通過mserviceHandler發(fā)送了一條消息,然后這個消息會在HandlerThread中被處理。mServiceHandler接收到消息后會把intent傳遞給onHandlerIntent(),這個intent跟啟動IntentService時的startService中的intent是一樣的,因此可以通過這個intent解析出啟動IntentService傳遞的參數(shù)是什么然后通過這些參數(shù)就可以區(qū)分具體的后臺任務,這樣onHandleIntent就可以對不同的后臺任務做處理了。當onHandleIntent方法執(zhí)行結束后IntentService就會通過stopSelf(int startId)方法來嘗試停止服務,這里不用stopSelf()的原因是因為這個方法被調用之后會立即停止服務但是這個時候可能還有其他消息未處理完畢,而采用stopSelf(int startId)方法則會等待所有消息都處理完畢后才會終止服務。調用stopSelf(int startId)終止服務時會根據(jù)startId判斷最近啟動的服務的startId是否相等,相等則立即終止服務否則不終止服務。
每執(zhí)行一個后臺任務就會啟動一次intentService,而IntentService內部則通過消息的方式向HandlerThread請求執(zhí)行任務,Handler中的Looper是順序處理消息的,這就意味著IntentService也是順序執(zhí)行后臺任務的,當有多個后臺任務同時存在時這些后臺任務會按照外界發(fā)起的順序排隊執(zhí)行。
3.Android中的線程池
線程池的優(yōu)點:
- 線程池中的線程可重復使用,避免因為線程的創(chuàng)建和銷毀帶來的性能開銷;
- 能有效控制線程池中的最大并發(fā)數(shù)避免大量的線程之間因互相搶占系統(tǒng)資源導致的阻塞現(xiàn)象;
- 能夠對線程進行簡單的管理并提供定時執(zhí)行以及指定間隔循環(huán)執(zhí)行等功能。
Android的線程池的概念來自于Java中的Executor,Executor是一個接口,真正的線程的實現(xiàn)是ThreadPoolExecutor,它提供了一些列參數(shù)來配置線程池,通過不同的參數(shù)可以創(chuàng)建不同的線程池。
3.1 ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
ThreadPoolExecutor是線程池的真正實現(xiàn),它的構造函數(shù)中提供了一系列參數(shù),先看一下每個參數(shù)的含義:
- corePoolSize:線程池的核心線程數(shù),默認情況下核心線程會在線程池中一直存活即使他們處于閑置狀態(tài)。如果將ThreadPoolExecutor的allowCoreThreadTimeOut置為true那么閑置的核心線程在等待新的任務到來時會有超時策略,超時時間由keepAliveTime指定,當?shù)却龝r間超過keepAliveTime設置的時間后核心線程就會被終止。
- maxinumPoolSize:線程池中所能容納的最大線程數(shù),當活動線程達到做大數(shù)量時后續(xù)的新任務就會被阻塞。
- keepAliveTime:非核心線程閑置時的超時時長,超過這個時長非核心線程就會被回收。
- unit:用于指定超時時間的單位,常用單位有毫秒、秒、分鐘等。
- workQueue:線程池中的任務隊列,通過線程池中的execute方法提交的Runnable對象會存儲在這個參數(shù)中。
- threadFactory:線程工廠,為線程池提供創(chuàng)建新的線程的功能。
- handler:這個參數(shù)不常用,當線程池無法執(zhí)行新的任務時,這可能是由于任務隊列已滿或者無法成功執(zhí)行任務,這個時候ThreadPoolExecutor會調用handler的rejectExecution方法來通知調用者。
ThreadPoolExecutor執(zhí)行任務時大致遵循如下規(guī)則:
如果線程池中的線程數(shù)量沒有達到核心線程的數(shù)量那么會直接啟動一個核心線程來執(zhí)行任務;
如果線程池中線程數(shù)量已經(jīng)達到或者超過核心線程的數(shù)量那么會把后續(xù)的任務插入到隊列中等待執(zhí)行;
如果任務隊列也無法插入那么在基本可以確定是隊列已滿這時如果線程池中的線程數(shù)量沒有達到最大值就會立刻創(chuàng)建非核心線程來執(zhí)行任務;
如果非核心線程的創(chuàng)建已經(jīng)達到或者超過線程池的最大數(shù)量那么就拒絕執(zhí)行此任務,同時ThreadPoolExecutor會通過RejectedExecutionHandler拋出異常rejectedExecution。
3.2線程池的分類
- FixedThreadPool:它是一種數(shù)量固定的線程池,當線程處于空閑狀態(tài)時也不會被回收,除非線程池被關閉。當所有的線程都處于活動狀態(tài)時,新任務都會處于等待狀態(tài),直到有空閑線程出來。FixedThreadPool只有核心線程并且不會被回收因此它可以更加快速的響應外界的請求。
- CacheThreadPool:它是一種線程數(shù)量不定的線程池且只有非核心線程,線程的最大數(shù)量是Integer.MAX_VALUE,當線程池中的線程都處于活動狀態(tài)時如果有新的任務進來就會創(chuàng)建一個新的線程去執(zhí)行任務,同時它還有超時機制,當一個線程閑置超過60秒時就會被回收。
- ScheduleThreadPool:它是一種擁有固定數(shù)量的核心線程和不固定數(shù)量的非核心線程的線程池,當非核心線程閑置時會立即被回收。
- SignleThreadExecutor:它是一種只有一個核心線程的線程池,所有任務都在同一個線程中按順序執(zhí)行。
總結
到此這篇關于Android中線程和線程池的文章就介紹到這了,更多相關Android線程和線程池內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
android-wheel控件實現(xiàn)三級聯(lián)動效果
這篇文章主要為大家詳細介紹了android-wheel控件實現(xiàn)三級聯(lián)動效果的代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10Android viewpager中動態(tài)添加view并實現(xiàn)偽無限循環(huán)的方法
這篇文章主要介紹了Android viewpager中動態(tài)添加view并實現(xiàn)偽無限循環(huán)的方法,涉及Android使用viewpager動態(tài)加載view及view無限循環(huán)顯示的相關技巧,需要的朋友可以參考下2016-01-01詳解Android?Flutter如何使用相機實現(xiàn)拍攝照片
在app中使用相機肯定是再平常不過的一項事情了,相機肯定涉及到了底層原生代碼的調用,那么在flutter中如何快速簡單的使用上相機的功能呢?一起來看看吧2023-04-04