Android開發(fā)藝術(shù)探索_第1頁
Android開發(fā)藝術(shù)探索_第2頁
Android開發(fā)藝術(shù)探索_第3頁
Android開發(fā)藝術(shù)探索_第4頁
Android開發(fā)藝術(shù)探索_第5頁
已閱讀5頁,還剩434頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

Android開發(fā)藝術(shù)探索目錄\h第1章Activity的生命周期和啟動(dòng)模式\h1.1Activity的生命周期全面分析\h1.1.1典型情況下的生命周期分析\h1.1.2異常情況下的生命周期分析\h1.2Activity的啟動(dòng)模式\h1.2.1Activity的LaunchMode\h1.2.2Activity的Flags\h1.3IntentFilter的匹配規(guī)則\h第2章IPC機(jī)制\h2.1AndroidIPC簡介\h2.2Android中的多進(jìn)程模式\h2.2.1開啟多進(jìn)程模式\h2.2.2多進(jìn)程模式的運(yùn)行機(jī)制\h2.3IPC基礎(chǔ)概念介紹\h2.3.1Serializable接口\h2.3.2Parcelable接口\h2.3.3Binder\h2.4Android中的IPC方式\h2.4.1使用Bundle\h2.4.2使用文件共享\h2.4.3使用Messenger\h2.4.4使用AIDL\h2.4.5使用ContentProvider\h2.4.6使用Socket\h2.5Binder連接池\h2.6選用合適的IPC方式\h第3章View的事件體系\h3.1View基礎(chǔ)知識(shí)\h3.1.1什么是View\h3.1.2View的位置參數(shù)\h3.1.3MotionEvent和TouchSlop\h3.1.4VelocityTracker、GestureDetector和Scroller\h3.2View的滑動(dòng)\h3.2.1使用scrollTo/scrollBy\h3.2.2使用動(dòng)畫\h3.2.3改變布局參數(shù)\h3.2.4各種滑動(dòng)方式的對(duì)比\h3.3彈性滑動(dòng)\h3.3.1使用Scroller\h3.3.2通過動(dòng)畫\h3.3.3使用延時(shí)策略\h3.4View的事件分發(fā)機(jī)制\h3.4.1點(diǎn)擊事件的傳遞規(guī)則\h3.4.2事件分發(fā)的源碼解析\h3.5View的滑動(dòng)沖突\h3.5.1常見的滑動(dòng)沖突場(chǎng)景\h3.5.2滑動(dòng)沖突的處理規(guī)則\h3.5.3滑動(dòng)沖突的解決方式\h第4章View的工作原理\h4.1初識(shí)ViewRoot和DecorView\h4.2理解MeasureSpec\h4.2.1MeasureSpec\h4.2.2MeasureSpec和LayoutParams的對(duì)應(yīng)關(guān)系\h4.3View的工作流程\h4.3.1measure過程\h4.3.2layout過程\h4.3.3draw過程\h4.4自定義View\h4.4.1自定義View的分類\h4.4.2自定義View須知\h4.4.3自定義View示例\h4.4.4自定義View的思想\h第5章理解RemoteViews\h5.1RemoteViews的應(yīng)用\h5.1.1RemoteViews在通知欄上的應(yīng)用\h5.1.2RemoteViews在桌面小部件上的應(yīng)用\h5.1.3PendingIntent概述\h5.2RemoteViews的內(nèi)部機(jī)制\h5.3RemoteViews的意義\h第6章Android的Drawable\h6.1Drawable簡介\h6.2Drawable的分類\h6.2.1BitmapDrawable\h6.2.2ShapeDrawable\h6.2.3LayerDrawable\h6.2.4StateListDrawable\h6.2.5LevelListDrawable\h6.2.6TransitionDrawable\h6.2.7InsetDrawable\h6.2.8ScaleDrawable\h6.2.9ClipDrawable\h6.3自定義Drawable\h第7章Android動(dòng)畫深入分析\h7.1View動(dòng)畫\h7.1.1View動(dòng)畫的種類\h7.1.2自定義View動(dòng)畫\h7.1.3幀動(dòng)畫\h7.2View動(dòng)畫的特殊使用場(chǎng)景\h7.2.1LayoutAnimation\h7.2.2Activity的切換效果\h7.3屬性動(dòng)畫\h7.3.1使用屬性動(dòng)畫\h7.3.2理解插值器和估值器\h7.3.3屬性動(dòng)畫的監(jiān)聽器\h7.3.4對(duì)任意屬性做動(dòng)畫\h7.3.5屬性動(dòng)畫的工作原理\h7.4使用動(dòng)畫的注意事項(xiàng)\h第8章理解Window和WindowManager\h8.1Window和WindowManager\h8.2Window的內(nèi)部機(jī)制\h8.2.1Window的添加過程\h8.2.2Window的刪除過程\h8.2.3Window的更新過程\h8.3Window的創(chuàng)建過程\h8.3.1Activity的Window創(chuàng)建過程\h8.3.2Dialog的Window創(chuàng)建過程\h8.3.3Toast的Window創(chuàng)建過程\h第9章四大組件的工作過程\h9.1四大組件的運(yùn)行狀態(tài)\h9.2Activity的工作過程\h9.3Service的工作過程\h9.3.1Service的啟動(dòng)過程\h9.3.2Service的綁定過程\h9.4BroadcastReceiver的工作過程\h9.4.1廣播的注冊(cè)過程\h9.4.2廣播的發(fā)送和接收過程\h9.5ContentProvider的工作過程\h第10章Android的消息機(jī)制\h10.1Android的消息機(jī)制概述\h10.2Android的消息機(jī)制分析\h10.2.1ThreadLocal的工作原理\h10.2.2消息隊(duì)列的工作原理\h10.2.3Looper的工作原理\h10.2.4Handler的工作原理\h10.3主線程的消息循環(huán)\h第11章Android的線程和線程池\h11.1主線程和子線程\h11.2Android中的線程形態(tài)\h11.2.1AsyncTask\h11.2.2AsyncTask的工作原理\h11.2.3HandlerThread\h11.2.4IntentService\h11.3Android中的線程池\h11.3.1ThreadPoolExecutor\h11.3.2線程池的分類\h第12章Bitmap的加載和Cache\h12.1Bitmap的高效加載\h12.2Android中的緩存策略\h12.2.1LruCache\h12.2.2DiskLruCache\h12.2.3ImageLoader的實(shí)現(xiàn)\h12.3ImageLoader的使用\h12.3.1照片墻效果\h12.3.2優(yōu)化列表的卡頓現(xiàn)象\h第13章綜合技術(shù)\h13.1使用CrashHandler來獲取應(yīng)用的crash信息\h13.2使用multidex來解決方法數(shù)越界\h13.3Android的動(dòng)態(tài)加載技術(shù)\h13.4反編譯初步\h13.4.1使用dex2jar和jd-gui反編譯apk\h13.4.2使用apktool對(duì)apk進(jìn)行二次打包\h第14章JNI和NDK編程\h14.1JNI的開發(fā)流程\h14.2NDK的開發(fā)流程\h14.3JNI的數(shù)據(jù)類型和類型簽名\h14.4JNI調(diào)用Java方法的流程\h第15章Android性能優(yōu)化\h15.1Android的性能優(yōu)化方法\h15.1.1布局優(yōu)化\h15.1.2繪制優(yōu)化\h15.1.3內(nèi)存泄露優(yōu)化\h15.1.4響應(yīng)速度優(yōu)化和ANR日志分析\h15.1.5ListView和Bitmap優(yōu)化\h15.1.6線程優(yōu)化\h15.1.7一些性能優(yōu)化建議\h15.2內(nèi)存泄露分析之MAT工具\(yùn)h15.3提高程序的可維護(hù)性注:原文檔電子版,非掃描,需要的請(qǐng)下載本文檔后留言謝謝。第1章Activity的生命周期和啟動(dòng)模式作為本書的第1章,本章主要介紹Activity相關(guān)的一些內(nèi)容。Activity作為四大組件之首,是使用最為頻繁的一種組件,中文直接翻譯為“活動(dòng)”,但是筆者認(rèn)為這種翻譯有些生硬,如果翻譯成界面就會(huì)更好理解。正常情況下,除了Window、Dialog和Toast,我們能見到的界面的確只有Activity。Activity是如此重要,以至于本書開篇就不得不講到它。當(dāng)然,由于本書的定位為進(jìn)階書,所以不會(huì)介紹如何啟動(dòng)Activity這類入門知識(shí),本章的側(cè)重點(diǎn)是Activity在使用過程中的一些不容易搞清楚的概念,主要包括生命周期和啟動(dòng)模式以及IntentFilter的匹配規(guī)則分析。其中Activity在異常情況下的生命周期是十分微妙的,至于Activity的啟動(dòng)模式和形形色色的Flags更是讓初學(xué)者摸不到頭腦,就連隱式啟動(dòng)Activity中也有著復(fù)雜的Intent匹配過程,不過不用擔(dān)心,本章接下來將一一解開這些疑難問題的神秘面紗。1.1Activity的生命周期全面分析本節(jié)將Activity的生命周期分為兩部分內(nèi)容,一部分是典型情況下的生命周期,另一部分是異常情況下的生命周期。所謂典型情況下的生命周期,是指在有用戶參與的情況下,Activity所經(jīng)過的生命周期的改變;而異常情況下的生命周期是指Activity被系統(tǒng)回收或者由于當(dāng)前設(shè)備的Configuration發(fā)生改變從而導(dǎo)致Activity被銷毀重建,異常情況下的生命周期的關(guān)注點(diǎn)和典型情況下略有不同。1.1.1典型情況下的生命周期分析在正常情況下,Activity會(huì)經(jīng)歷如下生命周期。(1)onCreate:表示Activity正在被創(chuàng)建,這是生命周期的第一個(gè)方法。在這個(gè)方法中,我們可以做一些初始化工作,比如調(diào)用setContentView去加載界面布局資源、初始化Activity所需數(shù)據(jù)等。(2)onRestart:表示Activity正在重新啟動(dòng)。一般情況下,當(dāng)當(dāng)前Activity從不可見重新變?yōu)榭梢姞顟B(tài)時(shí),onRestart就會(huì)被調(diào)用。這種情形一般是用戶行為所導(dǎo)致的,比如用戶按Home鍵切換到桌面或者用戶打開了一個(gè)新的Activity,這時(shí)當(dāng)前的Activity就會(huì)暫停,也就是onPause和onStop被執(zhí)行了,接著用戶又回到了這個(gè)Activity,就會(huì)出現(xiàn)這種情況。(3)onStart:表示Activity正在被啟動(dòng),即將開始,這時(shí)Activity已經(jīng)可見了,但是還沒有出現(xiàn)在前臺(tái),還無法和用戶交互。這個(gè)時(shí)候其實(shí)可以理解為Activity已經(jīng)顯示出來了,但是我們還看不到。(4)onResume:表示Activity已經(jīng)可見了,并且出現(xiàn)在前臺(tái)并開始活動(dòng)。要注意這個(gè)和onStart的對(duì)比,onStart和onResume都表示Activity已經(jīng)可見,但是onStart的時(shí)候Activity還在后臺(tái),onResume的時(shí)候Activity才顯示到前臺(tái)。(5)onPause:表示Activity正在停止,正常情況下,緊接著onStop就會(huì)被調(diào)用。在特殊情況下,如果這個(gè)時(shí)候快速地再回到當(dāng)前Activity,那么onResume會(huì)被調(diào)用。筆者的理解是,這種情況屬于極端情況,用戶操作很難重現(xiàn)這一場(chǎng)景。此時(shí)可以做一些存儲(chǔ)數(shù)據(jù)、停止動(dòng)畫等工作,但是注意不能太耗時(shí),因?yàn)檫@會(huì)影響到新Activity的顯示,onPause必須先執(zhí)行完,新Activity的onResume才會(huì)執(zhí)行。(6)onStop:表示Activity即將停止,可以做一些稍微重量級(jí)的回收工作,同樣不能太耗時(shí)。(7)onDestroy:表示Activity即將被銷毀,這是Activity生命周期中的最后一個(gè)回調(diào),在這里,我們可以做一些回收工作和最終的資源釋放。正常情況下,Activity的常用生命周期就只有上面7個(gè),圖1-1更詳細(xì)地描述了Activity各種生命周期的切換過程。圖1-1Activity生命周期的切換過程針對(duì)圖1-1,這里再附加一下具體說明,分如下幾種情況。(1)針對(duì)一個(gè)特定的Activity,第一次啟動(dòng),回調(diào)如下:onCreate->onStart->onResume。(2)當(dāng)用戶打開新的Activity或者切換到桌面的時(shí)候,回調(diào)如下:onPause->onStop。這里有一種特殊情況,如果新Activity采用了透明主題,那么當(dāng)前Activity不會(huì)回調(diào)onStop。(3)當(dāng)用戶再次回到原Activity時(shí),回調(diào)如下:onRestart->onStart->onResume。(4)當(dāng)用戶按back鍵回退時(shí),回調(diào)如下:onPause->onStop->onDestroy。(5)當(dāng)Activity被系統(tǒng)回收后再次打開,生命周期方法回調(diào)過程和(1)一樣,注意只是生命周期方法一樣,不代表所有過程都一樣,這個(gè)問題在下一節(jié)會(huì)詳細(xì)說明。(6)從整個(gè)生命周期來說,onCreate和onDestroy是配對(duì)的,分別標(biāo)識(shí)著Activity的創(chuàng)建和銷毀,并且只可能有一次調(diào)用。從Activity是否可見來說,onStart和onStop是配對(duì)的,隨著用戶的操作或者設(shè)備屏幕的點(diǎn)亮和熄滅,這兩個(gè)方法可能被調(diào)用多次;從Activity是否在前臺(tái)來說,onResume和onPause是配對(duì)的,隨著用戶操作或者設(shè)備屏幕的點(diǎn)亮和熄滅,這兩個(gè)方法可能被調(diào)用多次。這里提出2個(gè)問題,不知道大家是否清楚。問題1:onStart和onResume、onPause和onStop從描述上來看差不多,對(duì)我們來說有什么實(shí)質(zhì)的不同呢?問題2:假設(shè)當(dāng)前Activity為A,如果這時(shí)用戶打開一個(gè)新ActivityB,那么B的onResume和A的onPause哪個(gè)先執(zhí)行呢?先說第一個(gè)問題,從實(shí)際使用過程來說,onStart和onResume、onPause和onStop看起來的確差不多,甚至我們可以只保留其中一對(duì),比如只保留onStart和onStop。既然如此,那為什么Android系統(tǒng)還要提供看起來重復(fù)的接口呢?根據(jù)上面的分析,我們知道,這兩個(gè)配對(duì)的回調(diào)分別表示不同的意義,onStart和onStop是從Activity是否可見這個(gè)角度來回調(diào)的,而onResume和onPause是從Activity是否位于前臺(tái)這個(gè)角度來回調(diào)的,除了這種區(qū)別,在實(shí)際使用中沒有其他明顯區(qū)別。第二個(gè)問題可以從Android源碼里得到解釋。關(guān)于Activity的工作原理在本書后續(xù)章節(jié)會(huì)進(jìn)行介紹,這里我們先大概了解即可。從Activity的啟動(dòng)過程來看,我們來看一下系統(tǒng)源碼。Activity的啟動(dòng)過程的源碼相當(dāng)復(fù)雜,涉及Instrumentation、ActivityThread和ActivityManagerService(下面簡稱AMS)。這里不詳細(xì)分析這一過程,簡單理解,啟動(dòng)Activity的請(qǐng)求會(huì)由Instrumentation來處理,然后它通過Binder向AMS發(fā)請(qǐng)求,AMS內(nèi)部維護(hù)著一個(gè)ActivityStack并負(fù)責(zé)棧內(nèi)的Activity的狀態(tài)同步,AMS通過ActivityThread去同步Activity的狀態(tài)從而完成生命周期方法的調(diào)用。在ActivityStack中的resumeTopActivity-InnerLocked方法中,有這么一段代碼://Weneedtostartpausingthecurrentactivitysothetopone

//canberesumed...

booleandontWaitForPause=(.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING)!=0;

booleanpausing=mStackSupervisor.pauseBackStacks(userLeaving,true,dontWaitForPause);

if(mResumedActivity!=null){

pausing|=startPausingLocked(userLeaving,false,true,dontWait-ForPause);

if(DEBUG_STATES)Slog.d(TAG,"resumeTopActivityLocked:Pausing"+mResumedActivity);

}

從上述代碼可以看出,在新Activity啟動(dòng)之前,桟頂?shù)腁ctivity需要先onPause后,新Activity才能啟動(dòng)。最終,在ActivityStackSupervisor中的realStartActivityLocked方法會(huì)調(diào)用如下代碼。app.thread.scheduleLaunchActivity(newIntent(ent),r.appToken,

System.identityHashCode(r),,newConfiguration(mService.mConfiguration),

pat,r.task.voiceInteractor,app.repProcState,r.icicle,r.persistentState,

results,newIntents,!andResume,mService.isNextTransition-Forward(),

profilerInfo);

我們知道,這個(gè)app.thread的類型是IApplicationThread,而IApplicationThread的具體實(shí)現(xiàn)是ActivityThread中的ApplicationThread。所以,這段代碼實(shí)際上調(diào)到了ActivityThread的中,即ApplicationThread的scheduleLaunchActivity方法,而scheduleLaunchActivity方法最終會(huì)完成新Activity的onCreate、onStart、onResume的調(diào)用過程。因此,可以得出結(jié)論,是舊Activity先onPause,然后新Activity再啟動(dòng)。至于ApplicationThread的scheduleLaunchActivity方法為什么會(huì)完成新Activity的onCreate、onStart、onResume的調(diào)用過程,請(qǐng)看下面的代碼。scheduleLaunchActivity最終會(huì)調(diào)用如下方法,而如下方法的確會(huì)完成onCreate、onStart、onResume的調(diào)用過程。源碼:ActivityThread#handleLaunchActivityprivatevoidhandleLaunchActivity(ActivityClientRecordr,Intentcustom-Intent){

//Ifwearegettingreadytogcaftergoingtothebackground,well

//wearebackactivesoskipit.

unscheduleGcIdler();

mSomeActivitiesChanged=true;

if(filerInfo!=null){

mProfiler.setProfiler(filerInfo);

mProfiler.startProfiling();

}

//Makesurewearerunningwiththemostrecentconfig.

handleConfigurationChanged(null,null);

if(localLOGV)Slog.v(

TAG,"Handlinglaunchof"+r);

//這里新Activity被創(chuàng)建出來,其onCreate和onStart會(huì)被調(diào)用

Activitya=performLaunchActivity(r,customIntent);

if(a!=null){

r.createdConfig=newConfiguration(mConfiguration);

BundleoldState=r.state;

//這里新Activity的onResume會(huì)被調(diào)用

handleResumeActivity(r.token,false,r.isForward,

!r.activity.mFinished&&!r.startsNotResumed);

//省略

}

從上面的分析可以看出,當(dāng)新啟動(dòng)一個(gè)Activity的時(shí)候,舊Activity的onPause會(huì)先執(zhí)行,然后才會(huì)啟動(dòng)新的Activity。到底是不是這樣呢?我們寫個(gè)例子驗(yàn)證一下,如下是2個(gè)Activity的代碼,在MainActivity中單擊按鈕可以跳轉(zhuǎn)到SecondActivity,同時(shí)為了分析我們的問題,在生命周期方法中打印出了日志,通過日志我們就能看出它們的調(diào)用順序。代碼:MainActivity.javapublicclassMainActivityextendsActivity{

privatestaticfinalStringTAG="MainActivity";

//省略

@Override

protectedvoidonPause(){

super.onPause();

Log.d(TAG,"onPause");

}

@Override

protectedvoidonStop(){

super.onStop();

Log.d(TAG,"onStop");

}

}

代碼:SecondActivity.javapublicclassSecondActivityextendsActivity{

privatestaticfinalStringTAG="SecondActivity";

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_second);

Log.d(TAG,"onCreate");

}

@Override

protectedvoidonStart(){

super.onStart();

Log.d(TAG,"onStart");

}

@Override

protectedvoidonResume(){

super.onResume();

Log.d(TAG,"onResume");

}

}

我們來看一下log,是不是和我們上面分析的一樣,如圖1-2所示。圖1-2Activity生命周期方法的回調(diào)順序通過圖1-2可以發(fā)現(xiàn),舊Activity的onPause先調(diào)用,然后新Activity才啟動(dòng),這也證實(shí)了我們上面的分析過程。也許有人會(huì)問,你只是分析了Android5.0的源碼,你怎么知道所有版本的源碼都是相同邏輯呢?關(guān)于這個(gè)問題,我們的確不大可能把所有版本的源碼都分析一遍,但是作為Android運(yùn)行過程的基本機(jī)制,隨著版本的更新并不會(huì)有大的調(diào)整,因?yàn)锳ndroid系統(tǒng)也需要兼容性,不能說在不同版本上同一個(gè)運(yùn)行機(jī)制有著截然不同的表現(xiàn)。關(guān)于這一點(diǎn)我們需要把握一個(gè)度,就是對(duì)于Android運(yùn)行的基本機(jī)制在不同Android版本上具有延續(xù)性。從另一個(gè)角度來說,Android官方文檔對(duì)onPause的解釋有這么一句:不能在onPause中做重量級(jí)的操作,因?yàn)楸仨歰nPause執(zhí)行完成以后新Activity才能Resume,從這一點(diǎn)也能間接證明我們的結(jié)論。通過分析這個(gè)問題,我們知道onPause和onStop都不能執(zhí)行耗時(shí)的操作,尤其是onPause,這也意味著,我們應(yīng)當(dāng)盡量在onStop中做操作,從而使得新Activity盡快顯示出來并切換到前臺(tái)。1.1.2異常情況下的生命周期分析上一節(jié)我們分析了典型情況下Activity的生命周期,本節(jié)我們接著分析Activity在異常情況下的生命周期。我們知道,Activity除了受用戶操作所導(dǎo)致的正常的生命周期方法調(diào)度,還有一些異常情況,比如當(dāng)資源相關(guān)的系統(tǒng)配置發(fā)生改變以及系統(tǒng)內(nèi)存不足時(shí),Activity就可能被殺死。下面我們具體分析這兩種情況。1.情況1:資源相關(guān)的系統(tǒng)配置發(fā)生改變導(dǎo)致Activity被殺死并重新創(chuàng)建理解這個(gè)問題,我們首先要對(duì)系統(tǒng)的資源加載機(jī)制有一定了解,這里不詳細(xì)分析系統(tǒng)的資源加載機(jī)制,只是簡單說明一下。拿最簡單的圖片來說,當(dāng)我們把一張圖片放在drawable目錄后,就可以通過Resources去獲取這張圖片。同時(shí)為了兼容不同的設(shè)備,我們可能還需要在其他一些目錄放置不同的圖片,比如drawable-mdpi、drawable-hdpi、drawable-land等。這樣,當(dāng)應(yīng)用程序啟動(dòng)時(shí),系統(tǒng)就會(huì)根據(jù)當(dāng)前設(shè)備的情況去加載合適的Resources資源,比如說橫屏手機(jī)和豎屏手機(jī)會(huì)拿到兩張不同的圖片(設(shè)定了landscape或者portrait狀態(tài)下的圖片)。比如說當(dāng)前Activity處于豎屏狀態(tài),如果突然旋轉(zhuǎn)屏幕,由于系統(tǒng)配置發(fā)生了改變,在默認(rèn)情況下,Activity就會(huì)被銷毀并且重新創(chuàng)建,當(dāng)然我們也可以阻止系統(tǒng)重新創(chuàng)建我們的Activity。在默認(rèn)情況下,如果我們的Activity不做特殊處理,那么當(dāng)系統(tǒng)配置發(fā)生改變后,Activity就會(huì)被銷毀并重新創(chuàng)建,其生命周期如圖1-3所示。圖1-3異常情況下Activity的重建過程當(dāng)系統(tǒng)配置發(fā)生改變后,Activity會(huì)被銷毀,其onPause、onStop、onDestroy均會(huì)被調(diào)用,同時(shí)由于Activity是在異常情況下終止的,系統(tǒng)會(huì)調(diào)用onSaveInstanceState來保存當(dāng)前Activity的狀態(tài)。這個(gè)方法的調(diào)用時(shí)機(jī)是在onStop之前,它和onPause沒有既定的時(shí)序關(guān)系,它既可能在onPause之前調(diào)用,也可能在onPause之后調(diào)用。需要強(qiáng)調(diào)的一點(diǎn)是,這個(gè)方法只會(huì)出現(xiàn)在Activity被異常終止的情況下,正常情況下系統(tǒng)不會(huì)回調(diào)這個(gè)方法。當(dāng)Activity被重新創(chuàng)建后,系統(tǒng)會(huì)調(diào)用onRestoreInstanceState,并且把Activity銷毀時(shí)onSaveInstanceState方法所保存的Bundle對(duì)象作為參數(shù)同時(shí)傳遞給onRestoreInstanceState和onCreate方法。因此,我們可以通過onRestoreInstanceState和onCreate方法來判斷Activity是否被重建了,如果被重建了,那么我們就可以取出之前保存的數(shù)據(jù)并恢復(fù),從時(shí)序上來說,onRestoreInstanceState的調(diào)用時(shí)機(jī)在onStart之后。同時(shí),我們要知道,在onSaveInstanceState和onRestoreInstanceState方法中,系統(tǒng)自動(dòng)為我們做了一定的恢復(fù)工作。當(dāng)Activity在異常情況下需要重新創(chuàng)建時(shí),系統(tǒng)會(huì)默認(rèn)為我們保存當(dāng)前Activity的視圖結(jié)構(gòu),并且在Activity重啟后為我們恢復(fù)這些數(shù)據(jù),比如文本框中用戶輸入的數(shù)據(jù)、ListView滾動(dòng)的位置等,這些View相關(guān)的狀態(tài)系統(tǒng)都能夠默認(rèn)為我們恢復(fù)。具體針對(duì)某一個(gè)特定的View系統(tǒng)能為我們恢復(fù)哪些數(shù)據(jù),我們可以查看View的源碼。和Activity一樣,每個(gè)View都有onSaveInstanceState和onRestoreInstanceState這兩個(gè)方法,看一下它們的具體實(shí)現(xiàn),就能知道系統(tǒng)能夠自動(dòng)為每個(gè)View恢復(fù)哪些數(shù)據(jù)。關(guān)于保存和恢復(fù)View層次結(jié)構(gòu),系統(tǒng)的工作流程是這樣的:首先Activity被意外終止時(shí),Activity會(huì)調(diào)用onSaveInstanceState去保存數(shù)據(jù),然后Activity會(huì)委托Window去保存數(shù)據(jù),接著Window再委托它上面的頂級(jí)容器去保存數(shù)據(jù)。頂層容器是一個(gè)ViewGroup,一般來說它很可能是DecorView。最后頂層容器再去一一通知它的子元素來保存數(shù)據(jù),這樣整個(gè)數(shù)據(jù)保存過程就完成了。可以發(fā)現(xiàn),這是一種典型的委托思想,上層委托下層、父容器委托子元素去處理一件事情,這種思想在Android中有很多應(yīng)用,比如View的繪制過程、事件分發(fā)等都是采用類似的思想。至于數(shù)據(jù)恢復(fù)過程也是類似的,這里就不再重復(fù)介紹了。接下來舉個(gè)例子,拿TextView來說,我們分析一下它到底保存了哪些數(shù)據(jù)。源碼:TextView#onSaveInstanceState@Override

publicParcelableonSaveInstanceState(){

ParcelablesuperState=super.onSaveInstanceState();

//Savestateifweareforcedto

booleansave=mFreezesText;

intstart=0;

intend=0;

if(mText!=null){

start=getSelectionStart();

end=getSelectionEnd();

if(start=>0||end=>0){

//Orsavestateifthereisaselection

save=true;

}

}

if(save){

SavedStatess=newSavedState(superState);

//XXXShouldalsosavethecurrentscrollposition!

ss.selStart=start;

ss.selEnd=end;

if(mTextinstanceofSpanned){

Spannablesp=newSpannableStringBuilder(mText);

if(mEditor!=null){

removeMisspelledSpans(sp);

sp.removeSpan(mEditor.mSuggestionRangeSpan);

}

ss.text=sp;

}else{

ss.text=mText.toString();

}

if(isFocused()&&start=>0&&end=>0){

ss.frozenWithFocus=true;

}

ss.error=getError();

returnss;

}

returnsuperState;

}

從上述源碼可以很容易看出,TextView保存了自己的文本選中狀態(tài)和文本內(nèi)容,并且通過查看其onRestoreInstanceState方法的源碼,可以發(fā)現(xiàn)它的確恢復(fù)了這些數(shù)據(jù),具體源碼就不再貼出了,讀者可以去看看源碼。下面我們看一個(gè)實(shí)際的例子,來對(duì)比一下Activity正常終止和異常終止的不同,同時(shí)驗(yàn)證系統(tǒng)的數(shù)據(jù)恢復(fù)能力。為了方便,我們選擇旋轉(zhuǎn)屏幕來異常終止Activity,如圖1-4所示。圖1-4Activity旋轉(zhuǎn)屏幕后數(shù)據(jù)的保存和恢復(fù)通過圖1-4可以看出,在我們選擇屏幕以后,Activity被銷毀后重新創(chuàng)建,我們輸入的文本“這是測(cè)試文本”被正確地還原,這說明系統(tǒng)的確能夠自動(dòng)地做一些View層次結(jié)構(gòu)方面的數(shù)據(jù)存儲(chǔ)和恢復(fù)。下面再用一個(gè)例子,來驗(yàn)證我們自己做數(shù)據(jù)存儲(chǔ)和恢復(fù)的情況,代碼如下:@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

if(savedInstanceState!=null){

Stringtest=savedInstanceState.getString("extra_test");

Log.d(TAG,"[onCreate]restoreextra_test:"+test);

}

}

@Override

protectedvoidonSaveInstanceState(BundleoutState){

super.onSaveInstanceState(outState);

Log.d(TAG,"onSaveInstanceState");

outState.putString("extra_test","test");

}

@Override

protectedvoidonRestoreInstanceState(BundlesavedInstanceState){

super.onRestoreInstanceState(savedInstanceState);

Stringtest=savedInstanceState.getString("extra_test");

Log.d(TAG,"[onRestoreInstanceState]restoreextra_test:"+test);

}

上面的代碼很簡單,首先我們?cè)趏nSaveInstanceState中存儲(chǔ)一個(gè)字符串,然后當(dāng)Activity被銷毀并重新創(chuàng)建后,我們?cè)偃カ@取之前存儲(chǔ)的字符串。接收的位置可以選擇onRestoreInstanceState或者onCreate,二者的區(qū)別是:onRestoreInstanceState一旦被調(diào)用,其參數(shù)BundlesavedInstanceState一定是有值的,我們不用額外地判斷是否為空;但是onCreate不行,onCreate如果是正常啟動(dòng)的話,其參數(shù)BundlesavedInstanceState為null,所以必須要額外判斷。這兩個(gè)方法我們選擇任意一個(gè)都可以進(jìn)行數(shù)據(jù)恢復(fù),但是官方文檔的建議是采用onRestoreInstanceState去恢復(fù)數(shù)據(jù)。下面我們看一下運(yùn)行的日志,如圖1-5所示。圖1-5系統(tǒng)日志如圖1-5所示,Activity被銷毀了以后調(diào)用了onSaveInstanceState來保存數(shù)據(jù),重新創(chuàng)建以后在onCreate和onRestoreInstanceState中都能夠正確地恢復(fù)我們之前存儲(chǔ)的字符串。這個(gè)例子很好地證明了上面我們的分析結(jié)論。針對(duì)onSaveInstanceState方法還有一點(diǎn)需要說明,那就是系統(tǒng)只會(huì)在Activity即將被銷毀并且有機(jī)會(huì)重新顯示的情況下才會(huì)去調(diào)用它??紤]這么一種情況,當(dāng)Activity正常銷毀的時(shí)候,系統(tǒng)不會(huì)調(diào)用onSaveInstanceState,因?yàn)楸讳N毀的Activity不可能再次被顯示。這句話不好理解,但是我們可以對(duì)比一下旋轉(zhuǎn)屏幕所造成的Activity異常銷毀,這個(gè)過程和正常停止Activity是不一樣的,因?yàn)樾D(zhuǎn)屏幕后,Activity被銷毀的同時(shí)會(huì)立刻創(chuàng)建新的Activity實(shí)例,這個(gè)時(shí)候Activity有機(jī)會(huì)再次立刻展示,所以系統(tǒng)要進(jìn)行數(shù)據(jù)存儲(chǔ)。這里可以簡單地這么理解,系統(tǒng)只在Activity異常終止的時(shí)候才會(huì)調(diào)用onSaveInstanceState和onRestoreInstanceState來存儲(chǔ)和恢復(fù)數(shù)據(jù),其他情況不會(huì)觸發(fā)這個(gè)過程。2.情況2:資源內(nèi)存不足導(dǎo)致低優(yōu)先級(jí)的Activity被殺死這種情況我們不好模擬,但是其數(shù)據(jù)存儲(chǔ)和恢復(fù)過程和情況1完全一致。這里我們描述一下Activity的優(yōu)先級(jí)情況。Activity按照優(yōu)先級(jí)從高到低,可以分為如下三種:(1)前臺(tái)Activity——正在和用戶交互的Activity,優(yōu)先級(jí)最高。(2)可見但非前臺(tái)Activity——比如Activity中彈出了一個(gè)對(duì)話框,導(dǎo)致Activity可見但是位于后臺(tái)無法和用戶直接交互。(3)后臺(tái)Activity——已經(jīng)被暫停的Activity,比如執(zhí)行了onStop,優(yōu)先級(jí)最低。當(dāng)系統(tǒng)內(nèi)存不足時(shí),系統(tǒng)就會(huì)按照上述優(yōu)先級(jí)去殺死目標(biāo)Activity所在的進(jìn)程,并在后續(xù)通過onSaveInstanceState和onRestoreInstanceState來存儲(chǔ)和恢復(fù)數(shù)數(shù)據(jù)。如果一個(gè)進(jìn)程中沒有四大組件在執(zhí)行,那么這個(gè)進(jìn)程將很快被系統(tǒng)殺死,因此,一些后臺(tái)工作不適合脫離四大組件而獨(dú)自運(yùn)行在后臺(tái)中,這樣進(jìn)程很容易被殺死。比較好的方法是將后臺(tái)工作放入Service中從而保證進(jìn)程有一定的優(yōu)先級(jí),這樣就不會(huì)輕易地被系統(tǒng)殺死。上面分析了系統(tǒng)的數(shù)據(jù)存儲(chǔ)和恢復(fù)機(jī)制,我們知道,當(dāng)系統(tǒng)配置發(fā)生改變后,Activity會(huì)被重新創(chuàng)建,那么有沒有辦法不重新創(chuàng)建呢?答案是有的,接下來我們就來分析這個(gè)問題。系統(tǒng)配置中有很多內(nèi)容,如果當(dāng)某項(xiàng)內(nèi)容發(fā)生改變后,我們不想系統(tǒng)重新創(chuàng)建Activity,可以給Activity指定configChanges屬性。比如不想讓Activity在屏幕旋轉(zhuǎn)的時(shí)候重新創(chuàng)建,就可以給configChanges屬性添加orientation這個(gè)值,如下所示。android:configChanges="orientation"

如果我們想指定多個(gè)值,可以用“|”連接起來,比如android:configChanges="orientation|keyboardHidden"。系統(tǒng)配置中所含的項(xiàng)目是非常多的,下面介紹每個(gè)項(xiàng)目的含義,如表1-1所示。表1-1configChanges的項(xiàng)目和含義從表1-1可以知道,如果我們沒有在Activity的configChanges屬性中指定該選項(xiàng)的話,當(dāng)配置發(fā)生改變后就會(huì)導(dǎo)致Activity重新創(chuàng)建。上面表格中的項(xiàng)目很多,但是我們常用的只有l(wèi)ocale、orientation和keyboardHidden這三個(gè)選項(xiàng),其他很少使用。需要注意的是screenSize和smallestScreenSize,它們兩個(gè)比較特殊,它們的行為和編譯選項(xiàng)有關(guān),但和運(yùn)行環(huán)境無關(guān)。下面我們?cè)倏匆粋€(gè)demo,看看當(dāng)我們指定了configChanges屬性后,Activity是否真的不會(huì)重新創(chuàng)建了。我們所要修改的代碼很簡單,只需要在AndroidMenifest.xml中加入Activity的聲明即可,代碼如下:<uses-sdk

android:minSdkVersion="8"

android:targetSdkVersion="19"/>

<activity

android:name="com.ryg.chapter_1.MainActivity"

android:configChanges="orientation|screenSize"

android:label="@string/app_name">

<intent-filter>

<actionandroid:name="ent.action.MAIN"/>

<categoryandroid:name="ent.category.LAUNCHER"/>

</intent-filter>

</activity>

@Override

publicvoidonConfigurationChanged(ConfigurationnewConfig){

super.onConfigurationChanged(newConfig);

Log.d(TAG,"onConfigurationChanged,newOrientation:"+newConfig.orientation);

}

需要說明的是,由于編譯時(shí)筆者指定的minSdkVersion和targetSdkVersion有一個(gè)大于13,所以為了防止旋轉(zhuǎn)屏幕時(shí)Activity重啟,除了orientation,我們還要加上screenSize,原因在上面的表格里已經(jīng)說明了。其他代碼還是不變,運(yùn)行后看看log,如圖1-6所示。圖1-6系統(tǒng)日志由上面的日志可見,Activity的確沒有重新創(chuàng)建,并且也沒有調(diào)用onSaveInstanceState和onRestoreInstanceState來存儲(chǔ)和恢復(fù)數(shù)據(jù),取而代之的是系統(tǒng)調(diào)用了Activity的onConfigurationChanged方法,這個(gè)時(shí)候我們就可以做一些自己的特殊處理了。1.2Activity的啟動(dòng)模式上一節(jié)介紹了Activity在標(biāo)準(zhǔn)情況下和異常情況下的生命周期,我們對(duì)Activity的生命周期應(yīng)該有了深入的了解。除了Activity的生命周期外,Activity的啟動(dòng)模式也是一個(gè)難點(diǎn),原因是形形色色的啟動(dòng)模式和標(biāo)志位實(shí)在是太容易被混淆了,但是Activity作為四大組件之首,它的的確確非常重要,有時(shí)候?yàn)榱藵M足項(xiàng)目的特殊需求,就必須使用Activity的啟動(dòng)模式,所以我們必須要搞清楚它的啟動(dòng)模式和標(biāo)志位,本節(jié)將會(huì)一一介紹。1.2.1Activity的LaunchMode首先說一下Activity為什么需要啟動(dòng)模式。我們知道,在默認(rèn)情況下,當(dāng)我們多次啟動(dòng)同一個(gè)Activity的時(shí)候,系統(tǒng)會(huì)創(chuàng)建多個(gè)實(shí)例并把它們一一放入任務(wù)棧中,當(dāng)我們單擊back鍵,會(huì)發(fā)現(xiàn)這些Activity會(huì)一一回退。任務(wù)棧是一種“后進(jìn)先出”的棧結(jié)構(gòu),這個(gè)比較好理解,每按一下back鍵就會(huì)有一個(gè)Activity出棧,直到??諡橹?,當(dāng)棧中無任何Activity的時(shí)候,系統(tǒng)就會(huì)回收這個(gè)任務(wù)棧。關(guān)于任務(wù)棧的系統(tǒng)工作原理,這里暫時(shí)不做說明,在后續(xù)章節(jié)會(huì)專門介紹任務(wù)棧。知道了Activity的默認(rèn)啟動(dòng)模式以后,我們可能就會(huì)發(fā)現(xiàn)一個(gè)問題:多次啟動(dòng)同一個(gè)Activity,系統(tǒng)重復(fù)創(chuàng)建多個(gè)實(shí)例,這樣不是很傻嗎?這樣的確有點(diǎn)傻,Android在設(shè)計(jì)的時(shí)候不可能不考慮到這個(gè)問題,所以它提供了啟動(dòng)模式來修改系統(tǒng)的默認(rèn)行為。目前有四種啟動(dòng)模式:standard、singleTop、singleTask和singleInstance,下面先介紹各種啟動(dòng)模式的含義:(1)standard:標(biāo)準(zhǔn)模式,這也是系統(tǒng)的默認(rèn)模式。每次啟動(dòng)一個(gè)Activity都會(huì)重新創(chuàng)建一個(gè)新的實(shí)例,不管這個(gè)實(shí)例是否已經(jīng)存在。被創(chuàng)建的實(shí)例的生命周期符合典型情況下Activity的生命周期,如上節(jié)描述,它的onCreate、onStart、onResume都會(huì)被調(diào)用。這是一種典型的多實(shí)例實(shí)現(xiàn),一個(gè)任務(wù)棧中可以有多個(gè)實(shí)例,每個(gè)實(shí)例也可以屬于不同的任務(wù)棧。在這種模式下,誰啟動(dòng)了這個(gè)Activity,那么這個(gè)Activity就運(yùn)行在啟動(dòng)它的那個(gè)Activity所在的棧中。比如ActivityA啟動(dòng)了ActivityB(B是標(biāo)準(zhǔn)模式),那么B就會(huì)進(jìn)入到A所在的棧中。不知道讀者是否注意到,當(dāng)我們用ApplicationContext去啟動(dòng)standard模式的Activity的時(shí)候會(huì)報(bào)錯(cuò),錯(cuò)誤如下:E/AndroidRuntime(674):android.util.AndroidRuntimeException:CallingstartActivityfromoutsideofanActivitycontextrequirestheFLAG_ACTIVITY_NEW_TASKflag.Isthisreallywhatyouwant?

相信這句話讀者一定不陌生,這是因?yàn)閟tandard模式的Activity默認(rèn)會(huì)進(jìn)入啟動(dòng)它的Activity所屬的任務(wù)棧中,但是由于非Activity類型的Context(如ApplicationContext)并沒有所謂的任務(wù)棧,所以這就有問題了。解決這個(gè)問題的方法是為待啟動(dòng)Activity指定FLAG_ACTIVITY_NEW_TASK標(biāo)記位,這樣啟動(dòng)的時(shí)候就會(huì)為它創(chuàng)建一個(gè)新的任務(wù)棧,這個(gè)時(shí)候待啟動(dòng)Activity實(shí)際上是以singleTask模式啟動(dòng)的,讀者可以仔細(xì)體會(huì)。(2)singleTop:棧頂復(fù)用模式。在這種模式下,如果新Activity已經(jīng)位于任務(wù)棧的棧頂,那么此Activity不會(huì)被重新創(chuàng)建,同時(shí)它的onNewIntent方法會(huì)被回調(diào),通過此方法的參數(shù)我們可以取出當(dāng)前請(qǐng)求的信息。需要注意的是,這個(gè)Activity的onCreate、onStart不會(huì)被系統(tǒng)調(diào)用,因?yàn)樗]有發(fā)生改變。如果新Activity的實(shí)例已存在但不是位于棧頂,那么新Activity仍然會(huì)重新重建。舉個(gè)例子,假設(shè)目前棧內(nèi)的情況為ABCD,其中ABCD為四個(gè)Activity,A位于棧底,D位于棧頂,這個(gè)時(shí)候假設(shè)要再次啟動(dòng)D,如果D的啟動(dòng)模式為singleTop,那么棧內(nèi)的情況仍然為ABCD;如果D的啟動(dòng)模式為standard,那么由于D被重新創(chuàng)建,導(dǎo)致棧內(nèi)的情況就變?yōu)锳BCDD。(3)singleTask:棧內(nèi)復(fù)用模式。這是一種單實(shí)例模式,在這種模式下,只要Activity在一個(gè)棧中存在,那么多次啟動(dòng)此Activity都不會(huì)重新創(chuàng)建實(shí)例,和singleTop一樣,系統(tǒng)也會(huì)回調(diào)其onNewIntent。具體一點(diǎn),當(dāng)一個(gè)具有singleTask模式的Activity請(qǐng)求啟動(dòng)后,比如ActivityA,系統(tǒng)首先會(huì)尋找是否存在A想要的任務(wù)棧,如果不存在,就重新創(chuàng)建一個(gè)任務(wù)棧,然后創(chuàng)建A的實(shí)例后把A放到棧中。如果存在A所需的任務(wù)棧,這時(shí)要看A是否在棧中有實(shí)例存在,如果有實(shí)例存在,那么系統(tǒng)就會(huì)把A調(diào)到棧頂并調(diào)用它的onNewIntent方法,如果實(shí)例不存在,就創(chuàng)建A的實(shí)例并把A壓入棧中。舉幾個(gè)例子:比如目前任務(wù)棧S1中的情況為ABC,這個(gè)時(shí)候ActivityD以singleTask模式請(qǐng)求啟動(dòng),其所需要的任務(wù)棧為S2,由于S2和D的實(shí)例均不存在,所以系統(tǒng)會(huì)先創(chuàng)建任務(wù)棧S2,然后再創(chuàng)建D的實(shí)例并將其入棧到S2。另外一種情況,假設(shè)D所需的任務(wù)棧為S1,其他情況如上面例子1所示,那么由于S1已經(jīng)存在,所以系統(tǒng)會(huì)直接創(chuàng)建D的實(shí)例并將其入棧到S1。如果D所需的任務(wù)棧為S1,并且當(dāng)前任務(wù)棧S1的情況為ADBC,根據(jù)棧內(nèi)復(fù)用的原則,此時(shí)D不會(huì)重新創(chuàng)建,系統(tǒng)會(huì)把D切換到棧頂并調(diào)用其onNewIntent方法,同時(shí)由于singleTask默認(rèn)具有clearTop的效果,會(huì)導(dǎo)致棧內(nèi)所有在D上面的Activity全部出棧,于是最終S1中的情況為AD。這一點(diǎn)比較特殊,在后面還會(huì)對(duì)此種情況詳細(xì)地分析。通過上述3個(gè)例子,讀者應(yīng)該能比較清晰地理解singleTask的含義了。(4)singleInstance:單實(shí)例模式。這是一種加強(qiáng)的singleTask模式,它除了具有singleTask模式的所有特性外,還加強(qiáng)了一點(diǎn),那就是具有此種模式的Activity只能單獨(dú)地位于一個(gè)任務(wù)棧中,換句話說,比如ActivityA是singleInstance模式,當(dāng)A啟動(dòng)后,系統(tǒng)會(huì)為它創(chuàng)建一個(gè)新的任務(wù)棧,然后A獨(dú)自在這個(gè)新的任務(wù)棧中,由于棧內(nèi)復(fù)用的特性,后續(xù)的請(qǐng)求均不會(huì)創(chuàng)建新的Activity,除非這個(gè)獨(dú)特的任務(wù)棧被系統(tǒng)銷毀了。上面介紹了幾種啟動(dòng)模式,這里需要指出一種情況,我們假設(shè)目前有2個(gè)任務(wù)棧,前臺(tái)任務(wù)棧的情況為AB,而后臺(tái)任務(wù)棧的情況為CD,這里假設(shè)CD的啟動(dòng)模式均為singleTask?,F(xiàn)在請(qǐng)求啟動(dòng)D,那么整個(gè)后臺(tái)任務(wù)棧都會(huì)被切換到前臺(tái),這個(gè)時(shí)候整個(gè)后退列表變成了ABCD。當(dāng)用戶按back鍵的時(shí)候,列表中的Activity會(huì)一一出棧,如圖1-7所示。如果不是請(qǐng)求啟動(dòng)D而是啟動(dòng)C,那么情況就不一樣了,請(qǐng)看圖1-8,具體原因在本節(jié)后面會(huì)再進(jìn)行詳細(xì)分析。圖1-7任務(wù)棧示例1圖1-8任務(wù)棧示例2另外一個(gè)問題是,在singleTask啟動(dòng)模式中,多次提到某個(gè)Activity所需的任務(wù)棧,什么是Activity所需要的任務(wù)棧呢?這要從一個(gè)參數(shù)說起:TaskAffinity,可以翻譯為任務(wù)相關(guān)性。這個(gè)參數(shù)標(biāo)識(shí)了一個(gè)Activity所需要的任務(wù)棧的名字,默認(rèn)情況下,所有Activity所需的任務(wù)棧的名字為應(yīng)用的包名。當(dāng)然,我們可以為每個(gè)Activity都單獨(dú)指定TaskAffinity屬性,這個(gè)屬性值必須不能和包名相同,否則就相當(dāng)于沒有指定。TaskAffinity屬性主要和singleTask啟動(dòng)模式或者allowTaskReparenting屬性配對(duì)使用,在其他情況下沒有意義。另外,任務(wù)棧分為前臺(tái)任務(wù)棧和后臺(tái)任務(wù)棧,后臺(tái)任務(wù)棧中的Activity位于暫停狀態(tài),用戶可以通過切換將后臺(tái)任務(wù)棧再次調(diào)到前臺(tái)。當(dāng)TaskAffinity和singleTask啟動(dòng)模式配對(duì)使用的時(shí)候,它是具有該模式的Activity的目前任務(wù)棧的名字,待啟動(dòng)的Activity會(huì)運(yùn)行在名字和TaskAffinity相同的任務(wù)棧中。當(dāng)TaskAffinity和allowTaskReparenting結(jié)合的時(shí)候,這種情況比較復(fù)雜,會(huì)產(chǎn)生特殊的效果。當(dāng)一個(gè)應(yīng)用A啟動(dòng)了應(yīng)用B的某個(gè)Activity后,如果這個(gè)Activity的allowTaskReparenting屬性為true的話,那么當(dāng)應(yīng)用B被啟動(dòng)后,此Activity會(huì)直接從應(yīng)用A的任務(wù)棧轉(zhuǎn)移到應(yīng)用B的任務(wù)棧中。這還是很抽象,再具體點(diǎn),比如現(xiàn)在有2個(gè)應(yīng)用A和B,A啟動(dòng)了B的一個(gè)ActivityC,然后按Home鍵回到桌面,然后再單擊B的桌面圖標(biāo),這個(gè)時(shí)候并不是啟動(dòng)了B的主Activity,而是重新顯示了已經(jīng)被應(yīng)用A啟動(dòng)的ActivityC,或者說,C從A的任務(wù)棧轉(zhuǎn)移到了B的任務(wù)棧中。可以這么理解,由于A啟動(dòng)了C,這個(gè)時(shí)候C只能運(yùn)行在A的任務(wù)棧中,但是C屬于B應(yīng)用,正常情況下,它的TaskAffinity值肯定不可能和A的任務(wù)棧相同(因?yàn)榘煌?。所以,?dāng)B被啟動(dòng)后,B會(huì)創(chuàng)建自己的任務(wù)棧,這個(gè)時(shí)候系統(tǒng)發(fā)現(xiàn)C原本所想要的任務(wù)棧已經(jīng)被創(chuàng)建了,所以就把C從A的任務(wù)棧中轉(zhuǎn)移過來了。這種情況讀者可以寫個(gè)例子測(cè)試一下,這里就不做示例了。如何給Activity指定啟動(dòng)模式呢?有兩種方法,第一種是通過AndroidMenifest為Activity指定啟動(dòng)模式,如下所示。<activity

android:name="com.ryg.chapter_1.SecondActivity"

android:configChanges="screenLayout"

android:launchMode="singleTask"

android:label="@string/app_name"/>

另一種情況是通過在Intent中設(shè)置標(biāo)志位來為Activity指定啟動(dòng)模式,比如:Intentintent=newIntent();

intent.setClass(MainActivity.this,SecondActivity.class);

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivity(intent);

這兩種方式都可以為Activity指定啟動(dòng)模式,但是二者還是有區(qū)別的。首先,優(yōu)先級(jí)上,第二種方式的優(yōu)先級(jí)要高于第一種,當(dāng)兩種同時(shí)存在時(shí),以第二種方式為準(zhǔn);其次,上述兩種方式在限定范圍上有所不同,比如,第一種方式無法直接為Activity設(shè)定FLAG_ACTIVITY_CLEAR_TOP標(biāo)識(shí),而第二種方式無法為Activity指定singleInstance模式。關(guān)于Intent中為Activity指定的各種標(biāo)記位,在下面的小節(jié)中會(huì)繼續(xù)介紹。下面通過一個(gè)例子來體驗(yàn)啟動(dòng)模式的使用效果。還是前面的例子,這里我們把MainActivity的啟動(dòng)模式設(shè)為singleTask,然后重復(fù)啟動(dòng)它,看看是否會(huì)重復(fù)創(chuàng)建,代碼修改如下:<activity

android:name="com.ryg.chapter_1.MainActivity"

android:configChanges="orientation|screenSize"

android:label="@string/app_name"

android:launchMode="singleTask">

<intent-filter>

<actionandroid:name="ent.action.MAIN"/>

<categoryandroid:name="ent.category.LAUNCHER"/>

</intent-filter>

</activity>

@Override

protectedvoidonNewIntent(Intentintent){

super.onNewIntent(intent);

Log.d(TAG,"onNewIntent,time="+intent.getLongExtra("time",0));

}

findViewById(R.id.button1).setOnClickListener(newOnClickListener(){

@Override

publicvoidonClick(Viewv){

Intentintent=newIntent();

intent.setClass(MainActivity.this,MainActivity.class);

intent.putExtra("time",System.currentTimeMillis());

startActivity(intent);

}

});

根據(jù)上述修改,我們做如下操作,連續(xù)單擊三次按鈕啟動(dòng)3次MainActivity,算上原本的MainActvity的實(shí)例,正常情況下,任務(wù)棧中應(yīng)該有4個(gè)MainActivity的實(shí)例,但是我們?yōu)槠渲付藄ingleTask模式,現(xiàn)在來看一看到底有何不同。執(zhí)行adbshelldumpsysactivity命令:ACTIVITYMANAGERACTIVITIES(dumpsysactivityactivities)

Mainstack:

TaskRecord{41350dc8#9Acom.ryg.chapter_1}

Intent{cmp=com.ryg.chapter_1/.MainActivity(hasextras)}

Hist#1:ActivityRecord{412cc188com.ryg.chapter_1/.MainActivity}

Intent{act=ent.action.MAINcat=[ent.category.LAUNCHER]flg=0x

0cmp=com.ryg.chapter_1/.MainActivitybnds=[160,235][240,335]}

ProcessRecord{411e6898634:com.ryg.chapter_1/10052}

TaskRecord{4125abc8#2Acom.android.launcher}

Intent{act=ent.action.MAINcat=[ent.category.

HOME]flg=0x10000000

m.android.launcher/com.android.launcher2.Launcher}

Hist#0:ActivityRecord{412381f8com.android.launcher/com.android.

launcher2.Launcher}

Intent{act=ent.action.MAINcat=[ent.

category.HOME]flg=0x1000

p=com.android.launcher/com.android.launcher2.Launcher}

ProcessRecord{411d24c8214:com.android.launcher/10013}

Runningactivities(mostrecentfirst):

TaskRecord{41350dc8#9Acom.ryg.chapter_1}

Run#1:ActivityRecord{412cc188com.ryg.chapter_1/.MainActivity}

TaskRecord{4125abc8#2Acom.android.launcher}

Run#0:ActivityRecord{412381f8com.android.launcher/com.android.

launcher2.Launcher}

mResumedActivity:ActivityRecord{412cc188com.ryg.chapter_1/.MainActivity}

mFocusedActivity:ActivityRecord{412cc188com.ryg.chapter_1/.MainActivity}

Recenttasks:

*Recent#0:TaskRecord{41350dc8#9Acom.ryg.chapter_1}

*Recent#1:TaskRecord{4125abc8#2Acom.android.launcher}

*Recent#2:TaskRecord{412b60a0#5Acom.estrongs.android.pop.app.

InstallMonitorActivity}

從上面導(dǎo)出的Activity信息可以看出,盡管啟動(dòng)了4次MainActivity,但是它始終只有一個(gè)實(shí)例在任務(wù)棧中,從圖1-9的log可以看出,Activity的確沒有重新創(chuàng)建,只是暫停了一下,然后調(diào)用了onNewIntent,接著調(diào)用onResume就又繼續(xù)了。圖1-9系統(tǒng)日志現(xiàn)在我們?nèi)サ魋ingleTask,再來對(duì)比一下,還是同樣的操作,單擊三次按鈕啟動(dòng)MainActivity三次。執(zhí)行adbshelldumpsysactivity命令:ACTIVITYMANAGERACTIVITIES(dumpsysactivityactivities)

Mainstack:

TaskRecord{41325370#17Acom.ryg.chapter_1}

Intent{act=ent.action.MAINcat=[ent.category.

LAUNCHER]flg=0x100000

p=com.ryg.chapter_1/.MainActivity}

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論