在Android當中,一般將線程分爲兩種,一種叫作Main Thread,除了Main Thread以外的線程均可稱爲Worker Thread。javascript
當一個應用程序運行的時候,Android操做系統就會給該應用程序啓動一個線程,這個線程就是咱們的Main Thread,這個線程很是的重要,它主要用來加載咱們的UI界面,完成系統和咱們用戶之間的交互,並將交互後的結果又展現給咱們用戶,因此Main Thread又被稱爲UI Thread。html
Android系統默認不會給咱們的應用程序組件建立一個額外的線程,全部的這些組件默認都是在同一個線程中運行。然而,某些時候當咱們的應用程序須要完成一個耗時的操做的時候,例如訪問網絡或者是對數據庫進行查詢時,此時咱們的UI Thread就會被阻塞。例如,當咱們點擊一個Button,而後但願其從網絡中獲取一些數據,若是此操做在UI Thread當中完成的話,當咱們點擊Button的時候,UI線程就會處於阻塞的狀態,此時,咱們的系統不會調度任何其它的事件,更糟糕的是,當咱們的整個現場若是阻塞時間超過5秒鐘(官方是這樣說的),這個時候就會出現 ANR (Application Not Responding)的現象,此時,應用程序會彈出一個框,讓用戶選擇是否退出該程序。對於Android開發來講,出現ANR的現象是絕對不能被容許的。java
另外,因爲咱們的Android UI控件是線程不安全的,因此咱們不能在UI Thread以外的線程當中對咱們的UI控件進行操做。所以在Android的多線程編程當中,咱們有兩條很是重要的原則必需要遵照:android
既然在Android當中有兩條重要的原則要遵照,那麼咱們可能就有疑問了?咱們既不能在主線程當中處理耗時的操做,又不能在工做線程中來訪問咱們的UI控件,那麼咱們好比從網絡中要下載一張圖片,又怎麼能將其更新到UI控件上呢?這就關係到了咱們的主線程和工做線程之間的通訊問題了。在Android當中,提供了兩種方式來解決線程直接的通訊問題,一種是經過Handler的機制( 能夠閱讀-關於Handler的理解),還有一種就是今天要詳細講解的 AsyncTask 機制。git
關於AsyncTask的解釋,Google上面是這樣說的:github
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.apache
大概意思就是說「它使建立異步任務變得更加簡單,再也不須要編寫任務線程和Handler實例便可完成相同的任務。一個異步任務一般是在後臺運行的計算等而後將結果發送到UI主線程中去。一般狀況下,異步任務被定義爲3種通用類型,分別爲:參數、過程以及結果和4個步驟,分別爲「onPreExecute、doInBackground、onProgressUpdate、onPostExecute」」這就是關於異步任務的大概說明。編程
怎麼來理解AsyncTask呢?通俗一點來講,AsyncTask就至關於Android給咱們提供了一個多線程編程的一個框架,其介於Thread和Handler之間,咱們若是要定義一個AsyncTask,就須要定義一個類來繼承AsyncTask這個抽象類,並實現其惟一的一個 doInBackgroud 抽象方法。要掌握AsyncTask,咱們就必需要一個概念,總結起來就是: 3個泛型,4個步驟。windows
3個泛型指的是什麼呢?咱們來看看AsyncTask這個抽象類的定義,當咱們定義一個類來繼承AsyncTask這個類的時候,咱們須要爲其指定3個泛型參數:
public abstract class AsyncTask<Params, Progress, Result>複製代碼
Params: 這個泛型指定的是咱們傳遞給異步任務執行時的參數的類型
Progress: 這個泛型指定的是咱們的異步任務在執行的時候將執行的進度返回給UI線程的參數的類型
Result: 這個泛型指定的異步任務執行完後返回給UI線程的結果的類型
4個步驟:當咱們執行一個異步任務的時候,其須要按照下面的4個步驟分別執行:
一、onPreExecute():
這個方法是在執行異步任務以前的時候執行,而且是在UI Thread當中執行的,一般咱們在這個方法裏作一些UI控件的初始化的操做,例如彈出要給ProgressDialog。
二、doInBackground(Params... params):
在onPreExecute()方法執行完以後,會立刻執行這個方法,這個方法就是來處理異步任務的方法,Android操做系統會在後臺的線程池當中開啓一個worker thread來執行咱們的這個方法,因此這個方法是在worker thread當中執行的,這個方法執行完以後就能夠將咱們的執行結果發送給咱們的最後一個 onPostExecute 方法,在這個方法裏,咱們能夠從網絡當中獲取數據等一些耗時的操做。
三、onProgressUpdate(Progess... values):
這個方法也是在UI Thread當中執行的,咱們在異步任務執行的時候,有時候須要將執行的進度返回給咱們的UI界面,例以下載一張網絡圖片,咱們須要時刻顯示其下載的進度,就可使用這個方法來更新咱們的進度。這個方法在調用以前,咱們須要在 doInBackground 方法中調用一個 publishProgress(Progress) 的方法來將咱們的進度時時刻刻傳遞給 onProgressUpdate 方法來更新。
四、onPostExecute(Result... result):
當咱們的異步任務執行完以後,就會將結果返回給這個方法,這個方法也是在UI Thread當中調用的,咱們能夠將返回的結果顯示在UI控件上。
爲何咱們的AsyncTask抽象類只有一個 doInBackground 的抽象方法呢??緣由是,咱們若是要作一個異步任務,咱們必需要爲其開闢一個新的Thread,讓其完成一些操做,而在完成這個異步任務時,我可能並不須要彈出要給ProgressDialog,我並不須要隨時更新個人ProgressDialog的進度條,我也並不須要將結果更新給咱們的UI界面,因此除了 doInBackground 方法以外的三個方法,都不是必須有的,所以咱們必需要實現的方法是 doInBackground 方法。
接下來咱們經過下載一張網絡圖片進行演示對於AsyncTask的使用。首先來看下效果:
其次,咱們來了解一些相關代碼。其實下載的代碼原理很簡單,就是經過流的方式轉爲字節數組,而後再轉化爲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> {
@Override
protected void onPreExecute() {
super.onPreExecute();
// 在onPreExecute()中咱們讓ProgressDialog顯示出來
progressDialog.show();
}
@Override
protected 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;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// 更新ProgressDialog的進度條
progressDialog.setProgress(values[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
imageView.setImageBitmap(bitmap);
try {
saveFile(bitmap, "netpic.jpg");
} catch (IOException e) {
e.printStackTrace();
}
progressDialog.dismiss();
}
}
//在UI主線程中執行下載程序
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的相關內容。
首先咱們從異步任務的起點execute開始分析:
//<p>This method must be invoked on the UI thread.
//必須在UI主線程中調用該方法。
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
//跳轉到executeOnExecutor方法
@MainThread
public 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的狀態爲RUNNING
mStatus = Status.RUNNING;
//仍是在UI主線程,這個時候能夠進行一些初始化操做
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}複製代碼
代碼比較簡單,其中出現了mWork和mFuture變量,接下來咱們跟蹤這兩個變量進行研究。
一、對於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 {
//發送執行結果
postResult(result);
}
return result;
}
};複製代碼
從上面源碼咱們能夠分析出mWork在AsyncTask的構造函數中進行初始化,而後實現CallBack的call方法,進行一些設置,而後調用doInBackground方法,最後執行postResult(result)進行結果處理,接下來咱們繼續分析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,發送一個消息,
msg.what=MESSAGE_POST_RESULT;
msg.obj=new AsyncTaskResult<Result>(this, result);複製代碼
從上面的代碼咱們能夠知道,既然handler已經發送出了消息的話,,那麼確定會存在一個Handler,並在某處進行消息的處理,咱們來繼續尋找一下這些內容:
//找到相關的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"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
//消息處理完以後,設定狀態爲finished
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
//執行4個方法中的最後一步,處理結果
onPostExecute(result);
}
//設置最後的狀態爲結束finished
mStatus = Status.FINISHED;
}複製代碼
二、對於mFuture變量
//申明變量
private final FutureTask<Result> mFuture;
//在AsyncTask的構造函數中進行變量的初始化
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected 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,因此基本不會執行複製代碼
分析完了mWork和mFuture這兩個變量,咱們接着分析下面的代碼:
exec.execute(mFuture);
這個exec其實就是sDefaultExecutor,那麼這個sDefaultExecutor是什麼東西呢?
//sDefaultExecutor的定義
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//繼續跟蹤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;
//執行內容
public synchronized void execute(final Runnable r) {
//在隊列的尾部插入一個任務task
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//取出隊首的任務開始執行
if ((mActive = mTasks.poll()) != null) {
//開始執行任務
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 work
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;複製代碼
以上就是過程分析,接下來咱們來進一步總結說明具體的流程。
首先設置當前AsyncTask的狀態爲RUNNING,而後執行了onPreExecute(),當前依然在UI線程,因此咱們能夠在其中作一些準備工做。其次將咱們傳入的參數賦值給了mWorker.mParams ,mWorker爲一個Callable的子類,且在內部的call()方法中,調用了doInBackground(mParams),而後獲得的返回值做爲postResult的參數進行執行;postResult中經過sHandler發送消息,最終sHandler的handleMessage中完成onPostExecute的調用。最後執行exec.execute(mFuture),mFuture爲真正的執行任務的單元,將mWorker進行封裝,而後由sDefaultExecutor交給線程池進行執行。
這裏面咱們涉及到了4個方法中的三個,那麼還有一個方法:
//更新進度
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
progressDialog.setProgress(values[0]);
}複製代碼
那麼這個方法是何時調用的的呢?咱們在使用AsyncTask中的第三個方法doInBackground時在裏面咱們調用了一個傳遞進度的方法 publishProgress(int progress),咱們進入到該方法中查看一下:
//工做線程中執行該方法
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
//經過Handler和Message異步消息機制進行UI線程和Work線程通訊
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}複製代碼
publishProgress方法其實就是發送一個消息,
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"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS://處理進度消息
//調用onProgressUpdate方法顯示進度
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}複製代碼
這就很明朗了,四個方法都調用到了。以上即是AsyncTask全部的執行流程,經過源碼分析可得AsyncTask的內部也是使用Handler+Message的方式進行消息傳遞和處理的。
Android6.0 谷歌把HttpClient相關的類移除了,因此若是繼續使用的話,須要添加相關的jar包。
一、對於AndroidStudio的添加方法是:
在相應的module下的build.gradle中加入:
android {
useLibrary 'org.apache.http.legacy'
}複製代碼
二、對於Eclipse的添加方法是:
libs中加入
org.apache.http.legacy.jar
上面的jar包在:**\android-sdk-windows\platforms\android-23\optional下(須要下載android 6.0的SDK)複製代碼
一、http://www.cnblogs.com/xiaoluo501395377/p/3430542.html
二、http://blog.csdn.net/liuhe688/article/details/6532519
三、http://blog.csdn.net/lmj623565791/article/details/38614699