App 啟動的時候 Android 系統自動啟動一個 Linux process 這個 process 包含一個 Thread 成為 UI Thread / Main Thread. 通常應用的所有組件都運行在這一個 process 中. 同時也可以通過修改四大組件在 AndroidManifest.xml 中的代碼塊<activity><service><provider><receiver>中的 android:process 屬性指定其運行在不同的 process 中 當一個組件在啟動的時候 如果該 process 已經存在了 那么該組件就直接通過這個 process 被啟動起來 并且運行在這個 process 的 UI Thread中
UI Thread 中運行著很多重要的邏輯: 系統事件處理(system Events) 用戶輸入事件處理(input Events) UI 繪制(Application) Service Alarm 我們編寫的代碼澤施穿插在這些邏輯的中間 比如對用戶觸摸時間的檢測和響應 對用戶輸入的處理 自定義 View 的繪制 如果我們插入的代碼比較耗時 比如網絡請求 或者數據庫讀取 那么就會阻塞 UI 線程其他的邏輯執行 從而導致界面卡頓 如果卡頓超過五秒 系統就回報 ANR 錯誤 所以耗時操作需要另起線程
獲取到數據之后 更新 UI 的時候 最好只在 UI 線程里面更新 子線程里面更新的方法(之所以子線程不能更新界面,是因為Android在線程的方法里面采用checkThread進行判斷是否是主線程,而這個方法是在ViewRootImpl中的,這個類是在onResume里面才生成的,因此,如果這個時候子線程在onCreate方法里面生成更新UI,而且沒有做阻塞,就是耗時多的操作,還是可以更新UI的。)
Android 提供了四種常用的操作多線程的方式:
Handler + Thread
AsyncTask
ThreadPoolExecutor
IntentService
Handler + Thread
Android 主線程包括一個消息隊列 (MessageQueue) 此消息隊列可以存入一系列的 message 或者 Runnable 對象 通過一個 handler 你可以往這個消息隊列中發送 Message 或者 Runnable對象 并且處理這些對象 每次創建一個新的 Handle 對象 他會綁定于創建她的線程(UI 線程)以及該線程的消息隊列 從這時起 這個 Handler 就會開始吧 Message 或者 Runnable 對象傳遞到消息隊列中 并在出隊列的時候執行他們 Handler 可以吧一個 Message 對象或者 Runnable 對象壓入消息隊列中 進而在 UI 線程中獲取 Message 或者執行 Runnable 對象 Handler 把消息壓入隊列有兩種方式 Post 和 SendMessage
post 方式:
post 允許把一個 Runnable 對象壓入到隊列消息中 它的方法有 post(Runnable) postAtTime(Runnable,long) postDelayed(Runnable,long)
對于 Handler 的 post 方式來說 他會傳遞一個 Runnable 對象到消息隊列中 在這個 Runnable 對象中 重寫run()方法 一般在這個 run() 方法中寫入需要在 UI 線程上的操作
new Handler().post(new Runnable() {
@Override
public void run() {
new ImageView(getContext()).setImageBitmap(new Bitmap());
}
});
sendMessage:
sendMessage 允許吧一個包含消息數據的 Message對象壓入到消息隊列中 她的方法有
sendEmptyMessage(int) sendMessage(Message) sendMessageAtTime(Message,long) sendMessageDelayed(Message,long)
Handler 如果使用 sendMessage 的方式吧消息壓入到消息隊列中 需要傳遞一個 Message 對象 而在 Handler 中 需要重寫 HandleMessage()方法 用于獲取工作線程傳遞過來的消息 此方法運行在 UI 線程上 Message 是一個 final 類 所以不可以被繼承
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1){
//更新 UI 代碼
}
}
};
這個時候或許有人會有疑問:更新 UI 不是只能在主線程中更新嗎 那么 Handler 是怎么回事兒呢?
更新UI只能在主線程中更新,子線程中操作是危險的. 這個時候,Handler就出現了來解決這個復雜的問題,由于Handler運行在主線程中(UI線程中),它與子線程可以通過Message對象來傳遞數據,這個時候,Handler就承擔著接受子線程傳過來的(子線程用sedMessage()方法傳弟)Message對象,(里面包含數據) , 把這些消息放入主線程隊列中,配合主線程進行更新UI。
——————————這一段來自 windroid之父 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/u013252711/article/details/37820719?utm_source=copy
優缺點:Handler 用法簡單明了 可以將多個異步任務更新 UI 的代碼放在一起 但是處理單個異步任務代碼顯得比較多
使用范圍:多個異步任務的更新 UI
AsyncTask
AsyncTask是Android 提供的輕量級異步類 可以直接繼承AsyncTask 在類中實現異步操作 并提供接口返回當前異步執行的成都(可以通過接口實現 UI 進度更新) 最后反饋執行的結果給 UI 主線程
AsyncTask通過阻塞 BlockingQuery(Runnable) 儲存待執行的任務 利用靜態線程池 THREAD_POOL_EXECUTOR 提供一定數量的線程 默認是128個 在 Android 3.0以前默認采取數據的是并行任務執行器 3.0以后改成了默認采用串行任務執行器 通過靜態串行任務執行器SERIAL_EXECUTOR 控制任務串行執行 循環取出任務給THREAD_POOL_EXECUTOR 中的線程執行 執行完一個 再執行下一個
class DownloadTask extends AsyncTask<Integer, Integer, String>{
// AsyncTask<Params, Progress, Result>
//后面尖括號內分別是參數(例子里是線程休息時間),進度(publishProgress用到),返回值類型
@Override
protected void onPreExecute() {
//第一個執行方法
super.onPreExecute();
}
@Override
protected String doInBackground(Integer... params) {
//第二個執行方法,onPreExecute()執行完后執行
for(int i=0;i<=100;i++){
publishProgress(i);
try {
Thread.sleep(params[0]);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "執行完畢";
}
@Override
protected void onProgressUpdate(Integer... progress) {
//這個函數在doInBackground調用publishProgress時觸發,雖然調用時只有一個參數
//但是這里取到的是一個數組,所以要用progesss[0]來取值
//第n個參數就用progress[n]來取值
tv.setText(progress[0]+"%");
super.onProgressUpdate(progress);
}
@Override
protected void onPostExecute(String result) {
//doInBackground返回時觸發,換句話說,就是doInBackground執行完后觸發
//這里的result就是上面doInBackground執行后的返回值,所以這里是"執行完畢"
setTitle(result);
super.onPostExecute(result);
}
}
優缺點:處理單個異步任務簡單 可以獲取到異步任務的進度 可以通過 cancel 方法取消還沒有執行完的AsyncTask 處理多個異步任務閑的代碼較多
適用范圍:單個異步任務的處理
ThreadPoolExecutor
ThreadPoolExecutor提供了一組線程池 可以管理多個線程并執行 這樣一方面減少了每個并行任務獨自建立線程的開銷 另一方面可以管理多個并發線程的公共資源 從而提高了多線程的小鹿 所以ThreadPoolExecutor比較適合一組任務的執行 Excutors 利用工廠模式對ThreadPoolExecutor進行了封裝 使用起來更加方便
Executors 提供了四種創建ExecutorService 的方法 她們的場景使用如下
Executors.newFixedThreadPool()
創建一個定長的線程池 提交一個任務就創建一個縣城 直到到達線程池的最大長度 這是線程會保持長度不再變化
Executors.newCachedThreadPool()
創建一個可緩存的線程池 如果當前線程池的長度超過了處理時所需要的 他可以靈活的收回空閑的線程 當需要增加時 他可以靈活的添加心的線程 而不會對線程池的長度做任何的限制
Executors.newScheduledThreadPool()
創建一個定長的線程池,而且支持定時的以及周期性的任務執行,類似于Timer
Executors.newSingleThreadExecutor()
創建一個單線程化的executor,它只創建唯一的worker線程來執行任務
使用范圍:批量處理任務
IntentService
ntentService繼承自Service,是一個經過包裝的輕量級的Service,用來接收并處理通過Intent傳遞的異步請求。客戶端通過調用startService(Intent)啟動一個IntentService,利用一個work線程依次處理順序過來的請求,處理完成后自動結束Service。 一個可以處理異步任務的簡單Service