爲了不阻塞UI線程(亦稱主線程),提高應用的響應性,我們經常會使用新開線程的方式,異步處理那些導致阻塞的任務。
AsyncTask是Android爲我們提供的方便編寫異步任務的工具類,但是,在瞭解AsyncTask的實現原理之後,發現AsyncTask並不能滿足我們所有的需求,使用不當還有可能導致應用FC。
本文主要通過分析AsyncTask提交任務的策略和一個具體的例子,說明AsyncTask的不足之處
分析:
AsyncTask類包含一個全局靜態的線程池,線程池的配置參數如下:
- private static final int CORE_POOL_SIZE =5;//5個核心工作線程
- private static final int MAXIMUM_POOL_SIZE = 128;//最多128個工作線程
- private static final int KEEP_ALIVE = 1;//空閒線程的超時時間爲1秒
- private static final BlockingQueue<Runnable> sWorkQueue =
- new LinkedBlockingQueue<Runnable>(10);//等待隊列
- private static final ThreadPoolExecutorsExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
- MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue,sThreadFactory);//線程池是靜態變量,所有的異步任務都會放到這個線程池的工作線程內執行。
我們這裏不詳細講解ThreadPoolExecutor的原理,但將會講解一個異步任務提交到AsyncTask的線程池時可能會出現的4種情況,並會提出在Android硬件配置普遍較低這個客觀條件下,每個情況可能會出現的問題。
1、線程池中的工作線程少於5個時,將會創建新的工作線程執行異步任務(紅色表示新任務,下同)
2、線程池中已經有5個線程,緩衝隊列未滿,異步任務將會放到緩衝隊列中等待
3、線程池中已經有5個線程,緩衝隊列已滿,那麼線程池將新開工作線程執行異步任務
問題:Android的設備一般不超過2個cpu核心,過多的線程會造成線程間切換頻繁,消耗系統資源。
4、線程池中已經有128個線程,緩衝隊列已滿,如果此時向線程提交任務,將會拋出RejectedExecutionException
問題:拋出的錯誤不catch的話會導致程序FC。
好吧,理論分析之後還是要結合實際例子,我們通過實現一個模擬異步獲取網絡圖片的例子,看看會不會出現上面提到的問題。
例子:使用GridView模擬異步加載大量圖片
- package cn.caiwb;
- 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);//執行獲取數據的任務
- }
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case DIALOG_PROGRESS:
- mProgressDialog = new ProgressDialog(ActivityA.this);
- mProgressDialog.setMessage("正在獲取數據");
- 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));//執行獲取圖片的任務
- }
- 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);//模擬耗時的網絡操作
- } 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);//關閉等待對話框
- }
- }
- 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); //模擬耗時的網絡操作
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- mData.get(pos).put("pic", R.drawable.icon);
- return null;
- }
- protected void onPostExecute (Void result) {
- mAdapter.notifyDataSetChanged();//通知ui界面更新
- }
- }
- }
由運行圖可見
當網絡情況較差,異步任務不能儘快完成執行的情況下,新開的線程會造成listview滑動不流暢。當開啓的工作線程過多時,還有出現FC的可能。
至此,你還相信萬能的AsyncTask嗎?
總結:
AsyncTask可能存在新開大量線程消耗系統資源和導致應用FC的風險,因此,我們需要根據自己的需求自定義不同的線程池
OK~