android多執行緒開發
目錄
1.多執行緒開發
1)多執行緒開發目的
當我們應用啟動時候,系統會為我們建立一個主執行緒。主執行緒可以向我們UI控制元件分發事件以及繪製頁面等事件。所以我們也常稱之為UI執行緒。
開發的時候要避免在UI執行緒做一些耗時的操作,因為會阻塞UI執行緒,導致停止事件的分發,此時的介面就像是卡住一般。更嚴重的情況,如果耗時操作時間大於5S,程式還會ANR,此時系統會彈窗提示我們程式無響應。
還有2種情況,不過這2種ANR並不會彈窗提示,僅是輸出log而已:
- 主執行緒在執行BroadcastReceiver的onReceive函式時10秒內沒有執行完畢
BroadcastReceiver(簡稱BR)的onReceive函式執行在主執行緒中,當這個函式超過10秒鐘沒有返回就會觸發ANR
- 主執行緒在執行Service的各個生命週期函式時20秒內沒有執行完畢
Service的各個生命週期函式也執行在主執行緒中,當這些函式超過20秒鐘沒有返回就會觸發ANR。
因此,將耗時操作放到非UI執行緒中顯得尤為重要,後面說一些開發中常見的工作執行緒。
2)多執行緒開發注意點
不要在非UI執行緒更新UI元件,可以嘗試以下方式:
a、Activity.runOnUiThread(Runnable)
b、View.post(Runnable) View.postDelayed(Runnable, long)
c、handler
2.工作執行緒型別
1)Thread
new Thread(new Runnable() { public void run() { final Bitmap bitmap = 網路下載的圖片; //工作執行緒切換至UI執行緒更新UI mImageView.post(new Runnable() { public void run() { mImageView.setImageBitmap(bitmap); } }); } }).start();
2)intentService
特點:intentService處理完耗時操作自動銷燬自身。但是頁面在網路訂閱事件完成前退出,還是需要手動銷燬訂閱事件。
intentService:內部開啟了一個Worker Thread(工作執行緒)處理佇列中的Intent,處理完成一個之後在處理第二個,每個請求都會在單獨的Worker Thread中處理
public class MyIntentService extends IntentService {
//構造方法 一定要實現此方法否則Service執行出錯。
public MyIntentService() {
super("MyIntentService");
}
// onHandleIntent方法-耗時操作的處理
@Override
protected void onHandleIntent(Intent intent) {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("MyIntentService","睡眠結束");
}
}
//FIXME 多次啟動新任務--發現第一個執行完才會去執行第二個任務
startService(new Intent(this,MyIntentService.class));
startService(new Intent(this,MyIntentService.class));
3)handlerThread
handlerThread和intentService類似,都會處理完上個任務,再去處理下個任務。
4)AsyncTask
AsyncTask可以序列或者並行的去執行後臺任務。內部封裝了Handler,4個重要的方法,除了doInBackGround方法是在工作執行緒執行,其餘的都是在主執行緒執行,省去了執行緒切換去更新UI的操作。
5)執行緒池
執行緒池對執行緒做了統一的管理,不需要我們來一個耗時任務就開一個執行緒,這樣會造成不必要的開銷。我們可以根據業務需求選擇合適的執行緒池型別去實現。常見的幾種執行緒池如下:
a、FixedTreadPool:
執行緒數量固定的執行緒池,僅有核心執行緒且沒有超時策略,所以執行緒不會被回收。這意味著它能夠快速的響應外界請求。
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(runnable);
executorService.execute(runable1);
executorService.execute(runable2);
executorService.execute(runable3);
上面程式碼含義為同時有2個核心執行緒處理任務,當其中一個任務執行完畢才會去執行下一個。
b、CachedThreadPool:
是一種執行緒數量不定的執行緒池,只有非核心執行緒,可以簡單理解為最大執行緒數是無限大的。CachedThreadPool的任務佇列相當於一個空集合,這導致任何任務都會被立即執行,比較適合做一些大量的耗時較少的任務。
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(runnable);
executorService.execute(runable1);
executorService.execute(runable2);
executorService.execute(runable3);
上面程式碼含義為同時執行4個任務,但是順序不一定和執行的順序一樣。
c、ScheduledThreadPool.Scheduled:
核心執行緒池的數量書固定的且非核心執行緒池是沒有限制的,非核心執行緒池被閒置時會被立即回收。
主要用於執行定時任務和具有固定週期的重複任務。
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.schedule(runnable, 2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(runable1, 2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(runable2, 2000, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(runable3, 2000, TimeUnit.MILLISECONDS);
上面程式碼含義為延時2S執行任務
scheduledExecutorService.scheduleAtFixedRate(runnable,1000,1000,TimeUnit.MILLISECONDS);
上面程式碼含義為延遲1S,然後每隔1S執行一次runnable的任務
d、SingleThreadExecutor:
只有一個核心執行緒,所有的任務都是在一個執行緒裡順序執行。
ExecutorService executorService=Executors.newSingleThreadExecutor();
executorService.execute(runnable);
e、4種常見執行緒池的配置
Android中常用的執行緒池都是通過對ThreadPoolExecutor進行不同配置來實現的。舉個例子:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
看下ThreadPoolExecutor的這個引數最多的構造方法(有4個):
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize 執行緒池中核心執行緒的數量
maximumPoolSize 執行緒池中最大執行緒數量
keepAliveTime 非核心執行緒的超時時長,當系統中非核心執行緒閒置時間超過keepAliveTime之後,則會被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut屬性設定為true,則該引數也表示核心執行緒的超時時長
unit 第三個引數的單位,有納秒、微秒、毫秒、秒、分、時、天等
workQueue 執行緒池中的任務佇列,該佇列主要用來儲存已經被提交但是尚未執行的任務。儲存在這裡的任務是由ThreadPoolExecutor的execute方法提交來的。
threadFactory 為執行緒池提供建立新執行緒的功能,這個我們一般使用預設即可
handler 拒絕策略,當執行緒無法執行新任務時(一般是由於執行緒池中的執行緒數量已經達到最大數或者執行緒池關閉導致的),預設情況下,當執行緒池無法處理新執行緒時,會丟擲一個RejectedExecutionException。
f、執行緒池常見方法
ExecutorService.shutdown():阻止新來的任務提交,對已經提交了的任務不會產生任何影響。通過將執行緒池的狀態改成SHUTDOWN,當再將執行execute提交任務時,如果測試到狀態不為RUNNING,則丟擲rejectedExecution,從而達到阻止新任務提交的目的。
ExecutorService.shutdownNow():阻止新來的任務提交,同時會中斷當前正在執行的執行緒,即workers中的執行緒。另外它還將workQueue中的任務給移除,並將這些任務新增到列表中進行返回。通過將執行緒池的狀態改成STOP,當再將執行execute提交任務時,如果測試到狀態不為RUNNING,則丟擲rejectedExecution,從而達到阻止新任務提交的目的.
ExecutorService.awaitTermination(long timeout, TimeUnit unit):這個方法有兩個引數,一個是timeout即超時時間,另一個是unit即時間單位。這個方法會使執行緒等待timeout時長,當超過timeout時間後,會監測ExecutorService是否已經關閉,若關閉則返回true,否則返回false。一般情況下會和shutdown方法組合使用。例如:
ExecutorService.isTerminated:當呼叫shutdown()方法後,並且所有提交的任務完成後返回為true,這個方法會校驗ExecutorService當前的狀態是否為“TERMINATED”即關閉狀態,當為“TERMINATED”時返回true否則返回false。
ExecutorService service = Executors.newFixedThreadPool(3);
service.submit(runnbale);
service.submit(runnbale1);
service.submit(runnbale2);
service.shutdown();
System.out.println(service.isTerminated());
日誌顯示為false,即沒有關閉,因為要等待當前任務都執行完,才會返回false。
ExecutorService.iisShutdown:當呼叫shutdown()方法後返回為true。判斷是否為阻止新任務接收的狀態SHUTDOWN。
ExecutorService.submit:與execute執行不同,submit的提交是帶返回值的,會將Future返回。