為什么要用AsyncTask
我們寫App都有一個(gè)原則道媚,主線程不能夠運(yùn)行需要占用大量CPU時(shí)間片的任務(wù),如大量復(fù)雜的浮點(diǎn)運(yùn)算,較大的磁盤IO操作势就,網(wǎng)絡(luò)socket等,這些都會導(dǎo)致我們的主線程對用戶的響應(yīng)變得遲鈍脉漏,甚至ANR苞冯,這些會使應(yīng)用的用戶體驗(yàn)變差,但是有時(shí)又的確需要執(zhí)行這些耗時(shí)的任務(wù)侧巨,那么我們通尘顺可以使用AsyncTask或者new Thread
來處理,這樣把任務(wù)放入工作線程中執(zhí)行司忱,不會占用主線程的時(shí)間片皇忿,所以主線程會及時(shí)響應(yīng)用戶的操作,如果使用new Thread來執(zhí)行任務(wù)坦仍,那么如果需要中途取消任務(wù)執(zhí)行或者需要返回任務(wù)執(zhí)行結(jié)果鳍烁,就需要我們自己維護(hù)很多額外的代碼,而AsyncTask是基于concurrent架包提供的并發(fā)類實(shí)現(xiàn)的繁扎,上面的二個(gè)需求都已經(jīng)幫我們封裝了幔荒,這也是我們選擇AsyncTask的原因。
怎么用AsyncTask
我們還是簡單介紹下AsyncTask一些使用示例锻离。我們先新建一個(gè)類DemoAsyncTask繼承AsyncTask铺峭,因?yàn)锳syncTask是抽象類,其中doInBackground方法必須重寫汽纠。
private class DemoAsyncTask extends AsyncTask<String, Void, Void> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Void doInBackground(String... params) {
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
@Override
protected void onCancelled(Void aVoid) {
super.onCancelled(aVoid);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
}
DemoAsyncTask task = new DemoAsyncTask();
task.execute("demo test AsyncTask");
//task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, "test");
//myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "test");
簡單分析下
上面就是AsyncTask最簡單的使用方法卫键,我們上面重寫的方法中,onInBackground方法運(yùn)行在工作線程虱朵,其他的方法全部運(yùn)行在主線程莉炉,另外它的運(yùn)行方式Android提供給我們2個(gè)方法钓账,上面都列出了。
1.第一個(gè)方法會使用默認(rèn)的Executor執(zhí)行我們的任務(wù), 其實(shí)也就是SERIAL_EXECUTOR絮宁,SERIAL_EXECUTOR我們其實(shí)也是可以通過方法去自定義的梆暮,Android幫我們的默認(rèn)實(shí)現(xiàn)是逐個(gè)執(zhí)行任務(wù),也就是單線程的绍昂,關(guān)于AsyncTask的任務(wù)執(zhí)行是單線程實(shí)現(xiàn)還是多線程實(shí)現(xiàn)還有一段很有意思的歷史啦粹,較早的版本是單線程實(shí)現(xiàn),從Android2.X開始窘游,Google又把它改為多線程實(shí)現(xiàn)唠椭,后來Google發(fā)現(xiàn),多線程實(shí)現(xiàn)的話忍饰,會有很多需要保證線程安全的額外工作留給開發(fā)者贪嫂,所以從Android3.0開始,又把默認(rèn)實(shí)現(xiàn)改為單線程了艾蓝,今天我們演示的是Framwork代碼版本是21(Android5.0)力崇。
-
2.同時(shí)也提供了多線程實(shí)現(xiàn)的接口,即我們上面寫的最后一行代碼 AsyncTask.THREAD_POOL_EXECUTOR赢织。
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
其實(shí)相信大家平時(shí)工作學(xué)習(xí)中經(jīng)常使用AsyncTask亮靴,我們直接去看AsyncTask類源碼(插一句題外話,平時(shí)大家也可以把自己工作學(xué)習(xí)中的心得體會總結(jié)一下敌厘,記下來~~)
public abstract class AsyncTask<Params, Progress, Result> {
....
}
AsyncTask抽象類台猴,有三個(gè)泛型參數(shù)類型,第一個(gè)是你需要傳遞進(jìn)來的參數(shù)類型俱两,第二個(gè)是任務(wù)完成進(jìn)度的類型一般是Integer饱狂,第三個(gè)是任務(wù)完成結(jié)果的返回類型,有時(shí)這些參數(shù)不是全部需要宪彩,不需要的設(shè)為Void即可休讳,另外Result只有一個(gè),但Params可以有多個(gè)尿孔。
我們可以看到AsyncTask的默認(rèn)構(gòu)造器初始化了二個(gè)對象俊柔,mWorker和mFuture。
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
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 occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
mWoker實(shí)現(xiàn)了Callback接口活合,Callback接口是JDK1.5加入的高級并發(fā)架包里面的一個(gè)接口雏婶,它可以有一個(gè)泛型返回值。
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
FutureTask實(shí)現(xiàn)了RunnableFuture接口白指,RunnableFuture接口是JDK提供的留晚,看名稱就知道它繼承了Runnable和Future接口,mFuture是FutureTask的一個(gè)實(shí)例告嘲,可以直接被Executor類實(shí)例execute错维。我們來繼續(xù)看AsyncTask的execute方法奖地。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
先調(diào)用onPreExecute()方法,此時(shí)還在主線程(嚴(yán)格來說是調(diào)用AsyncTask執(zhí)行的線程)赋焕,然后exec.execute(mFuture)参歹,把任務(wù)交給exec處理,execute mFuture其實(shí)就是invoke mWoker隆判,然后調(diào)用postResult(doInBackground(mParams))犬庇,此時(shí)已經(jīng)運(yùn)行在工作線程池,不會阻塞主線程侨嘀。然后給mHandler發(fā)送MESSAGE_POST_RESULT消息械筛,然后調(diào)用finish方法,如果isCancelled飒炎,回調(diào)onCancelled,否則回調(diào)onPostExecute笆豁。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
@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;
}
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
現(xiàn)在其實(shí)我們已經(jīng)把AsyncTask整個(gè)執(zhí)行任務(wù)的過程走完了郎汪,其中暴露給我們的那幾個(gè)回調(diào)方法也都走到了。現(xiàn)在我們回過頭來看闯狱,AsyncTask其實(shí)只是對JDK 1.5提供的高級并發(fā)特性煞赢,concurrent架包做的一個(gè)封裝,方便開發(fā)者來處理異步任務(wù)哄孤,當(dāng)然里面還有很多細(xì)節(jié)處理的方法值得大家學(xué)習(xí)照筑,如任務(wù)執(zhí)行進(jìn)度的反饋,任務(wù)執(zhí)行原子性的保證等瘦陈,這些留給大家自己學(xué)習(xí)了凝危。
使用AsyncTask一點(diǎn)小技巧
我們以一個(gè)實(shí)例來說明,“點(diǎn)擊按鈕開始下載QQAndroid安裝包晨逝,然后顯示一個(gè)對話框來反饋下載進(jìn)度”蛾默。我們先初始化一個(gè)對話框,由于要顯示進(jìn)度捉貌,我們用Github上面一個(gè)能夠顯示百分比的進(jìn)度條 NumberProgressbar支鸡,啟動(dòng)任務(wù)的按鈕我們使用* circlebutton*,一個(gè)有酷炫動(dòng)畫的按鈕趁窃,Github上面有很多非常好的開源項(xiàng)目牧挣,當(dāng)然炫酷的控件是其中一部分了,后面有機(jī)會醒陆,會去學(xué)習(xí)一些比較流行的控件它們的實(shí)現(xiàn)原理瀑构,今天就暫且拿來主義了~~。
- 1.先初始化進(jìn)度條提示對話框统求。
builder = new AlertDialog.Builder(
MainActivity.this);
LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
mDialogView = inflater.inflate(R.layout.progress_dialog_layout, null);
mNumberProgressBar = (NumberProgressBar)mDialogView.findViewById(R.id.number_progress_bar);
builder.setView(mDialogView);
mDialog = builder.create();
* 2.設(shè)置按鈕點(diǎn)擊事件检碗。
findViewById(R.id.circle_btn).setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
dismissDialog();
mNumberProgressBar.setProgress(0);
myTask = new MyAsyncTask();
myTask.execute(qqDownloadUrl);
}
});
- 3.DownloadAsyncTask實(shí)現(xiàn)据块,有點(diǎn)長。
private class DownloadAsyncTask extends AsyncTask<String , Integer, String> {
```
@Override
protected void onPreExecute() {
super.onPreExecute();
mDialog.show();
}
@Override
protected void onPostExecute(String aVoid) {
super.onPostExecute(aVoid);
dismissDialog();
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mNumberProgressBar.setProgress(values[0]);
}
@Override
protected void onCancelled(String aVoid) {
super.onCancelled(aVoid);
dismissDialog();
}
@Override
protected void onCancelled() {
super.onCancelled();
dismissDialog();
}
@Override
protected String doInBackground(String... params) {
String urlStr = params[0];
FileOutputStream output = null;
try {
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
String qqApkFile = "qqApkFile";
File file = new File(Environment.getExternalStorageDirectory() + "/" + qqApkFile);
if (file.exists()) {
file.delete();
}
file.createNewFile();
InputStream input = connection.getInputStream();
output = new FileOutputStream(file);
int total = connection.getContentLength();
if (total <= 0) {
return null;
}
int plus = 0;
int totalRead = 0;
byte[] buffer = new byte[4*1024];
while((plus = input.read(buffer)) != -1){
output.write(buffer);
totalRead += plus;
publishProgress(totalRead * 100 / total);
if (isCancelled()) {
break;
}
}
output.flush();
} catch (MalformedURLException e) {
e.printStackTrace();
if (output != null) {
try {
output.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
if (output != null) {
try {
output.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
```
}
這樣一個(gè)簡單的下載文件文件就基本實(shí)現(xiàn)了折剃,到目前為止談不上技巧另假,但是現(xiàn)在我們有一個(gè)問題,就是如果我們的Activity正在后臺執(zhí)行一個(gè)任務(wù)怕犁,可能耗時(shí)較長边篮,那用戶可能會點(diǎn)擊返回退出Activity或者退出App,那么后臺任務(wù)不會立即退出奏甫,如果AsyncTask內(nèi)部有Activity中成員變量的引用戈轿,還會造成Activity的回收延時(shí),造成一段時(shí)間內(nèi)的內(nèi)存泄露阵子,所以我們需要加上下面的第四步處理思杯。
* 4.onPause中判斷應(yīng)用是否要退出,從而決定是否取消AsyncTask執(zhí)行挠进。
@Override
protected void onPause() {
super.onPause();
if (myTask != null && isFinishing()) {
myTask.cancel(false);
}
}
這樣我們的異步任務(wù)就會在Activity退出時(shí)色乾,也隨之取消任務(wù)執(zhí)行,順利被系統(tǒng)銷毀回收领突,第四步很多時(shí)候會被遺漏暖璧,而且一般也不會有什么致命的問題,但是一旦出問題了君旦,就很難排查陕贮,所以遵循編碼規(guī)范還是有必要的柿顶。
小結(jié)
AsyncTask的基本實(shí)現(xiàn)原理我們已經(jīng)清楚了,同時(shí)我們也介紹了一個(gè)使用AsyncTask要注意的一個(gè)小技巧,希望大家讀完能有所收獲
歡迎大家一起交流討論哈搜囱!