Activity是Android應用中負責與用戶交互的組件,給Android應用提供可視化用戶界面。Activity是Window的容器,Activity包含一個getWindow()方法,返回該Activity所包含的窗口。
創建Activity
需要在清單文件中為其配置一個activity標簽
-
標簽中如果帶有這個子節點,則會在系統中多創建一個快捷圖標
<intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter>
一個應用程序可以在桌面創建多個快捷圖標。
-
activity的名稱、圖標可以和應用程序的名稱、圖標不相同
android:icon="@drawable/ic_launcher" android:label="@string/app_name"
Activity的生命周期
Activity的四個狀態
- 活動狀態:當前Activity位于前臺,用戶可見,可以獲得焦點進行交互。
- 暫停狀態:其他Activity位于前臺,該Activity依然可見,只是不能獲得焦點進行交互,常見如彈出PopupWindow。
- 停止狀態:該Activity不可見,失去焦點。
- 銷毀狀態:該Activity結束,或Activity所在的Dalvik進程結束。
Activity生命周期及回調方法
- onCreate(Bundle savedStatus):創建Activity時被回調;
- onStart():啟動Activity時被回調,Activity已經顯示在屏幕,但沒有得到焦;
- onRestart():重新啟動Activity時被回調,Activity從不可見變成可見時會執行此方法;
- onResume():恢復Activity時被回調, Activity得到焦點,可以與用戶交互;
- onPause():暫停Activity時被回調,Activity失去焦點,無法再與用戶交互,但依然可見;(可用于保護界面當前狀態)
- onStop():停止Activity時被回調,Activity不可見,進入后臺;
- onDestroy():銷毀Activity時被回調。
Activity實踐中的生命周期方法回調過程
- 啟動Activity:onCreate->onStart->onResume;
- 點擊Home鍵返回系統桌面:onPause->onStop;
- 點擊應用列表的圖標重新進入應用:onRestart->onStart->onResume;
- 點擊返回鍵(或程序調用finish()方法):onPause->onStop->onDestory;
Activity跳轉時的生命周期方法回調過程
橫豎屏切換的生命周期
默認情況下 ,橫豎屏切換, 銷毀當前的activity,重新創建一個新的activity。在一些特殊的應用程序常見下,比如游戲,不希望橫豎屏切換activity被銷毀重新創建。
默認情況(manifest清單文件中不對Activity的configChanges屬性做任何設置):
1、android3.2之前的版本:onSaveInstanceState->onPause->onStop->onDestroy->onCreate->onStart->onResume;
2、android3.2以后的版本:onPause->onSaveInstanceState->onStop->onDestroy->onCreate->onStart->onResume;設置讓Activity對橫豎屏切換不敏感(清單文件中設置android:configChanges="orientation"):
1、android3.2之前的版本:調用onConfigurationChanged(不會重建Activity,也不會調用任何生命周期方法)
2、android3.2之后的版本,又分為兩種情況:
①targetSdkVersion<=12:調用onConfigurationChanged(不會重建Activity,也不會調用任何生命周期方法);
②targetSdkVersion>12:onPause->onSaveInstanceState->onStop->onDestroy->onCreate->onStart->onResume(配置的android:configChanges="orientation"沒起作用);
③如果targetSdkVersion>12時,想讓橫豎屏切換時不重建Activity,還得配置screenSize,也就是android:configChanges="orientation|screenSize"(因為google在android3.2中添加了screensize改變的通知,在轉屏的時候,不僅是orientation發生了改變,screensize同樣也發生了改變),這是最保險的做法。開發實踐中禁用掉橫豎屏重建Activity:
1、直接將Activity的橫豎屏寫死(簡單、但是用戶體驗不友好,具體根據產品需求):
//①通過清單文件配置Activity
android:screenOrientation="landscape"http://始終橫屏
android:screenOrientation="portrait"http://始終豎屏
//②在代碼中實現:
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//始終橫屏
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); //始終豎屏
2、通過清單文件配置Activity不再敏感橫豎屏切換(相對更友好安全的做法,參見上文這也是兼容性更好的做法,如果需要在橫豎屏切換時做業務邏輯處理,可以在onConfigurationChanged方法中實現):
在manifest.xml中配置activity:
android:configChanges="orientation|screenSize"
Activity的啟動模式
所有的Activity都將存放在任務棧中,通過設置Activity的啟動模式,可以修改任務棧的排列模式。我們可以在可以通過adb指令來查看系統的任務棧情況:adb shell dumpsys activity。 adb shell dumpsys activity activities | findstr "ResumedActivity"(這是用來查看當前活躍的棧頂Activity)。
Activity的四種啟動模式
- Standard 標準啟動模式:Android創建Activity時的默認模式,假設沒有為Activity設置啟動模式的話,默覺得標準模式。每次啟動一個Activity都會又一次創建一個新的實例入棧,無論這個實例是否存在。
- SingleTop 棧頂復用模式:假設你在當前的Activity中又要啟動同類型的Activity,此時建議將此類型Activity的啟動模式指定為SingleTop,能夠降低Activity的創建,節省內存。
①如果需要創建的Activity已經不處于棧頂,將會創建一個新的Activity入棧,同Standard模式一樣。
②如果須要創建的Activity已經處于棧頂時,此時不會再創建新的Activity,而是直接復用棧頂的Activity,保證棧頂如果存在,不會重復創建。此時Activity的onCreate、onStart不會被系統調用,由于它并沒有發生改變。但是它的的 onNewIntent會被回調。
- SingleTask 棧內復用模式: 當開啟activity的時候,就去檢查在任務棧里面是否有實例已經存在,如果有實例存在就復用這個已經存在的activity,并且把這個activity上面的所有的別的activity都清空讓此Activity位于棧頂,并且回調Activity的onNewIntent方法。保證整個任務棧里面只有一個實例存在。如果不存在同Standard模式一樣。(如果一個activity的創建需要占用大量的系統資源一般配置這個activity為singletask的啟動模式,一般應用的主界面也會配置為SingleTask模式) 。
- SingleInstance啟動模式非常特殊,是全局單例模式,是一種加強的SingleTask模式。它除了具有SingleTask的所有特性外,還有一個特點,SingleInstance啟動的activity會運行在自己的任務棧里面,并且這個任務棧里面只有一個實例存在。一般應用開發很少用到,如果你要保證一個activity在整個手機操作系統里面只有一個實例存在,使用singleInstance。比如Launch、鎖屏鍵、電話撥打界面等。
①我們可以試著做一個SingleInstance啟動模式的小猜想:假設我們從MainActivity ->(跳轉到)SingleInstanceActivity ->(跳轉到) ->SecondActivity。 然后我們點擊返回按鈕,頁面的返回順序會是怎樣?如果你試了之后不理解其原理,下文有關于任務棧的詳細介紹。
任務棧的相關理解
在上文的啟動模式中我們多次提到任務棧這個概念,任務是一個activity的集合,它用棧(后進先出)的方式來管理activity;這個棧被稱為返回棧(back stack),棧里的activity順序是按打開順序放置。
①當用戶在home界面點擊應用圖標時候,這個應用的任務就會被轉移到前臺,如果這個應用的任務是空的,說明最近這個應用沒有被啟動過,系統就會去創建一個新的任務,將該應用的主activity放入到返回棧中。
②當一個activity啟動了一個新activity的時候,新的activity會被放置到返回棧的棧頂并獲取焦點(如果是SingleInstance啟動模式,新的Activity會新建一個棧并獨享);前一個activity仍然保留在任務棧,但處于停止狀態。
③當用戶按下返回鍵的時候,處于棧頂的activity會被移除掉,前一個activity就會重新回到棧頂的位置。我們只能向棧頂添加activity或者將棧頂的activity移除掉。
④如果一直按返回鍵,返回棧中的activity會一個一個的被移除,最終返回到主屏幕,這時候返回棧中activity全被清空,對應的任務也就不存在了。
- 當打開一個應用,對應的任務處于前臺;這時候點擊home鍵回到主屏幕,任務就被轉移到后臺;當任務處于后臺狀態的時候,返回棧中的activity都進入停止狀態,但在返回棧中的順序不會變,每個activity的信息和數據都在;當處于內存不足的情況下有可能會被銷毀回收(涉及到進程優先級問題)。
啟動模式的使用方式
1、在Manifest.xml清單文件中靜態指定啟動模式:
<activity
android:name=".MainActivity"
android:launchMode="singleTask"/>
2、在Intent中動態指定啟動模式去創建Activity:
Intent intent = new Intent();
intent.setClass(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
- 兩種方式的區別:在Intent中動態指定的方式,優先級更高,若兩者同時存在,以動態指定方式為準;
Activity異常結束時保存狀態
Activity異常結束
Activity的異常結束是指,非人為主動結束和退出Activity的行為,一般有以下兩種情況:
1、資源系統設置變更:如橫豎屏切換,可能會導致Activity銷毀重建,或者設備語言發生變化,或者鍵盤發生變化時。我們可以在清單文件中配置Activity的configChanges屬性來避免(具體參照上文)。
2、系統資源不足時:由于虛擬機的垃圾回收機制,在系統內存不足時,會自動回收掉低優先級的進程,釋放內存保證優先級高的進程能正常運行(Android進程優先級相關知識)。
在Activity異常結束時保存和恢復數據
- 保存數據:onSaveInstanceState(),只會在異常退出時才會回調。如果用戶顯式關閉Activity時(點擊返回,或者觸發Activity的finish方法),則系統不會回調此方法。調用的時機android3.2之前的版本:onSaveInstanceState在onPause之前回調,Android3.2之后在onPause之后回調。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("name", "rave");
}
- 恢復數據:onRestoreInstanceState(),會在頁面異常關閉之后,重新進入時回調。調用時機是在onStart之后,onResume之前(沒有做過低版本測試)。
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if(savedInstanceState != null){
String name = savedInstanceState.getString("name");
System.out.println(name);
}
}
- 對于控件來說,在Activity異常結束時,部分系統控件會自動保存和恢復它的狀態(如EditView)。因為View類里面也有onSaveInstanceState()和onRestoreInstanceState(Parcelable state),如果要自定義控件實現該功能可以參考系統控件的做法。
Activity的之間的數據傳遞
當一個Activity啟動另一個Activity時,或者當一個Activity關閉并返回時,常常會有一些數據需要傳遞。Android的做法是將數據以鍵值對的方式存放在Bundle中,讓Intent攜帶Bundle在Activity之間完成數據傳遞。我們通過intent的putExtras()來添加數據,里面可以存放所以可序列化的類型(基本數據類型及其數組:byte、boolean、char、short、int、long、float、double、byte[]...;實現Parcelable接口的類;實現Serializable接口的類;還有這些類型的ArrayList集合類:ArrayList<? extends Parcelable>、ArrayList<Integer>、ArrayList<String>、ArrayList<CharSequence>)。???疑問這里Parcelable和Serializable有什么區別
- Activity啟動另一個Activity傳遞數據:
Bundle bundle = new Bundle();
bundle.putBoolean("booleanValue", true);
Intent intent = new Intent();
intent.putExtras(bundle);
startActivity(intent);
//startActivityForResult(intent, 0);
- 從Intent里面提取數據:
Bundle bundle = getIntent().getExtras();
boolean value = bundle.getBoolean("booleanValue");
- Activity關閉并返回數據:
Bundle bundle = new Bundle();
bundle.putSerializable("booleanValue", true);
Intent intent = new Intent();
intent.putExtras(bundle);
setResult(1, intent);
finish();
- 從返回的Activity里提取數據:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Bundle bundle = data.getExtras();
}
Activity轉場動畫
使用Scheme方式喚起Activity
Android中啟動Activity主要有兩種方式,一種是通過顯示意圖啟動,一種是通過隱式意圖啟動。這里介紹的使用Scheme方式喚起,其實是隱式意圖啟動的一種,因為這種方式在hybird開發中很常用所以提出來單獨介紹。
android中的scheme是一種頁面內跳轉協議,是一種非常好的實現機制,通過定義自己的scheme協議,可以非常方便跳轉app中的各個頁面。客戶端應用可以通過清單文件向操作系統注冊一個 URL scheme,該 scheme 用于從瀏覽器或其他應用中啟動本應用。通過指定的 URL 字段,可以讓應用在被調起后直接打開某些特定頁面,比如商品詳情頁、活動詳情頁等等。也可以執行某些指定動作,如完成支付等。也可以在應用內通過 html 頁來直接調用顯示 app 內的某個頁面。
URL Scheme使用場景大致分以下幾種
- 服務器下發跳轉路徑,客戶端根據服務器下發跳轉路徑跳轉相應的頁面
- H5頁面點擊錨點,根據錨點具體跳轉路徑APP端跳轉具體的頁面
- APP端收到服務器端下發的PUSH通知欄消息,根據消息的點擊跳轉路徑跳轉相關頁面
- APP根據URL跳轉到另外一個APP指定頁面
URL scheme協議格式
例如:xl://goods:8888/goodsDetail?goodsId=10011002
- xl代表該Scheme 協議名稱
- goods代表Scheme作用于哪個地址域
- goodsDetail代表Scheme指定的頁面
- goodsId代表傳遞的參數
- 8888代表該路徑的端口號
使用方式
- 喚起外部應用的Activity:
1、必須在目標Activity的Manifest.xml中配置如下過濾器(關于Intent和Intent-filter會單獨說明):
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="artist"
android:host="first"
android:path="/enter"/>
</intent-filter>
2、啟動方式:
Intent intent = new Intent();
intent.setData(Uri.parse("artist://first/enter"));
startActivity(intent);
- 喚起應用內部Activity(也可以用上面的方式喚起內部Activity),可以用下面的方式:
1、在目標Activity的Manifest.xml中配置如下過濾器
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="artist"
android:host="first"
android:path="/enter"/>
</intent-filter>
2、啟動方式:
WebView.loadUrl("artist://first/enter");