AsyncTask的缺陷
導(dǎo)語:在開發(fā)Android應(yīng)用的過程中,我們需要時刻注意保障應(yīng)用的穩(wěn)定性和界面響應(yīng)性,因為不穩(wěn)定或者響應(yīng)速度慢的應(yīng)用將會給用戶帶來非常差的交互體驗。在越來越講究用戶體驗的大環(huán)境下,用戶也許會因為應(yīng)用的一次Force Close(簡稱FC)或者延遲嚴重的動畫效果而卸載你的應(yīng)用。由于現(xiàn)在的應(yīng)用大多需要異步連接網(wǎng)絡(luò),本系列文章就以構(gòu)建網(wǎng)絡(luò)應(yīng)用為例,從穩(wěn)定性和響應(yīng)性兩個角度分析多線程網(wǎng)絡(luò)任務(wù)的性能優(yōu)化方法。
概述:為了不阻塞UI線程(亦稱主線程),提高應(yīng)用的響應(yīng)性,我們經(jīng)常會使用新開線程的方式,異步處理那些導(dǎo)致阻塞的任務(wù)。
AsyncTask是Android為我們提供的方便編寫異步任務(wù)的工具類,但是,在了解AsyncTask的實現(xiàn)原理之后,發(fā)現(xiàn)AsyncTask并不能滿足我們所有的需求,使用不當還有可能導(dǎo)致應(yīng)用FC。
本文主要通過分析AsyncTask提交任務(wù)的策略和一個具體的例子,說明AsyncTask的不足之處,至于解決辦法,我們將在下篇再講解。
分析:
AsyncTask類包含一個全局靜態(tài)的線程池,線程池的配置參數(shù)如下:
//5個核心工作線程
private static final int CORE_POOL_SIZE =5;
//最多128個工作線程
private static final int MAXIMUM_POOL_SIZE = 128;
//空閑線程的超時時間為1秒
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);//等待隊列
private static final ThreadPoolExecutorsExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
//線程池是靜態(tài)變量,所有的異步任務(wù)都會放到這個線程池的工作線程內(nèi)執(zhí)行。
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue,sThreadFactory);
我們這里不詳細講解ThreadPoolExecutor的原理,但將會講解一個異步任務(wù)提交到AsyncTask的線程池時可能會出現(xiàn)的4種情況,并會提出在Android硬件配置普遍較低這個客觀條件下,每個情況可能會出現(xiàn)的問題。
1、線程池中的工作線程少于5個時,將會創(chuàng)建新的工作線程執(zhí)行異步任務(wù)(紅色表示新任務(wù),下同)
2、線程池中已經(jīng)有5個線程,緩沖隊列未滿,異步任務(wù)將會放到緩沖隊列中等待
3、線程池中已經(jīng)有5個線程,緩沖隊列已滿,那么線程池將新開工作線程執(zhí)行異步任務(wù)
問題:Android的設(shè)備一般不超過2個cpu核心,過多的線程會造成線程間切換頻繁,消耗系統(tǒng)資源。
4、線程池中已經(jīng)有128個線程,緩沖隊列已滿,如果此時向線程提交任務(wù),將會拋出RejectedExecutionException
問題:拋出的錯誤不catch的話會導(dǎo)致程序FC。
好吧,理論分析之后還是要結(jié)合實際例子,我們通過實現(xiàn)一個模擬異步獲取網(wǎng)絡(luò)圖片的例子,看看會不會出現(xiàn)上面提到的問題。
例子:使用GridView模擬異步加載大量圖片
ActivityA.java
package com.zhuozhuo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
public class ActivityA extends Activity {
private GridView mGridView;
private List<HashMap<String, Object>> mData;
private BaseAdapter mAdapter;
private ProgressDialog mProgressDialog;
private static final int DIALOG_PROGRESS = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mGridView = (GridView) findViewById(R.id.gridview);
mData = new ArrayList<HashMap<String,Object>>();
mAdapter = new CustomAdapter();
mGridView.setAdapter(mAdapter);
}
protected void onStart () {
super.onStart();
new GetGridDataTask().execute(null);//執(zhí)行獲取數(shù)據(jù)的任務(wù)
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_PROGRESS:
mProgressDialog = new ProgressDialog(ActivityA.this);
mProgressDialog.setMessage("正在獲取數(shù)據(jù)");
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
return mProgressDialog;
}
return null;
}
class CustomAdapter extends BaseAdapter {
CustomAdapter() {
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder vh;
if(view == null) {
view = LayoutInflater.from(ActivityA.this).inflate(R.layout.list_item, null);
vh = new ViewHolder();
vh.tv = (TextView) view.findViewById(R.id.textView);
vh.iv = (ImageView) view.findViewById(R.id.imageView);
view.setTag(vh);
}
vh = (ViewHolder) view.getTag();
vh.tv.setText((String) mData.get(position).get("title"));
Integer id = (Integer) mData.get(position).get("pic");
if(id != null) {
vh.iv.setImageResource(id);
}
else {
vh.iv.setImageBitmap(null);
}
FifoAsyncTask task = (FifoAsyncTask) mData.get(position).get("task");
if(task == null || task.isCancelled()) {
Log.d("Test", "" + position);
mData.get(position).put("task", new GetItemImageTask(position).execute(null));//執(zhí)行獲取圖片的任務(wù)
}
return view;
}
}
static class ViewHolder {
TextView tv;
ImageView iv;
}
class GetGridDataTask extends FifoAsyncTask<Void, Void, Void> {
protected void onPreExecute () {
mData.clear();
mAdapter.notifyDataSetChanged();
showDialog(DIALOG_PROGRESS);//打開等待對話框
}
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(500);//模擬耗時的網(wǎng)絡(luò)操作
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i = 0; i < 200; i++) {
HashMap<String, Object> hm = new HashMap<String, Object>();
hm.put("title", "Title");
mData.add(hm);
}
return null;
}
protected void onPostExecute (Void result) {
mAdapter.notifyDataSetChanged();//通知ui界面更新
dismissDialog(DIALOG_PROGRESS);//關(guān)閉等待對話框
}
}
class GetItemImageTask extends FifoAsyncTask<Void, Void, Void> {
int pos;
GetItemImageTask(int pos) {
this.pos = pos;
}
@Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(2000); //模擬耗時的網(wǎng)絡(luò)操作
} catch (InterruptedException e) {
e.printStackTrace();
}
mData.get(pos).put("pic", R.drawable.icon);
return null;
}
protected void onPostExecute (Void result) {
mAdapter.notifyDataSetChanged();//通知ui界面更新
}
}
}
當網(wǎng)絡(luò)情況較差,異步任務(wù)不能盡快完成執(zhí)行的情況下,新開的線程會造成listview滑動不流暢。當開啟的工作線程過多時,還有出現(xiàn)FC的可能。
至此,你還相信萬能的AsyncTask嗎?至于你信不信,反正我不信。
總結(jié):
AsyncTask可能存在新開大量線程消耗系統(tǒng)資源和導(dǎo)致應(yīng)用FC的風(fēng)險,因此,我們需要根據(jù)自己的需求自定義不同的線程池,由于篇幅問題,將留到下篇再講。