編輯:關於Android編程
一.Android的異步機制
在Android中實現異步任務機制有兩種方式,Handler和AsyncTask。
(1)Handler模式需要為每一個任務創建一個新的線程,任務完成後通過Handler實例向對應線程發送消息,完成事件處理,這種方式對於整個過程的控制比較精細,但也是有缺點的,例如代碼相對臃腫,在多個任務同時執行時,對多線程進行精確的控制復雜。
(2)1.5中引入AsyncTask,它使創建異步任務變得更加簡單,不再需要編寫任務線程和Handler實例即可完成相同的任務。它內部是基於handler實現,加入了多線程任務的控制。AsyncTask是對Thread+Handler良好的封裝輕量級異步類。可以直接繼承AsyncTask,在類中實現異步操作,並提供接口反饋當前異步執行的程度(可以通過接口實現UI進度更新),最後反饋執行的結果給UI主線程.
(3)優缺點對比:
AsyncTask:簡單,便捷。使用輕量級快速更新界面。
Handler:靈活,過程控制精細。可以自定義實現多種線程,對執行過程更加控制比較精細。
至於實際情況下,根據個人習慣來談談:
AsyncTask輕量,簡單便捷,那麼在對整個過程的控制要求不是很復雜精細的情況下使用,比如:層做過一個點餐,菜品飛到購物車的動畫效果。比如我們每點一道菜,需要菜的小圖,飛行軌跡到達購物車圖標所在的地方。飛行軌跡動畫一般幾百毫秒,那麼在點了一道菜,正在飛往購物車,有點一下,這樣屏幕上,應該是有一串菜品飛往購物車的軌跡更新,但是這個過程中可能存在一些復雜數據等操作,那麼我們就需要把這些並行計算放到異步線程中去操作,然後快速更新UI界面上其對應的動畫模塊的屬性。線程池並發執行,避免計算耗時,至浪費時間,渲染卡頓,性能問題等。這種過程如果利用Handler機制實現,控制將比較繁瑣。
當然AsyncTask是Runnable和Handler封裝的輕量類。那麼其適用性就受到了限制。
實際適用中,我們可以自定義Thread,封裝Handler來處理我們特定的過程。比如自定義一個空間,裡面異步消息處理當然是Handler來處理,封裝後的AsyncTask根本不能處理。看到網上有人在討論他們的性能等問題,我只想說,雖然AsyncTask是輕量級實現,但是畢竟是自己封裝的一些東西進去。沒有基礎Handler的性能高,但應該是很微小的一點吧。所以具體使用看實際情況與偏好吧O(∩_∩)O~,下面來帶大家看看;AsyncTask到底是如何實現的。
二.AsyncTask源碼解析
1.基本用法
首先來看一下AsyncTask的基本用法,由於AsyncTask是一個抽象類,所以如果我們想使用它,就必須要創建一個子類去繼承它。
在繼承時我們可以為AsyncTask類指定三個泛型參數,這三個參數的用途如下:
(1). Params
在執行AsyncTask時需要傳入的參數,可用於在後台任務中使用。
(2). Progress
後台任務執行時,如果需要在界面上顯示當前的進度,則使用這裡指定的泛型作為進度單位。
(3). Result
當任務執行完畢後,如果需要對結果進行返回,則使用這裡指定的泛型作為返回值類型。
我們還需要去重寫AsyncTask中的幾個方法才能完成對任務的定制。經常需要去重寫的方法有以下四個:
(1). onPreExecute()
這個方法會在後台任務開始執行之間調用,用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。
(2). doInBackground(Params...)
這個方法中的所有代碼都會在子線程中運行,我們應該在這裡去處理所有的耗時任務。任務一旦完成就可以通過return語句來將任務的執行結果進行返回,如果AsyncTask的第三個泛型參數指定的是Void,就可以不返回任務執行結果。注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說反饋當前任務的執行進度,可以調用publishProgress(Progress...)方法來完成。
(3). onProgressUpdate(Progress...)
當在後台任務中調用了publishProgress(Progress...)方法後,這個方法就很快會被調用,方法中攜帶的參數就是在後台任務中傳遞過來的。在這個方法中可以對UI進行操作,利用參數中的數值就可以對界面元素進行相應的更新。
(4). onPostExecute(Result)
當後台任務執行完畢並通過return語句進行返回時,這個方法就很快會被調用。返回的數據會作為參數傳遞到此方法中,可以利用返回的數據來進行一些UI操作,比如說提醒任務執行的結果,以及關閉掉進度條對話框等。
classtestTaskextendsAsyncTask<Void,Integer,Boolean>{
@Override
protectedvoidonPreExecute(){
showMyShortTip("准備工作");
}
@Override
protectedBooleandoInBackground(Void...params){
try{
while(true){
intvalue=test();
publishProgress(value);
if(value>=100){
break;
}
}
}catch(Exceptione){
returnfalse;
}
returntrue;
}
@Override
protectedvoidonProgressUpdate(Integer...values){
showMyShortTip("主線程當前進度:"+values);
}
@Override
protectedvoidonPostExecute(Booleanresult){
if(result){
showMyShortTip("執行成功");
}else{
showMyShortTip("執行失敗");
}
}
}
newtestTask().execute();
2.AsyncTask源碼分析
從執行過程,我們看一看到在啟動某一個任務之前,要先new出它的實例,然後在調用Execute()執行。
那麼先來解析構造函數,在來看執行過程。
publicAsyncTask(){
mWorker=newWorkerRunnable(){
publicResultcall()throwsException{
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspectionunchecked
returnpostResult(doInBackground(mParams));
}
};
mFuture=newFutureTask(mWorker){
@Override
protectedvoiddone(){
try{
postResultIfNotInvoked(get());
}catch(InterruptedExceptione){
android.util.Log.w(LOG_TAG,e);
}catch(ExecutionExceptione){
thrownewRuntimeException("AnerroroccuredwhileexecutingdoInBackground()",
e.getCause());
}catch(CancellationExceptione){
postResultIfNotInvoked(null);
}
}
};
}
如上所示並沒有任何具體的邏輯會得到執行,只是初始化了兩個變量,mWorker和mFuture,並在初始化mFuture的時候將mWorker作為參數傳入。mWorker是一個Callable對象,mFuture是一個FutureTask對象(繼承了Runable接口,後續run()方法執行,後面再詳解),這兩個變量會暫時保存在內存中,稍後執行會用到它們。
關於執行execute方法的代碼。
publicfinalAsyncTaskexecute(Params...params){
returnexecuteOnExecutor(sDefaultExecutor,params);
}
publicfinalAsyncTaskexecuteOnExecutor(Executorexec,
Params...params){
if(mStatus!=Status.PENDING){
switch(mStatus){
caseRUNNING:
thrownewIllegalStateException("Cannotexecutetask:"
+"thetaskisalreadyrunning.");
caseFINISHED:
thrownewIllegalStateException("Cannotexecutetask:"
+"thetaskhasalreadybeenexecuted"
+"(ataskcanbeexecutedonlyonce)");
}
}
mStatus=Status.RUNNING;
onPreExecute();
mWorker.mParams=params;
exec.execute(mFuture);
returnthis;
}
這裡率先調用了onPreExecute(),我們可以在主線程中先執行預備開始的操作。
將執行時的參數,傳入mworker,由構造函數,可知,mFuture中有mWorker的引用,那麼參數也傳入,繼承了Runable接口的類中,之後線程池調用執行。
顯而易見關於後台任務執行方法 doInBackground(Params…),是在mWorker的接口call調用時,執行的。
mWorker=newWorkerRunnable(){
publicResultcall()throwsException{
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspectionunchecked
returnpostResult(doInBackground(mParams));
}
};
並且將執行結構通過postResult,去分發響應,在在postresult'方法。
privateResultpostResult(Resultresult){
@SuppressWarnings("unchecked")
Messagemessage=sHandler.obtainMessage(MESSAGE_POST_RESULT,
newAsyncTaskResult(this,result));
message.sendToTarget();
returnresult;
}
通過sHandler,進行消息響應。
或者是調用了publicProgress來更新進度
protectedfinalvoidpublishProgress(Progress...values){
if(!isCancelled()){
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
newAsyncTaskResult
在來看Handler的定義。
privatestaticfinalInternalHandlersHandler=newInternalHandler();
privatestaticclassInternalHandlerextendsHandler{
@SuppressWarnings({"unchecked","RawUseOfParameterizedType"})
@Override
publicvoidhandleMessage(Messagemsg){
AsyncTaskResultresult=(AsyncTaskResult)msg.obj;
switch(msg.what){
caseMESSAGE_POST_RESULT:
//Thereisonlyoneresult
result.mTask.finish(result.mData[0]);
break;
caseMESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
首先根據標記判斷,消息類型,這裡只有2中,返回執行結果或返回執行進度,這裡返回執行結構,那麼我們在來看下AsyncTask的finish()方法定義。
privatevoidfinish(Resultresult){
if(isCancelled()){
onCancelled(result);
}else{
onPostExecute(result);
}
mStatus=Status.FINISHED;
}
如果取消,那麼執行取消操作,不是的話,就調用我們重寫的onPostExecute,來進行結果處理。進行進度更新的也是一樣。
那麼引起這一系列處理是,mWorker的call()。接下來,繼續看什麼時候調用了這個方法呢。
現在來看看線程池執行mFuture的具體過程。
exec.execute(mFuture);
默認的線程池是這個
publicstaticfinalExecutorSERIAL_EXECUTOR=newSerialExecutor();
privatestaticvolatileExecutorsDefaultExecutor=SERIAL_EXECUTOR;
來具體看看這個默認的實現
privatestaticclassSerialExecutorimplementsExecutor{
finalArrayDequemTasks=newArrayDeque();
RunnablemActive;
publicsynchronizedvoidexecute(finalRunnabler){
mTasks.offer(newRunnable(){
publicvoidrun(){
try{
r.run();
}finally{
scheduleNext();
}
}
});
if(mActive==null){
scheduleNext();
}
}
protectedsynchronizedvoidscheduleNext(){
if((mActive=mTasks.poll())!=null){
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
看到裡面有一個雙端隊列ArrayDeque,來每次offer新加一個新建Runnable添加進隊列,裡面阻塞執行run方法,,不管成功與否,只有結束時,才去執行下一個。第一次執行的時候,mActivi當前活躍Runnable肯定為空,需要初始啟動。這樣以串行讀取執行的方式,來模擬單線程池模式。
接下來我們繼續去看mFuture實現的Runnable接口,run()方法是如何實現的。
privatefinalWorkerRunnablemWorker;
privatestaticabstractclassWorkerRunnableimplementsCallable{
Params[]mParams;
}
看mWorker的實現,就是實現Callable接口,並且緩存了parems參數
privatefinalFutureTask
在來看Future的構造函數:
publicFutureTask(Callablecallable){
if(callable==null)
thrownewNullPointerException();
this.callable=callable;
this.state=NEW; //ensurevisibilityofcallable
}
其實就是將Callable接口實現,傳遞給FutureTask去執行,這邊在來關注run()方法的實現。
publicvoidrun(){
if(state!=NEW||
!UNSAFE.compareAndSwapObject(this,runnerOffset,
null,Thread.currentThread()))
return;
try{
Callablec=callable;
if(c!=null&&state==NEW){
Vresult;
booleanran;
try{
result=c.call();
ran=true;
}catch(Throwableex){
result=null;
ran=false;
setException(ex);
}
if(ran)
set(result);
}
}finally{
//runnermustbenon-nulluntilstateissettledto
//preventconcurrentcallstorun()
runner=null;
//statemustbere-readafternullingrunnertoprevent
//leakedinterrupts
ints=state;
if(s>=INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
那麼介紹到這裡,基本AsyncTask的基本執行流程的代碼算是介紹介紹完了。接下來隨便談談AsyncTask的一些
關於一些擴展需要注意的點:
SerialExecutor也是AsyncTask在3.0版本以後做了最主要的修改的地方,它在AsyncTask中是以常量的形式被使用的,因此在整個應用程序中的所有AsyncTask實例都會共用同一個SerialExecutor。
如果我們希望一些任務能夠並發執行,我們可以自己定義線程池的規則,後調用執行。
可以看到,這裡規定同一時刻能夠運行的線程數為5個,線程池總大小為128。也就是說當我們啟動了10個任務時,只有5個任務能夠立刻執行,另外的5個任務則需要等待,當有一個任務執行完畢後,第6個任務才會啟動,以此類推。而線程池中最大能存放的線程數是128個,當我們嘗試去添加第129個任務時,程序就會崩潰。
Executorexec=newThreadPoolExecutor(5,128,10, TimeUnit.SECONDS,newLinkedBlockingQueue()); newtestTask().executeOnExecutor(exec);
Android bluetooth介紹(四): a2dp connect流程分析
關鍵詞:藍牙blueZ A2DP、SINK、sink_connect、sink_disconnect、sink_suspend、sink_resume、sink_is_
詳解Android首選項框架的使用實例
首選項這個名詞對於熟悉Android的朋友們一定不會感到陌生,它經常用來設置軟件的運行參數。Android提供了一種健壯並且靈活的框架來處理首選項。它提供了簡單的API來
android花屏效果的實現(ViewPager的基本使用)
1、程序運行效果圖 二、代碼實現 1、main.xml 2、tab1.xml、tab2.xm
關於Android bitmap你不知道的一些事
本文為大家分享了Android bitmap使用細節,供大家參考,具體內容如下1、計算機表示圖形的幾種方式1)BMP :幾乎不進行壓縮 占用空間比較大 2)JPG : 在