****一般來說,一個應用至少有一個進程,一個進程至少有一個線程。線程是CPU調度的基本單位,進程是系統資源分配的基本單位。**
**進程擁有獨占的內存資源,一個進程可以看作一個JVM一個進程崩潰后,一般不會影響保護模式下的其他進程。同一進程中的線程共享內存資源,一個線程的死亡導致整個進程的死亡。****
Android開發四種常用的多線程實現方式:
- AsyncTask
- 異步消息機制
- IntentService
- ThreadPoolExcutor
1.AsyncTask
Android AsyncTask 類,它是封裝好的線程池,操作 UI 線程極其方便。
AsyncTask 的三個泛型參數:
public abstract class AsyncTask<Params, Progress, Result>
- Params ,傳入參數類型,即 doInBackground() 方法中的參數類型;
- Progress,異步任務執行過程中返回的任務執行進度類型,
即 publishProgress() 和onProgressUpdate() 方法中傳入的參數類型;
- Result,異步任務執行完返回的結果類型\,即 doInBackground() 方法中返回值的類型。
四個回調方法:
- onPreExecute(),在主線程執行,做一些準備工作。
- doInBackground(),在線程池中執行,該方法是抽象方法,在此方法中可以調用 publishProgress() 更新任務進度。
- onProgressUpdate(),在主線程中執行,在 publishProgress() 調用之后被回調,展示任務進度。
- onPostExecute(),在主線程中執行,異步任務結束后,回調此方法,處理返回結果。
注意:
- 當AsyncTask 任務被取消時,回調 onCanceled(obj) ,此時onPostExecute(),不會被調用,AsyncTask 中的 cancel()方法并不是真正去取消任務,只是設置這個任務為取消狀態,需要在 doInBackground() 中通過 isCancelled()判斷終止任務。
- AsyncTask 必須在主線程中創建實例,execute() 方法也必須在主線程中調用。
- 每個 AsyncTask實例只能執行一execute() ,多次執行會報錯,如需執行多次,則需創建多個實例。
- Android 3.0 之后,AsyncTask 對象默認執行多任務是串行執行,即 mAsyncTask.execute() ,并發執行的話需要使用executeOnExecutor()。
- AsyncTask 用的是線程池機制和異步消息機制(基于 ThreadPoolExecutor和 Handler )。Android 2.3 以前,AsyncTask 線程池容量是 128 ,全局線程池只有 5 個工作線程,如果運用 AsyncTask 對象來執行多個并發異步任務,那么同一時間最多只能有 5 個線程同時運行,其他線程將被阻塞。
- Android 3.0之后 Google 又進行了調整,新增接口 executeOnExecutor() ,允許自定義線程池(那么核心線程數以及線程容量也可自定義),并提供了 SERIAL_EXECUTOR 和THREAD_POOL_EXECUTOR 預定義線程池。后來 Google 又做了一些調整(任何事物都不完美),將線程池的容量與 CPU的核心數聯系起來,如目前 SDK 25 版本中,預定義的核心線程數量最少有 2 個,最多 4 個,線程池容量范圍 5 ~ 9 。
改動如下:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
2. 異步消息機制
異步消息機制的三大主角: Handler ,Message 和 Looper 。
Looper 負責創建 MessageQueue 消息對列,然后進入一個無限 for 循環中,不斷地從消息隊列中取消息,如果消息隊列為空,當前線程阻塞,Handler 負責向消息隊列中發送消息。
Looper
Looper 有兩個重要的方法: prepare() 和 loop()。
prepare() , Looper 與當前線程綁定,一個線程只能有一個 Looper 實例和一個 MessageQueue 實例。
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true)); 保證 Looper 對象在當前線程唯一
}
// Looper 的構造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
loop ,進入一個無限 for 循環體中,不斷地從消息隊列中取消息,然后交給消息的 target 屬性的 dispatchMessage 方法去處理。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// 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();
// 無限循環體,有沒有想過在 UI 線程里,有這樣一個死循環,為什么界面沒卡死??
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
Handler
Handler 負責向消息隊列中發送消息。
在 Activity 中我們直接可以 new Handler ,那是因為在 Activity 的啟動代碼中,已經在當前 UI 線程中調用了 Looper.prepare() 和 Looper.loop() 方法。
在子線程中 new Handler 必須要在當前線程(子線程)中創建好 Looper 對象和消息隊列,代碼如下
//在子線程中
Looper.prepare();
handler = new Handler() {
public void handleMessage(Message msg) {
//處理消息
};
};
Looper.loop();
之后,你拿著這個 Handler 對象就可以在其他線程中,往這個子線程的消息隊列中發消息了。
HandlerThread
HandlerThread 可以看作在子線程中創建一個異步消息處理機制的簡化版,HandlerThread 對象自動幫我們在工作線程里創建 Looper 對象和消息隊列。
使用方法:
mHandlerThread = new HandlerThread("MyHandlerThread");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//處理消息
}
};
之后你就可以使用 Handler 對象往工作線程中的消息隊列中發消息了。
看一下源碼片段:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
}
注意:handler 在 UI 線程中初始化的,looper 在一個子線程中執行,我們必須等 mLooper 創建完成之后,才能調用 getLooper ,源碼中是通過 wait 和 notify 解決兩個線程的同步問題。
3. IntentService
IntentService 可以看成是 Service 和 HandlerThread 的合體。它繼承自 Service ,并可以處理異步請求,其內部有一個 WorkerThread 來處理異步任務,當任務執行完畢后,IntentService 自動停止。
如果多次啟動 IntentService 呢? 看到 HandlerThread ,你就應該想到多次啟動 IntentService ,就是將多個異步任務放到任務隊列里面,然后在 onHandlerIntent 回調方法中串行執行,執行完畢后自動結束。
下面對源碼進行簡單的解析,IntentService 源碼:
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
//onHandleIntent 方法在工作線程中執行,執行完調用 stopSelf() 結束服務。
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentService(String name) {
super();
mName = name;
}
/**
* enabled == true 時,如果任務沒有執行完,當前進程就死掉了,那么系統就會令當前進程重啟。
* 任務會被重新執行。
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
// 上面已經講過,HandlerThread 對象 start 之后,會在工作線程里創建消息隊列 和 Looper 對象。
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
// 獲得 Looper 對象初始化 Handler 對象。
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
// IntentService 每次啟動都會往工作線程消息隊列中添加消息,不會創建新的線程。
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
// 官方建議 IntentService onStartCommand 方法不應該被重寫,注意該方法會調用 onStart 。
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
//服務停止會清除消息隊列中的消息,除了當前執行的任務外,后續的任務不會被執行。
mServiceLooper.quit();
}
/**
* 不建議通過 bind 啟動 IntentService ,如果通過 bind 啟動 IntentService ,那么 onHandlerIntent 方法不會被回調。Activity 與 IntentService 之間的通信一般采用廣播的方式。
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
/**
* 子類必須要實現,執行具體的異步任務邏輯,由 IntentService 自動回調。
*/
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
IntentService 源碼很容易理解,你也可以就自己的應用場景封裝自己的 IntentService 。
幾種場景:
- 正常情況下,啟動 IntentService ,任務完成,服務停止;
- 異步任務完成前,停止 IntentService,服務停止,但任務還會執行完成,完成后,工作線程結束;
- 多次啟動 IntentService,任務會被一次串行執行,執行結束后,服務停止;
- 多次啟動 IntentService ,在所有任務執行結束之前,停止 IntentService ,服務停止,除了當前執行的任務外,后續的任務不會被執行;
4. ThreadPoolExcutor
ThreadPool
用來管理一組工作線程,任務隊列( BlockingQueue )中持有的任務等待著被線程池中的空閑線程執行。
常用構造方法:
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue
);
- corePoolSize 核心線程池容量,即線程池中所維持線程的最低數量。corePoolSize 初始值為 0,當有新任務加入到任務隊列中,新的線程將被創建,這個時候即使線程池中存在空閑線程,只要當前線程數小于 corePoolSize,那么新的線程依然被創建。
- maximumPoolSize 線程池中所維持線程的最大數量。
- keepAliveTime 空閑線程在沒有新任務到來時的存活時間。
- unit 參數 keepAliveTime 的時間單位。
- workQueue 任務隊列,必須是 BlockingQueue 。
簡單使用
創建 ThreadFactory ,當然也可以自定義。
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
創建 ThreadPoolExecutor 。
// 根據 CPU 核心數確定線程池容量。
public static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
mThreadPoolExecutor = new ThreadPoolExecutor(
NUMBER_OF_CORES * 2,
NUMBER_OF_CORES * 2 + 1,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
backgroundPriorityThreadFactory
);
執行。
mThreadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
//do something
}
});
Future future = mThreadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
//do something
}
});
//任務可取消
future.cancel(true);
Future<Integer> futureInt = mThreadPoolExecutor.submit(new Callable<Integer>() {
@override
public Integer call() throws Exception {
return 0;
}
});
//獲取執行結果
futureInt.get();
FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>(){
@override
public Integer call() throws Exception {
return 0;
}
});
mThreadPoolExecutor.submit(task);
task.get();