Android多線程任務優化1:探討AsyncTask的缺陷

導語:在開發Android應用的過程當中,咱們須要時刻注意保障應用的穩定性和界面響應性,由於不穩定或者響應速度慢的應用將會給用戶帶來很是差的交互體驗。在愈來愈講究用戶體驗的大環境下,用戶也許會由於應用的一次Force Close(簡稱FC)或者延遲嚴重的動畫效果而卸載你的應用。因爲如今的應用大多須要異步鏈接網絡,本系列文章就以構建網絡應用爲例,從穩定性和響應性兩個角度分析多線程網絡任務的性能優化方法。java

概述:爲了避免阻塞UI線程(亦稱主線程),提升應用的響應性,咱們常常會使用新開線程的方式,異步處理那些致使阻塞的任務(如要了解Android異步處理的實現方式和原理,請先閱讀《Android異步處理系列文章索引》)。android

AsyncTask是Android爲咱們提供的方便編寫異步任務的工具類,可是,在瞭解AsyncTask的實現原理以後,發現AsyncTask並不能知足咱們全部的需求,使用不當還有可能致使應用FC。性能優化

本文主要經過分析AsyncTask提交任務的策略和一個具體的例子,說明AsyncTask的不足之處,至於解決辦法,咱們將在下篇再講解。網絡

分析多線程

AsyncTask類包含一個全局靜態的線程池,線程池的配置參數以下:app

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硬件配置廣泛較低這個客觀條件下,每一個狀況可能會出現的問題。

一、線程池中的工做線程少於5個時,將會建立新的工做線程執行異步任務(紅色表示新任務,下同)異步

二、線程池中已經有5個線程,緩衝隊列未滿,異步任務將會放到緩衝隊列中等待ide

三、線程池中已經有5個線程,緩衝隊列已滿,那麼線程池將新開工做線程執行異步任務工具

問題:Android的設備通常不超過2個cpu核心,過多的線程會形成線程間切換頻繁,消耗系統資源。性能

四、線程池中已經有128個線程,緩衝隊列已滿,若是此時向線程提交任務,將會拋出RejectedExecutionException

問題:拋出的錯誤不catch的話會致使程序FC。

好吧,理論分析以後仍是要結合實際例子,咱們經過實現一個模擬異步獲取網絡圖片的例子,看看會不會出現上面提到的問題。

例子:使用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);//執行獲取數據的任務  
    }  
      
      
      
      
    @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的風險,所以,咱們須要根據本身的需求自定義不一樣的線程池,因爲篇幅問題,將留到下篇再講。

相關文章
相關標籤/搜索