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

下載本文檔

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

文檔簡介

Android開發(fā)藝術(shù)探索目錄\h第1章Activity的生命周期和啟動模式\h1.1Activity的生命周期全面分析\h1.1.1典型情況下的生命周期分析\h1.1.2異常情況下的生命周期分析\h1.2Activity的啟動模式\h1.2.1Activity的LaunchMode\h1.2.2Activity的Flags\h1.3IntentFilter的匹配規(guī)則\h第2章IPC機制\h2.1AndroidIPC簡介\h2.2Android中的多進(jìn)程模式\h2.2.1開啟多進(jìn)程模式\h2.2.2多進(jìn)程模式的運行機制\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ǔ)知識\h3.1.1什么是View\h3.1.2View的位置參數(shù)\h3.1.3MotionEvent和TouchSlop\h3.1.4VelocityTracker、GestureDetector和Scroller\h3.2View的滑動\h3.2.1使用scrollTo/scrollBy\h3.2.2使用動畫\h3.2.3改變布局參數(shù)\h3.2.4各種滑動方式的對比\h3.3彈性滑動\h3.3.1使用Scroller\h3.3.2通過動畫\h3.3.3使用延時策略\h3.4View的事件分發(fā)機制\h3.4.1點擊事件的傳遞規(guī)則\h3.4.2事件分發(fā)的源碼解析\h3.5View的滑動沖突\h3.5.1常見的滑動沖突場景\h3.5.2滑動沖突的處理規(guī)則\h3.5.3滑動沖突的解決方式\h第4章View的工作原理\h4.1初識ViewRoot和DecorView\h4.2理解MeasureSpec\h4.2.1MeasureSpec\h4.2.2MeasureSpec和LayoutParams的對應(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)部機制\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動畫深入分析\h7.1View動畫\h7.1.1View動畫的種類\h7.1.2自定義View動畫\h7.1.3幀動畫\h7.2View動畫的特殊使用場景\h7.2.1LayoutAnimation\h7.2.2Activity的切換效果\h7.3屬性動畫\h7.3.1使用屬性動畫\h7.3.2理解插值器和估值器\h7.3.3屬性動畫的監(jiān)聽器\h7.3.4對任意屬性做動畫\h7.3.5屬性動畫的工作原理\h7.4使用動畫的注意事項\h第8章理解Window和WindowManager\h8.1Window和WindowManager\h8.2Window的內(nèi)部機制\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四大組件的運行狀態(tài)\h9.2Activity的工作過程\h9.3Service的工作過程\h9.3.1Service的啟動過程\h9.3.2Service的綁定過程\h9.4BroadcastReceiver的工作過程\h9.4.1廣播的注冊過程\h9.4.2廣播的發(fā)送和接收過程\h9.5ContentProvider的工作過程\h第10章Android的消息機制\h10.1Android的消息機制概述\h10.2Android的消息機制分析\h10.2.1ThreadLocal的工作原理\h10.2.2消息隊列的工作原理\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的實現(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的動態(tài)加載技術(shù)\h13.4反編譯初步\h13.4.1使用dex2jar和jd-gui反編譯apk\h13.4.2使用apktool對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工具\h15.3提高程序的可維護性注:原文檔電子版,非掃描,需要的請下載本文檔后留言謝謝。第1章Activity的生命周期和啟動模式作為本書的第1章,本章主要介紹Activity相關(guān)的一些內(nèi)容。Activity作為四大組件之首,是使用最為頻繁的一種組件,中文直接翻譯為“活動”,但是筆者認(rèn)為這種翻譯有些生硬,如果翻譯成界面就會更好理解。正常情況下,除了Window、Dialog和Toast,我們能見到的界面的確只有Activity。Activity是如此重要,以至于本書開篇就不得不講到它。當(dāng)然,由于本書的定位為進(jìn)階書,所以不會介紹如何啟動Activity這類入門知識,本章的側(cè)重點是Activity在使用過程中的一些不容易搞清楚的概念,主要包括生命周期和啟動模式以及IntentFilter的匹配規(guī)則分析。其中Activity在異常情況下的生命周期是十分微妙的,至于Activity的啟動模式和形形色色的Flags更是讓初學(xué)者摸不到頭腦,就連隱式啟動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)注點和典型情況下略有不同。1.1.1典型情況下的生命周期分析在正常情況下,Activity會經(jīng)歷如下生命周期。(1)onCreate:表示Activity正在被創(chuàng)建,這是生命周期的第一個方法。在這個方法中,我們可以做一些初始化工作,比如調(diào)用setContentView去加載界面布局資源、初始化Activity所需數(shù)據(jù)等。(2)onRestart:表示Activity正在重新啟動。一般情況下,當(dāng)當(dāng)前Activity從不可見重新變?yōu)榭梢姞顟B(tài)時,onRestart就會被調(diào)用。這種情形一般是用戶行為所導(dǎo)致的,比如用戶按Home鍵切換到桌面或者用戶打開了一個新的Activity,這時當(dāng)前的Activity就會暫停,也就是onPause和onStop被執(zhí)行了,接著用戶又回到了這個Activity,就會出現(xiàn)這種情況。(3)onStart:表示Activity正在被啟動,即將開始,這時Activity已經(jīng)可見了,但是還沒有出現(xiàn)在前臺,還無法和用戶交互。這個時候其實可以理解為Activity已經(jīng)顯示出來了,但是我們還看不到。(4)onResume:表示Activity已經(jīng)可見了,并且出現(xiàn)在前臺并開始活動。要注意這個和onStart的對比,onStart和onResume都表示Activity已經(jīng)可見,但是onStart的時候Activity還在后臺,onResume的時候Activity才顯示到前臺。(5)onPause:表示Activity正在停止,正常情況下,緊接著onStop就會被調(diào)用。在特殊情況下,如果這個時候快速地再回到當(dāng)前Activity,那么onResume會被調(diào)用。筆者的理解是,這種情況屬于極端情況,用戶操作很難重現(xiàn)這一場景。此時可以做一些存儲數(shù)據(jù)、停止動畫等工作,但是注意不能太耗時,因為這會影響到新Activity的顯示,onPause必須先執(zhí)行完,新Activity的onResume才會執(zhí)行。(6)onStop:表示Activity即將停止,可以做一些稍微重量級的回收工作,同樣不能太耗時。(7)onDestroy:表示Activity即將被銷毀,這是Activity生命周期中的最后一個回調(diào),在這里,我們可以做一些回收工作和最終的資源釋放。正常情況下,Activity的常用生命周期就只有上面7個,圖1-1更詳細(xì)地描述了Activity各種生命周期的切換過程。圖1-1Activity生命周期的切換過程針對圖1-1,這里再附加一下具體說明,分如下幾種情況。(1)針對一個特定的Activity,第一次啟動,回調(diào)如下:onCreate->onStart->onResume。(2)當(dāng)用戶打開新的Activity或者切換到桌面的時候,回調(diào)如下:onPause->onStop。這里有一種特殊情況,如果新Activity采用了透明主題,那么當(dāng)前Activity不會回調(diào)onStop。(3)當(dāng)用戶再次回到原Activity時,回調(diào)如下:onRestart->onStart->onResume。(4)當(dāng)用戶按back鍵回退時,回調(diào)如下:onPause->onStop->onDestroy。(5)當(dāng)Activity被系統(tǒng)回收后再次打開,生命周期方法回調(diào)過程和(1)一樣,注意只是生命周期方法一樣,不代表所有過程都一樣,這個問題在下一節(jié)會詳細(xì)說明。(6)從整個生命周期來說,onCreate和onDestroy是配對的,分別標(biāo)識著Activity的創(chuàng)建和銷毀,并且只可能有一次調(diào)用。從Activity是否可見來說,onStart和onStop是配對的,隨著用戶的操作或者設(shè)備屏幕的點亮和熄滅,這兩個方法可能被調(diào)用多次;從Activity是否在前臺來說,onResume和onPause是配對的,隨著用戶操作或者設(shè)備屏幕的點亮和熄滅,這兩個方法可能被調(diào)用多次。這里提出2個問題,不知道大家是否清楚。問題1:onStart和onResume、onPause和onStop從描述上來看差不多,對我們來說有什么實質(zhì)的不同呢?問題2:假設(shè)當(dāng)前Activity為A,如果這時用戶打開一個新ActivityB,那么B的onResume和A的onPause哪個先執(zhí)行呢?先說第一個問題,從實際使用過程來說,onStart和onResume、onPause和onStop看起來的確差不多,甚至我們可以只保留其中一對,比如只保留onStart和onStop。既然如此,那為什么Android系統(tǒng)還要提供看起來重復(fù)的接口呢?根據(jù)上面的分析,我們知道,這兩個配對的回調(diào)分別表示不同的意義,onStart和onStop是從Activity是否可見這個角度來回調(diào)的,而onResume和onPause是從Activity是否位于前臺這個角度來回調(diào)的,除了這種區(qū)別,在實際使用中沒有其他明顯區(qū)別。第二個問題可以從Android源碼里得到解釋。關(guān)于Activity的工作原理在本書后續(xù)章節(jié)會進(jìn)行介紹,這里我們先大概了解即可。從Activity的啟動過程來看,我們來看一下系統(tǒng)源碼。Activity的啟動過程的源碼相當(dāng)復(fù)雜,涉及Instrumentation、ActivityThread和ActivityManagerService(下面簡稱AMS)。這里不詳細(xì)分析這一過程,簡單理解,啟動Activity的請求會由Instrumentation來處理,然后它通過Binder向AMS發(fā)請求,AMS內(nèi)部維護著一個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啟動之前,桟頂?shù)腁ctivity需要先onPause后,新Activity才能啟動。最終,在ActivityStackSupervisor中的realStartActivityLocked方法會調(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);

我們知道,這個app.thread的類型是IApplicationThread,而IApplicationThread的具體實現(xiàn)是ActivityThread中的ApplicationThread。所以,這段代碼實際上調(diào)到了ActivityThread的中,即ApplicationThread的scheduleLaunchActivity方法,而scheduleLaunchActivity方法最終會完成新Activity的onCreate、onStart、onResume的調(diào)用過程。因此,可以得出結(jié)論,是舊Activity先onPause,然后新Activity再啟動。至于ApplicationThread的scheduleLaunchActivity方法為什么會完成新Activity的onCreate、onStart、onResume的調(diào)用過程,請看下面的代碼。scheduleLaunchActivity最終會調(diào)用如下方法,而如下方法的確會完成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會被調(diào)用

Activitya=performLaunchActivity(r,customIntent);

if(a!=null){

r.createdConfig=newConfiguration(mConfiguration);

BundleoldState=r.state;

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

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

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

//省略

}

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

}

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

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

}

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

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

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

android:configChanges="screenLayout"

android:launchMode="singleTask"

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

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

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

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivity(intent);

這兩種方式都可以為Activity指定啟動模式,但是二者還是有區(qū)別的。首先,優(yōu)先級上,第二種方式的優(yōu)先級要高于第一種,當(dāng)兩種同時存在時,以第二種方式為準(zhǔn);其次,上述兩種方式在限定范圍上有所不同,比如,第一種方式無法直接為Activity設(shè)定FLAG_ACTIVITY_CLEAR_TOP標(biāo)識,而第二種方式無法為Activity指定singleInstance模式。關(guān)于Intent中為Activity指定的各種標(biāo)記位,在下面的小節(jié)中會繼續(xù)介紹。下面通過一個例子來體驗啟動模式的使用效果。還是前面的例子,這里我們把MainActivity的啟動模式設(shè)為singleTask,然后重復(fù)啟動它,看看是否會重復(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ù)單擊三次按鈕啟動3次MainActivity,算上原本的MainActvity的實例,正常情況下,任務(wù)棧中應(yīng)該有4個MainActivity的實例,但是我們?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信息可以看出,盡管啟動了4次MainActivity,但是它始終只有一個實例在任務(wù)棧中,從圖1-9的log可以看出,Activity的確沒有重新創(chuàng)建,只是暫停了一下,然后調(diào)用了onNewIntent,接著調(diào)用onResume就又繼續(xù)了。圖1-9系統(tǒng)日志現(xiàn)在我們?nèi)サ魋ingleTask,再來對比一下,還是同樣的操作,單擊三次按鈕啟動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等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論