一步步完全解析AsyncTask

    做個Android開發的同學們應該都用過AsyncTask,通過繼承AsyncTask類實現異步操作,反饋當前異步執行的進度,最後執行的結果反饋給UI主線程。我們在開發中使用AsyncTask,因爲它的主要優點使用簡單方便、不用關係和主線程交互邏輯和執行過程可控,當然還可以支持取消。說完AsyncTask的優點,下面我們來數數AsyncTask的幾宗罪。

    1.不同版本實現對併發支持不一,需要控制線程任務先後的情況難以控制,當我們開發中要求兩個任務必須有先後執行順序,我們Android2.3版本AsyncTask默認是併發執行,無法做到控制執行先後順序。

    2.不能在主線程以外更新UI;

    3.不能靈活控制線程間任務執行順序,比如,當我多個任務Task1,Task2,Task3等,我們的Task2和Task3必須等待Task1先運行,而我們的Task2和Task3可以同時運行。這個時候AsyncTask就難以控制。

    4.沒完全捕捉AsyncTask運行時候的異常。

    5.AsyncTask難以處理多任務UI交互的問題。

    我們知道了AsyncTask的缺點後,這樣我們在以後的開發中就能有的選擇什麼時候使用AsyncTask,什麼時候不使用。

    本着加強對AsyncTask這個類的理解,下面我嘗試自己來一步步來實現AsyncTask的功能,努力來解決系統AsyncTask類存在的問題。我們知道AsyncTask的實現本質還是封裝對Handler + Thread模式來更新UI的實現,下面我們來實現我們AsyncTask的實現第一版(V1.0):

public abstract class MyAsyncTask<Result> {

	private static final int CORE_POOL_SIZE = 5;
	private static final int MESSAGE_POST_RESULT = 0x1;
	 
	//此處我們新建一個默認CORE_POOL_SIZE大小的線程池
	public static Executor executor = Executors.newFixedThreadPool(CORE_POOL_SIZE); 

	protected final InternalHandler sHandler  = new InternalHandler();

	private Result postResult(Result result) {
		Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new SafeAsyncTaskResult<Result>(this,result));
		message.sendToTarget(); //任務執行完成,給UI線程發消息,我們的消息參數被封裝到SafeAsyncTaskResult<Result>類的對象裏
		return result;
	}

	public void execute() {
		executor.execute(new FutureTask<Result>(new Callable<Result>() {
			@Override
			public Result call() throws Exception {
				return postResult(doInBackground()); //此處爲異步任務執行
			}
		})); 
	}
	
	/*
	 * 實現該方法,真正的異步任務執行的地方
	 */
	protected abstract Result doInBackground();

	private static class InternalHandler<Result> extends Handler {
		 public void handleMessage(Message msg) {
			 SafeAsyncTaskResult<Result> result = (SafeAsyncTaskResult<Result>) msg.obj;
	            switch (msg.what) {
	                case MESSAGE_POST_RESULT:
	                    result.mTask.finish(result.mData); //注意此處我們在hander裏處理我們接受的MESSAGE_POST_RESULT(表示任務執行完成)
	                    break;
	            }
	        }
	}
	
	private void finish(Result result){
		 onPostExecute(result);
	}
	
	/*
	 * 任務執行完更新UI	
	 */
	protected abstract void onPostExecute(Result result);

	private static class SafeAsyncTaskResult<Data> {
	        final MyAsyncTask mTask;
	        final Data mData;

	        SafeAsyncTaskResult(MyAsyncTask<?> task, Data data) {
	            mTask = task;
	            mData = data;
	        }
	    }
}

我們看我們的V1.0實現,基本實現一個Handler + Thread模式的異步線程操作。但是還存在很多不少問題,首先還沒有實現基本的取消功能,然後,我們也做不到線程執行順序,還做不到在子線程更新UI,還有不能捕捉所有線程異常,最後,做不到靈活實現控制線程執行順序。下面我們V1.1版本重點來支持線程的取消和解決在子線程更新我們的UI。我們知道FutureTask接口支持取消操作,因此我們把FutureTask作爲類MyAsyncTask的成員變量。下面我們來看V1.2版本的實現如下:

public abstract class MyAsyncTask<Result> {

	private static final int CORE_POOL_SIZE = 5;
	private static final int MESSAGE_POST_RESULT = 0x1;
	 
	//此處我們新建一個默認CORE_POOL_SIZE大小的線程池
	public static Executor executor = Executors.newFixedThreadPool(CORE_POOL_SIZE); 

	//保證多個線程對MyAsyncTask取消操作,其他線程立刻可見性
	private final AtomicBoolean mCancelled = new AtomicBoolean(); 
	
	protected final InternalHandler sHandler;
	
	private FutureTask<Result> futureTask;
	
	public MyAsyncTask(){
		if (Looper.myLooper() != Looper.getMainLooper()) {
			 // 當MyAsyncTask不在主線程實例化,給Handler指定Looper,可以實現在子線程更新主線程
			sHandler = new InternalHandler(Looper.getMainLooper()); 
		} else {
			sHandler = new InternalHandler();
		}

		futureTask = new FutureTask<Result>(new Callable<Result>() {
			@Override
			public Result call() throws Exception {
				return postResult(doInBackground()); //此處爲異步任務執行
			}
		});
	}

	private Result postResult(Result result) {
		Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new SafeAsyncTaskResult<Result>(this,result));
		message.sendToTarget(); //任務執行完成,給UI線程發消息,我們的消息參數被封裝到SafeAsyncTaskResult<Result>類的對象裏
		return result;
	}

	public void execute() {
		onPreExecute();
		executor.execute(futureTask); 
	}
	
	/*
	 * 當異步任務開始前的操作
	 */
	private void onPreExecute() {
		
	}

	public  boolean cancel(boolean mayInterruptIfRunning) {
		mCancelled.set(true);
        return futureTask.cancel(mayInterruptIfRunning);
    }
	
	/*
	 * 判斷當前任務是否取消
	 */
	public boolean isCanceled(){
		return mCancelled.get();
	}
	
	/*
	 * 實現該方法,真正的異步任務執行的地方
	 */
	protected abstract Result doInBackground();

	private static class InternalHandler<Result> extends Handler {
		
		public InternalHandler() {
			super();
		}
		
		public InternalHandler(Looper looper) {
			super(looper);
		}
		
		
		 public void handleMessage(Message msg) {
			 SafeAsyncTaskResult<Result> result = (SafeAsyncTaskResult<Result>) msg.obj;
	            switch (msg.what) {
	                case MESSAGE_POST_RESULT:
	                    result.mTask.finish(result.mData); //注意此處我們在hander裏處理我們接受的MESSAGE_POST_RESULT(表示任務執行完成)
	                    break;
	            }
	        }
	}
	
	private void finish(Result result){
		 onPostExecute(result);
	}
	
	/*
	 * 任務執行完更新UI	
	 */
	protected abstract void onPostExecute(Result result);

	private static class SafeAsyncTaskResult<Data> {
	        final MyAsyncTask mTask;
	        final Data mData;

	        SafeAsyncTaskResult(MyAsyncTask<?> task, Data data) {
	            mTask = task;
	            mData = data;
	        }
	    }
}

然後我們V1.3版本重點在控制線程併發和線程協作處理上,在V1.2版本我們的線程處理直接建立了一個大小固定的線程池來處理問題,在默認的4.0及以上,AsyncTask默認線程池線性執行系列線程,並且可以並行執行系列線程。此處我們直接參看源碼4.0對AsyncTask的執行控制,我們在源碼裏看到我們系統默認初始化一個併發的線程池THREAD_POOL_EXECUTE線程池,同時初始化了一個類SerialExecutor,我們看SerialExecutor的代碼:

private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            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);
            }
        }
    }

我們注意到scheduleNext方法的實現實際上還是調用THREAD_POOL_EXECUTOR併發的線程池來執行線程,這樣我們知道AsyncTask控制線程池順序執行任務時間上通過控制併發線程池順序執行來達到目的。通過這裏控制併發的線程池線性執行線程,我們可以大膽的說,如果我們設計合理的話,我們這裏也可以通過封裝實現更靈活的控制線程池實現。鑑於水平有限,沒能寫出更靈活的控制線程執行順序的執行器。下面我們給出V1.3版本的代碼實現功能,已經很接近系統4.0的AsyncTask的源碼實現。

public abstract class MyAsyncTask<Result> {

	private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
	private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
	private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
	private static final int MESSAGE_POST_RESULT = 0x1;
	private static final int KEEP_ALIVE = 1;
	
	private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128); 
	
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); 
    
    public static final Executor THREAD_POOL_EXECUTOR
    	= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
            TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
    
    //Android源碼AsyncTask 實現sDefaultExecutor用關鍵字volatile,是因爲 setDefaultExecutor方法可以修改AsyncTask默認實現
    private static Executor sDefaultExecutor = SERIAL_EXECUTOR; 
    
    //任務完成或者取消的回調接口
    private  AysncTaskFinishedListener finishListener;
    
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            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);
            }
        }
    }
    
	//保證多個線程對MyAsyncTask取消操作,其他線程立刻可見性
	private final AtomicBoolean mCancelled = new AtomicBoolean(); 
	
	protected final InternalHandler sHandler;
	
	private FutureTask<Result> futureTask;
	
	public MyAsyncTask(){
		if (Looper.myLooper() != Looper.getMainLooper()) {
			 // 當MyAsyncTask不在主線程實例化,給Handler指定Looper,可以實現在子線程更新主線程
			sHandler = new InternalHandler(Looper.getMainLooper()); 
		} else {
			sHandler = new InternalHandler();
		}

		futureTask = new FutureTask<Result>(new Callable<Result>() {
			@Override
			public Result call() throws Exception {
				return postResult(doInBackground()); //此處爲異步任務執行
			}
		});
	}
	
	protected void setFinishedListener(AysncTaskFinishedListener finishListener) {
		this.finishListener = finishListener;
	}

	private Result postResult(Result result) {
		Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new SafeAsyncTaskResult<Result>(this,result));
		message.sendToTarget(); //任務執行完成,給UI線程發消息,我們的消息參數被封裝到SafeAsyncTaskResult<Result>類的對象裏
		return result;
	}

	/*
	 * 默認線程線性執行
	 */
	public void execute() {
		onPreExecute();
		sDefaultExecutor.execute(futureTask); 
	}
	
	/*
	 * 顯式調用並行執行
	 */
	public void executeOnExecutor(){
		onPreExecute();
		THREAD_POOL_EXECUTOR.execute(futureTask);
	}
	
	/*
	 * 當異步任務開始前的操作
	 */
	private void onPreExecute() {
		
	}

	public  boolean cancel(boolean mayInterruptIfRunning) {
		mCancelled.set(true);
        return futureTask.cancel(mayInterruptIfRunning);
    }
	
	/*
	 * 判斷當前任務是否取消
	 */
	public boolean isCanceled(){
		return mCancelled.get();
	}
	
	/*
	 * 實現該方法,真正的異步任務執行的地方
	 */
	protected abstract Result doInBackground();

	private static class InternalHandler<Result> extends Handler {
		
		public InternalHandler() {
			super();
		}
		
		public InternalHandler(Looper looper) {
			super(looper);
		}
		
		
		 public void handleMessage(Message msg) {
			 SafeAsyncTaskResult<Result> result = (SafeAsyncTaskResult<Result>) msg.obj;
	            switch (msg.what) {
	                case MESSAGE_POST_RESULT:
	                    result.mTask.finish(result.mData); //注意此處我們在hander裏處理我們接受的MESSAGE_POST_RESULT(表示任務執行完成)
	                    break;
	            }
	        }
	}
	
	private void finish(Result result){
		 if(isCanceled()){
			 if(finishListener != null)
				 finishListener.onCancelled();
		 }else{
			 onPostExecute(result);
			 if(finishListener != null)
				 finishListener.onPostExecute();
		 }
		 
	}
	
	/*
	 * 任務執行完更新UI	
	 */
	protected abstract void onPostExecute(Result result);

	private static class SafeAsyncTaskResult<Data> {
	        final MyAsyncTask mTask;
	        final Data mData;

	        SafeAsyncTaskResult(MyAsyncTask<?> task, Data data) {
	            mTask = task;
	            mData = data;
	        }
	    }
	
	public static interface AysncTaskFinishedListener {
		void onCancelled();

		void onPostExecute();
	}
}

V1.3的代碼沒有能達到線程池對代碼的更靈活的控制過程,我們可以通過封裝MyAsyncTask的操作來實現。以下的實現參考了github上的android-lite-async項目的實現,對MyAsyncTask的實現如下:

public class TaskExecutor {
    /**
     * 關卡異步任務執行器
     *
     * @return
     */
    public static CyclicBarrierExecutor newCyclicBarrierExecutor() {
        return new CyclicBarrierExecutor();
    }
    
	public static class CyclicBarrierExecutor {
        ArrayList<MyAsyncTask<?>> taskList = new ArrayList<MyAsyncTask<?>>();

        public CyclicBarrierExecutor put(MyAsyncTask<?> task) {
            if (task != null) taskList.add(task);
            return this;
        }

        public void start(final MyAsyncTask< ?> finishTask) {
            start(finishTask, 0, null);
        }

        public void start(final MyAsyncTask<?> endOnUiTask, final long time, final TimeUnit unit) {
            final CountDownLatch latch = new CountDownLatch(taskList.size());
            new MyAsyncTask<Boolean>() {

                @Override
                protected Boolean doInBackground() {
                    try {
                        if (unit == null) 
                        	latch.await();
                        else 
                        	latch.await(time, unit);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return true;
                }

                @Override
                protected void onPostExecute(Boolean aBoolean) {
                    endOnUiTask.execute();
                }
            }.execute();
            startInternal(latch);
        }

        private void startInternal(final CountDownLatch latch) {
            for (MyAsyncTask<?> each : taskList) {
                each.setFinishedListener(new MyAsyncTask.AysncTaskFinishedListener() {

                    @Override
                    public void onPostExecute() {
                        latch.countDown();
                    }

                    @Override
                    public void onCancelled() {
                        latch.countDown();
                    }
                });
                each.execute();
            }
        }
    }
}
以上代碼我們通過CountDownLatch來控制線程的先後執行順序,已經能達到我們比較靈活的控制代碼的執行順序。通過以上的代碼分析,關於AsyncTask的實現,都是圍繞對handler + Thread模式展開,無非封裝的強大與否。如果想繼續深入學習對handler + Thread的封裝,大家可以學習學習github的Android客戶端封裝的SafeAsyncTask實現。ps:github的android客戶端值得學習的地方可遠非只有SafeAsyncTask的代碼封裝,很值得感興趣的同學可以對整個代碼框架深入學習。

通過上面的分析,我們也知道AsyncTask就算優化解決了以上的幾宗罪(第5條除外),我們的AsyncTask也不是萬能的。在我們的開發中還是很多線程交互的情況無法用AyncTask解決,比如,第五條的問題,還有比如,當我們兩個線程相互等待的情況,如,任務一請求接口數據返回任務2來處理,任務一等待回調任務二數據,然後繼續執行任務一(ps:在我們的開發,這種情況的出現很大一部分原因是接口接口來不及處理或者處理的不及時,最後,只能我們交給我們客戶端來解決)。

轉載請註明出處:http://blog.csdn.net/johnnyz1234/article/details/47092227







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章