在Android應用的開發過程中,我們不可避免的要使用多線程,獲取服務器數據、下載網絡數據、遍歷文件目錄查找特定文件等等耗時的工作都離不開線程的知識。Android繼承了Java的多線程體系,同時又實現了許多更加簡易的API來操作線程。通過這些API,我們可以方便快捷的實現線程的創建、線程間的交互。我打算記下最近自己學習Android多線程機制時的學習筆記,一來可以供以后翻閱查看,二來為那些正疑惑與此的朋友提供一條解決問題的途徑。
先大致說一下我想寫的內容:
一、AsyncTask
二、Thread 與 Handler
三、HandlerThread
全部內容分為3篇Bolg。
下面正式開始第一部分吧!
一、AsyncTask
(0)參考文檔:http://developer.android.com/reference/android/os/AsyncTask.html
(1)簡單介紹:
AsyncTask這是一個很便捷的關於線程的API,它將比較耗時(幾秒)的工作放在非UI線程中運行,然后將結果的展示放在UI線程中。整個過程不涉及到任何關於Thread、Handler及Message等的處理。
先來說說AsyncTask的構造吧,它接收3個泛型參數,分別代表了后台工作的傳入參數類型、進度的參數類型、結果的參數類型。每一個的具體含義會在接下來的AsyncTask的執行過程中講到。要想使用AsyncTask就必須拓展為它的子類同時指定3個泛型參數,然后在子類的中重寫幾個重要的方法。下面就AsyncTask的執行過程來寫。
(2)AsyncTask的執行過程:
AsyncTask中的回調方法有:
onPreExecute(),UI線程中執行,一般用來處理執行異步任務前的准備工作,比如彈出一個進度條,展現一個對話框告知用戶正在執行一項任務。
doInBackground(Params...),在非UI線程中執行,這里是異步任務的核心,所有需要耗時的工作全在這里執行,因為不是在UI線程中,因此不會存在阻塞UI線程的危險,不會引發ANR錯誤。
onProgressUpdate(Progress...),在UI 線程中執行,一般用來更新任務執行的進度,它的調用時在publishProgress(Progress...)調用之后接着被調用的,我們可以在doInBackground(Params...)中多次調用publishProgress(Progress...)來告知用戶任務的執行狀態。
onPostExecute(Result),在UI線程中執行,任務完成后(doInBackground(Params...)執行結束)且任務沒被取消時,會執行該方法,用來處理異步任務得到的結果。
onCancelled(Result),在UI線程中執行,當任務以柔和的方式結束(AsyncTask.cancel(false))這個方法會取代onPostExecute(Result)得以執行(當然這里也要看程序的執行邏輯,有沒有在doInBackground(Params...)中檢測任務是否被取消)
關於泛型參數:
三個泛型參數分別是:Params,Progress,Result。在上面的回調方法接受中我們可以看到它們分別所處那個方法,Params是啟動異步任務時傳入的參數,比如啟動異步任務從網上下載一張圖片傳入圖片的URL地址。Progress是進度參數類型,調用publishProgress(Progress...)時的參數需和Progress類型相容。比如用一個整數去描述進度,Progress及時Integer。Result是結果參數類型,異步任務完成后,如需返回一個結果,那么這個結果的類型就是Result類型。特別地,可以用Void去代替任何一個或多個泛型參數,這樣做就以為着不需要參數。
(3)一個簡單的例子:
話說百聞不如一見,下面我就用一個極簡單的例子說明AsyncTask的使用過程。先說我要干啥,我用一個異步任務來更新一個進度條(簡單吧!粗暴吧!顫抖吧,地球人!O(∩_∩)O)
注:示例沒什么格式,強迫症患者見諒!!
先給出布局:
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:layout_margin="15dp"> 7 8 9 <TextView 10 android:layout_width="wrap_content" 11 android:layout_height="wrap_content" 12 android:layout_centerHorizontal="true" 13 android:text="AsyncTask示例" 14 android:textSize="30sp"/> 15 16 <ProgressBar 17 android:id="@+id/progress" 18 style="@android:style/Widget.ProgressBar.Horizontal" 19 android:layout_width="match_parent" 20 android:layout_height="wrap_content" 21 android:layout_centerInParent="true"/> 22 23 24 <Button 25 android:id="@+id/cancel" 26 android:layout_width="match_parent" 27 android:layout_height="wrap_content" 28 android:layout_alignParentBottom="true" 29 android:text="cancel"/> 30 31 <Button 32 android:id="@+id/start" 33 android:layout_width="match_parent" 34 android:layout_height="wrap_content" 35 android:layout_above="@+id/cancel" 36 android:text="start"/> 37 </RelativeLayout>
下面是AsyncTask的邏輯代碼:
1 package comfallblank.github.asynctasktest; 2 3 import android.os.AsyncTask; 4 import android.os.Bundle; 5 import android.support.v7.app.AppCompatActivity; 6 import android.util.Log; 7 import android.view.View; 8 import android.widget.Button; 9 import android.widget.ProgressBar; 10 11 public class MainActivity extends AppCompatActivity { 12 13 private ProgressBar mProgressBar; 14 private Button mStartButton; 15 private Button mCancelButton; 16 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.activity_main); 21 mProgressBar = (ProgressBar) findViewById(R.id.progress); 22 //設置ProgressBar暫時不可見,一會在異步任務的中顯示它。 23 mProgressBar.setVisibility(View.GONE); 24 25 final MyTask myTask = new MyTask(); 26 mStartButton = (Button) findViewById(R.id.start); 27 mStartButton.setOnClickListener(new View.OnClickListener() { 28 @Override 29 public void onClick(View view) { 30 myTask.execute(); 31 } 32 }); 33 34 mCancelButton = (Button) findViewById(R.id.cancel); 35 mCancelButton.setOnClickListener(new View.OnClickListener() { 36 @Override 37 public void onClick(View view) { 38 myTask.cancel(false); 39 } 40 }); 41 42 43 } 44 45 private class MyTask extends AsyncTask<Void,Integer,String>{ 46 47 private static final String TAG = "MyTask"; 48 49 @Override 50 protected void onPreExecute() { 51 super.onPreExecute(); 52 //准備工作,顯示進度條 53 mProgressBar.setVisibility(View.VISIBLE); 54 //打印日志,查看執行路徑,下同 55 Log.d(TAG,"onPreExecute"); 56 } 57 58 @Override 59 protected String doInBackground(Void... voids) { 60 Log.d(TAG,"doInBackground"); 61 //每1s對進度條更新10% 62 synchronized (this){ 63 for (int i = 1;i<=10;i++){ 64 try { 65 Thread.sleep(1000*1); 66 publishProgress(i*10); 67 if (isCancelled()){ 68 return "Task cancelled"; 69 } 70 } catch (InterruptedException e) { 71 e.printStackTrace(); 72 } 73 74 } 75 } 76 return "Task executed"; 77 } 78 79 @Override 80 protected void onProgressUpdate(Integer... values) { 81 super.onProgressUpdate(values); 82 mProgressBar.setProgress(values[0]); 83 Log.d(TAG,"onProgressUpdate"); 84 } 85 86 @Override 87 protected void onPostExecute(String s) { 88 super.onPostExecute(s); 89 Log.d(TAG, s); 90 Log.d(TAG,"onPostExecute"); 91 92 } 93 94 @Override 95 protected void onCancelled(String s) { 96 super.onCancelled(s); 97 Log.d(TAG, s); 98 Log.d(TAG, "onCancelled"); 99 } 100 } 101 }
LogCat日志:
執行完畢,沒有中途取消任務:
10-06 13:55:05.871 6737-6737/comfallblank.github.asynctasktest D/MyTask: onPreExecute
10-06 13:55:05.891 6737-13040/comfallblank.github.asynctasktest D/MyTask: doInBackground
10-06 13:55:06.922 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:07.923 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:08.914 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:09.925 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:09.975 6737-6753/comfallblank.github.asynctasktest W/art: Suspending all threads took: 18.035ms
10-06 13:55:10.926 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:11.937 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:12.938 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:13.939 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:14.950 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:15.951 6737-6737/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:55:15.971 6737-6737/comfallblank.github.asynctasktest D/MyTask: Task executed
10-06 13:55:15.971 6737-6737/comfallblank.github.asynctasktest D/MyTask: onPostExecute
中途取消任務:
10-06 13:56:10.644 14129-14129/comfallblank.github.asynctasktest D/MyTask: onPreExecute
10-06 13:56:10.654 14129-14302/comfallblank.github.asynctasktest D/MyTask: doInBackground
10-06 13:56:11.665 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:12.666 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:13.667 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:14.668 14129-14129/comfallblank.github.asynctasktest D/MyTask: onProgressUpdate
10-06 13:56:15.689 14129-14129/comfallblank.github.asynctasktest D/MyTask: Task cancelled
10-06 13:56:15.689 14129-14129/comfallblank.github.asynctasktest D/MyTask: onCancelled
關於AsyncTask的一些注意事項:
(1)AsyncTask對象只能使用一次,也就是說一個對象執行完畢或者中途被cancel后,不能再次調用execute().
(2)AsyncTask.execute()只能在主線程中調用。
(3)AsyncTask不能處理並發,多個AsyncTask也是有執行順序的,且同一個應用的所有AsyncTask都是在同一進程中執行,按照隊列排序依次執行。
(4)AsyncTask只能處理那些耗時不是特別長的任務,對於需要長時間執行的任務最好自己實現Thread。
AsyncTask很方便,它屏蔽了所有Thread、Handler、Looper、Message及Messagequene的細節,這或許就是封裝的魅力吧!但當你真正明白了Android的線程通訊之后你就會明白那些良好設計帶來的美感。
以此拋磚引玉!可能多有偏頗,望多交流!