android AsyncTask 的分析与运用
線程
在Android當中,通常將線程分為兩種,一種叫做Main Thread,除了Main Thread之外的線程都可稱為Worker Thread。
當一個應用程序運行的時候,Android操作系統(tǒng)就會給該應用程序啟動一個線程,這個線程就是我們的Main Thread,這個線程非常的重要,它主要用來加載我們的UI界面,完成系統(tǒng)和我們用戶之間的交互,并將交互后的結果又展示給我們用戶,所以Main Thread又被稱為UI Thread。
Android系統(tǒng)默認不會給我們的應用程序組件創(chuàng)建一個額外的線程,所有的這些組件默認都是在同一個線程中運行。然而,某些時候當我們的應用程序需要完成一個耗時的操作的時候,例如訪問網絡或者是對數據庫進行查詢時,此時我們的UI Thread就會被阻塞。例如,當我們點擊一個Button,然后希望其從網絡中獲取一些數據,如果此操作在UI Thread當中完成的話,當我們點擊Button的時候,UI線程就會處于阻塞的狀態(tài),此時,我們的系統(tǒng)不會調度任何其它的事件,更糟糕的是,當我們的整個現場如果阻塞時間超過5秒鐘(官方是這樣說的),這個時候就會出現 ANR (Application Not Responding)的現象,此時,應用程序會彈出一個框,讓用戶選擇是否退出該程序。對于Android開發(fā)來說,出現ANR的現象是絕對不能被允許的。
另外,由于我們的Android UI控件是線程不安全的,所以我們不能在UI Thread之外的線程當中對我們的UI控件進行操作。因此在Android的多線程編程當中,我們有兩條非常重要的原則必須要遵守:
線程間通信
既然在Android當中有兩條重要的原則要遵守,那么我們可能就有疑問了?我們既不能在主線程當中處理耗時的操作,又不能在工作線程中來訪問我們的UI控件,那么我們比如從網絡中要下載一張圖片,又怎么能將其更新到UI控件上呢?這就關系到了我們的主線程和工作線程之間的通信問題了。在Android當中,提供了兩種方式來解決線程直接的通信問題,一種是通過Handler的機制( 可以閱讀-關于Handler的理解),還有一種就是今天要詳細講解的 AsyncTask 機制。
AsyncTask
關于AsyncTask的解釋,Google上面是這樣說的:
AsyncTask enables proper and easy use of the UI thread. This class allows you to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called onPreExecute, doInBackground, onProgressUpdate and onPostExecute.
大概意思就是說“它使創(chuàng)建異步任務變得更加簡單,不再需要編寫任務線程和Handler實例即可完成相同的任務。一個異步任務通常是在后臺運行的計算等然后將結果發(fā)送到UI主線程中去。通常情況下,異步任務被定義為3種通用類型,分別為:參數、過程以及結果和4個步驟,分別為“onPreExecute、doInBackground、onProgressUpdate、onPostExecute””這就是關于異步任務的大概說明。
怎么來理解AsyncTask呢?通俗一點來說,AsyncTask就相當于Android給我們提供了一個多線程編程的一個框架,其介于Thread和Handler之間,我們如果要定義一個AsyncTask,就需要定義一個類來繼承AsyncTask這個抽象類,并實現其唯一的一個 doInBackgroud 抽象方法。要掌握AsyncTask,我們就必須要一個概念,總結起來就是: 3個泛型,4個步驟。
3個泛型
3個泛型指的是什么呢?我們來看看AsyncTask這個抽象類的定義,當我們定義一個類來繼承AsyncTask這個類的時候,我們需要為其指定3個泛型參數:
public abstract class AsyncTask<Params, Progress, Result>復制代碼Params: 這個泛型指定的是我們傳遞給異步任務執(zhí)行時的參數的類型
Progress: 這個泛型指定的是我們的異步任務在執(zhí)行的時候將執(zhí)行的進度返回給UI線程的參數的類型
Result: 這個泛型指定的異步任務執(zhí)行完后返回給UI線程的結果的類型
4個步驟
4個步驟:當我們執(zhí)行一個異步任務的時候,其需要按照下面的4個步驟分別執(zhí)行:
1、onPreExecute():
這個方法是在執(zhí)行異步任務之前的時候執(zhí)行,并且是在UI Thread當中執(zhí)行的,通常我們在這個方法里做一些UI控件的初始化的操作,例如彈出要給ProgressDialog。
2、doInBackground(Params... params):
在onPreExecute()方法執(zhí)行完之后,會馬上執(zhí)行這個方法,這個方法就是來處理異步任務的方法,Android操作系統(tǒng)會在后臺的線程池當中開啟一個worker thread來執(zhí)行我們的這個方法,所以這個方法是在worker thread當中執(zhí)行的,這個方法執(zhí)行完之后就可以將我們的執(zhí)行結果發(fā)送給我們的最后一個 onPostExecute 方法,在這個方法里,我們可以從網絡當中獲取數據等一些耗時的操作。
3、onProgressUpdate(Progess... values):
這個方法也是在UI Thread當中執(zhí)行的,我們在異步任務執(zhí)行的時候,有時候需要將執(zhí)行的進度返回給我們的UI界面,例如下載一張網絡圖片,我們需要時刻顯示其下載的進度,就可以使用這個方法來更新我們的進度。這個方法在調用之前,我們需要在 doInBackground 方法中調用一個 publishProgress(Progress) 的方法來將我們的進度時時刻刻傳遞給 onProgressUpdate 方法來更新。
4、onPostExecute(Result... result):
當我們的異步任務執(zhí)行完之后,就會將結果返回給這個方法,這個方法也是在UI Thread當中調用的,我們可以將返回的結果顯示在UI控件上。
為什么我們的AsyncTask抽象類只有一個 doInBackground 的抽象方法呢??原因是,我們如果要做一個異步任務,我們必須要為其開辟一個新的Thread,讓其完成一些操作,而在完成這個異步任務時,我可能并不需要彈出要給ProgressDialog,我并不需要隨時更新我的ProgressDialog的進度條,我也并不需要將結果更新給我們的UI界面,所以除了 doInBackground 方法之外的三個方法,都不是必須有的,因此我們必須要實現的方法是 doInBackground 方法。
實例演示
接下來我們通過下載一張網絡圖片進行演示對于AsyncTask的使用。首先來看下效果:
其次,我們來了解一些相關代碼。其實下載的代碼原理很簡單,就是通過流的方式轉為字節(jié)數組,然后再轉化為Bitmap而已。
//進度框顯示progressDialog = new ProgressDialog(MainActivity.this);progressDialog.setTitle("提示信息");progressDialog.setMessage("正在下載中,請稍后......");// 設置setCancelable(false); 表示我們不能取消這個彈出框,等下載完成之后再讓彈出框消失progressDialog.setCancelable(false);// 設置ProgressDialog樣式為水平的樣式progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//下載類 public class MyAsyncTask extends AsyncTask<String, Integer, Bitmap> {@Overrideprotected void onPreExecute() {super.onPreExecute();// 在onPreExecute()中我們讓ProgressDialog顯示出來progressDialog.show();}@Overrideprotected Bitmap doInBackground(String... params) {Bitmap bitmap = null;try {URL url = new URL(params[0]);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5 * 1000);conn.setRequestMethod("GET");InputStream inputStream = conn.getInputStream();if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {int fileLength = conn.getContentLength();ByteArrayOutputStream outStread = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int length = 0;long total = 0;while ((length = inputStream.read(buffer)) != -1) {outStread.write(buffer, 0, length);total += length;if (fileLength > 0) {publishProgress((int) (total * 100 / fileLength));}}outStread.close();inputStream.close();byte[] data = outStread.toByteArray();if (data != null) {bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);} else {Toast.makeText(MainActivity.this, "Image error!", Toast.LENGTH_LONG).show();}return bitmap;}} catch (Exception e) {e.printStackTrace();}return null;}@Overrideprotected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);// 更新ProgressDialog的進度條progressDialog.setProgress(values[0]);}@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);imageView.setImageBitmap(bitmap);try {saveFile(bitmap, "netpic.jpg");} catch (IOException e) {e.printStackTrace();}progressDialog.dismiss();}}//在UI主線程中執(zhí)行下載程序 String picUrl = "http://img3.imgtn.bdimg.com/it/u=2437337628,1430863508&fm=214&gp=0.jpg";new MyAsyncTask().execute(picUrl);復制代碼詳細代碼請查看github-easy-net封裝學習基本的網絡請求庫
到這里基本上就結束了。這就是簡單的運用AsyncTask進行UI線程和Work線程進行通信的基本方式。接下來我們就源碼進行深入的研究關于AsyncTask的相關內容。
源碼解讀(基于API25)
首先我們從異步任務的起點execute開始分析:
//<p>This method must be invoked on the UI thread. //必須在UI主線程中調用該方法。 @MainThreadpublic final AsyncTask<Params, Progress, Result> execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);}//跳轉到executeOnExecutor方法 @MainThreadpublic final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {if (mStatus != Status.PENDING) {switch (mStatus) {case RUNNING:throw new IllegalStateException("Cannot execute task:"+ " the task is already running.");case FINISHED:throw new IllegalStateException("Cannot execute task:"+ " the task has already been executed "+ "(a task can be executed only once)");}}//設置當前AsyncTask的狀態(tài)為RUNNINGmStatus = Status.RUNNING;//還是在UI主線程,這個時候可以進行一些初始化操作onPreExecute();mWorker.mParams = params;exec.execute(mFuture);return this;}復制代碼代碼比較簡單,其中出現了mWork和mFuture變量,接下來我們跟蹤這兩個變量進行研究。
1、對于mWork變量
private final WorkerRunnable<Params, Result> mWorker;private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {Params[] mParams;} //可以看到是Callable的子類,且包含一個mParams用于保存我們傳入的參數,接下來看看 mWork的初始化操作 //這是在AsyncTask的構造函數中進行初始化的 mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {//設置為true,下面要用到mTaskInvoked.set(true);Result result = null;try {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//noinspection unchecked //這就是我們使用到的4個方法中的一個,獲取處理結果result = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);throw tr;} finally {//發(fā)送執(zhí)行結果postResult(result);}return result;}};復制代碼從上面源碼我們可以分析出mWork在AsyncTask的構造函數中進行初始化,然后實現CallBack的call方法,進行一些設置,然后調用doInBackground方法,最后執(zhí)行postResult(result)進行結果處理,接下來我們繼續(xù)分析postResult(result)方法。
private Result postResult(Result result) {@SuppressWarnings("unchecked")Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));message.sendToTarget();return result;}復制代碼我們看到了熟悉的異步消息處理,Handler和Message,發(fā)送一個消息,
msg.what=MESSAGE_POST_RESULT; msg.obj=new AsyncTaskResult<Result>(this, result);復制代碼從上面的代碼我們可以知道,既然handler已經發(fā)送出了消息的話,,那么肯定會存在一個Handler,并在某處進行消息的處理,我們來繼續(xù)尋找一下這些內容:
//找到相關的Handler private static Handler getHandler() {synchronized (AsyncTask.class) {if (sHandler == null) {sHandler = new InternalHandler();}return sHandler;}}//消息處理 private static class InternalHandler extends Handler {public InternalHandler() {super(Looper.getMainLooper());}@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic void handleMessage(Message msg) {AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;switch (msg.what) {case MESSAGE_POST_RESULT:// There is only one resultresult.mTask.finish(result.mData[0]);break;case MESSAGE_POST_PROGRESS:result.mTask.onProgressUpdate(result.mData);break;}}}//消息處理完之后,設定狀態(tài)為finishedprivate void finish(Result result) {if (isCancelled()) {onCancelled(result);} else {//執(zhí)行4個方法中的最后一步,處理結果onPostExecute(result);}//設置最后的狀態(tài)為結束finishedmStatus = Status.FINISHED;}復制代碼2、對于mFuture變量
//申明變量 private final FutureTask<Result> mFuture;//在AsyncTask的構造函數中進行變量的初始化 mFuture = new FutureTask<Result>(mWorker) {@Overrideprotected void done() {try {postResultIfNotInvoked(get());} catch (InterruptedException e) {android.util.Log.w(LOG_TAG, e);} catch (ExecutionException e) {throw new RuntimeException("An error occurred while executing doInBackground()",e.getCause());} catch (CancellationException e) {postResultIfNotInvoked(null);}}};//查看postResultIfNotInvoked方法,參數是get(),get()表示獲取mWorker的call的返回值,即Result。private void postResultIfNotInvoked(Result result) {final boolean wasTaskInvoked = mTaskInvoked.get();if (!wasTaskInvoked) {postResult(result);}}//注意上面的mWork初始化時設置的變量值mTaskInvoked.set(true),所以判斷中一般都是wasTaskInvoked=true,所以基本不會執(zhí)行復制代碼分析完了mWork和mFuture這兩個變量,我們接著分析下面的代碼:
exec.execute(mFuture);
這個exec其實就是sDefaultExecutor,那么這個sDefaultExecutor是什么東西呢?
//sDefaultExecutor的定義 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;//繼續(xù)跟蹤SERIAL_EXECUTOR public static final Executor SERIAL_EXECUTOR = new SerialExecutor();//SerialExecutor的定義 private static class SerialExecutor implements Executor {//維護一個數組隊列final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();Runnable mActive;//執(zhí)行內容public synchronized void execute(final Runnable r) {//在隊列的尾部插入一個任務taskmTasks.offer(new Runnable() {public void run() {try {r.run();} finally {scheduleNext();}}});if (mActive == null) {scheduleNext();}}protected synchronized void scheduleNext() {//取出隊首的任務開始執(zhí)行if ((mActive = mTasks.poll()) != null) {//開始執(zhí)行任務THREAD_POOL_EXECUTOR.execute(mActive);}}}復制代碼那么這個THREAD_POOL_EXECUTOR又是什么東西呢?接著分析這個變量:
/*** An {@link Executor} that can be used to execute tasks in parallel.*/public static final Executor THREAD_POOL_EXECUTOR;//線程池配置static {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,sPoolWorkQueue, sThreadFactory);threadPoolExecutor.allowCoreThreadTimeOut(true);THREAD_POOL_EXECUTOR = threadPoolExecutor;}//變量設置private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();// We want at least 2 threads and at most 4 threads in the core pool,// preferring to have 1 less than the CPU count to avoid saturating// the CPU with background workprivate 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;復制代碼以上就是過程分析,接下來我們來進一步總結說明具體的流程。
首先設置當前AsyncTask的狀態(tài)為RUNNING,然后執(zhí)行了onPreExecute(),當前依然在UI線程,所以我們可以在其中做一些準備工作。其次將我們傳入的參數賦值給了mWorker.mParams ,mWorker為一個Callable的子類,且在內部的call()方法中,調用了doInBackground(mParams),然后得到的返回值作為postResult的參數進行執(zhí)行;postResult中通過sHandler發(fā)送消息,最終sHandler的handleMessage中完成onPostExecute的調用。最后執(zhí)行exec.execute(mFuture),mFuture為真正的執(zhí)行任務的單元,將mWorker進行封裝,然后由sDefaultExecutor交給線程池進行執(zhí)行。
這里面我們涉及到了4個方法中的三個,那么還有一個方法:
//更新進度 @Overrideprotected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);progressDialog.setProgress(values[0]);}復制代碼那么這個方法是什么時候調用的的呢?我們在使用AsyncTask中的第三個方法doInBackground時在里面我們調用了一個傳遞進度的方法 publishProgress(int progress),我們進入到該方法中查看一下:
//工作線程中執(zhí)行該方法 @WorkerThreadprotected final void publishProgress(Progress... values) {if (!isCancelled()) {//通過Handler和Message異步消息機制進行UI線程和Work線程通信getHandler().obtainMessage(MESSAGE_POST_PROGRESS,new AsyncTaskResult<Progress>(this, values)).sendToTarget();}}復制代碼publishProgress方法其實就是發(fā)送一個消息,
msg.what=MESSAGE_POST_PROGRESS//消息類型 msg.obj=new AsyncTaskResult<Progress>(this, values)//進度//處理消息 private static class InternalHandler extends Handler {public InternalHandler() {super(Looper.getMainLooper());}@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic void handleMessage(Message msg) {AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;switch (msg.what) {case MESSAGE_POST_RESULT:// There is only one resultresult.mTask.finish(result.mData[0]);break;case MESSAGE_POST_PROGRESS://處理進度消息//調用onProgressUpdate方法顯示進度result.mTask.onProgressUpdate(result.mData);break;}}}復制代碼這就很明朗了,四個方法都調用到了。以上便是AsyncTask所有的執(zhí)行流程,通過源碼分析可得AsyncTask的內部也是使用Handler+Message的方式進行消息傳遞和處理的。
關于AsyncTask的內幕
1、深入理解AsyncTask的內幕,線程池引發(fā)的重大問題
注意
Android6.0 谷歌把HttpClient相關的類移除了,所以如果繼續(xù)使用的話,需要添加相關的jar包。
1、對于AndroidStudio的添加方法是:
在相應的module下的build.gradle中加入: android {useLibrary 'org.apache.http.legacy' }復制代碼2、對于Eclipse的添加方法是:
libs中加入 org.apache.http.legacy.jar 上面的jar包在:**\android-sdk-windows\platforms\android-23\optional下(需要下載android 6.0的SDK)復制代碼參考鏈接
1、http://www.cnblogs.com/xiaoluo501395377/p/3430542.html
2、http://blog.csdn.net/liuhe688/article/details/6532519
3、http://blog.csdn.net/lmj623565791/article/details/38614699
總結
以上是生活随笔為你收集整理的android AsyncTask 的分析与运用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多线程消息监听容器配置[ 消费者spri
- 下一篇: Redis数据持久化