初識Activity:
我們都知道android中有四大組件(Activity活動,Service服務,Content Provider內容提供者,BroadcastReceiver廣播接收者),Activity是我們用的最多的,也是最為基本的組件,因為應用的所有操作都與用戶相關,Activity提供窗口來和用戶進行交互。
官方文檔這么說:
An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View).
大概意思:activity是一個獨立的,能夠單獨處理用戶操作的組件,幾乎所有的activity都是用來和用戶做交互的,所以activity類會創建一個窗口,開發者可以通過setContent(View)的接口方法把UI放到activity組件上。
Android中activity全都歸屬于task管理。task:是一個activity的集合,這些activity按照啟動的順序排隊存入一個棧(即"back stack")。android默認會為每一個App維持一個task來存放該app的所有的activity,task的默認name為該app的packagename。
Activity的內部調用過程:
上面已經講過了,系統通過堆棧來管理activity,當一個新的activity開始時,它被放置在堆棧的頂部和成為運行活動,以前的activity始終保持低于它在堆棧的位置(也就是說當前活動的activity位于棧頂,以前使用過的activity位于棧低)而不會再一次到達前臺,直到新的活動退出。
下面分析Activity的生命周期過程:
- ** 啟動Activity:** onCreate() -> onstart() -> onResume(),Activity進入運行狀態。
- Activity退居后臺:當前Activity轉到新的Activity界面或者按手機Home鍵回到主屏:onPause() -> onStop(),進入停滯狀態。
- Activity從后臺返回前臺:onRestart() -> onStart() -> onResume(),再次回到運行狀態。
- Activity退居后臺,且系統內存不足時,系統會殺掉這個后臺轉態的Activity(此時這個Activity引用仍然處在任務棧中,只是這個時候引用指向的對象已經為null),若再次回到這個Activity,則會走onCreate() -> onStart() -> onResume()(將重新走一次Activity的初始化生命周期)。
- 鎖屏:onPause() -> onStop()
- 解鎖:onStart() -> onResume()
- Activity的完整生存期會在onCreate()調用和onDestroy()調用之間發生。
- Activity的可見生存期會在onStart()調用和onStop()調用之間發生。系統會在activity的整個生存期內多次調用onStart()和onStop(),因為activity可能會在顯示和隱藏之間不斷的來回切換。
- Activity的前后臺切換會在onResume()調用和onPause()調用之間發生。因為這個狀態可能會經常發生轉換,為了避免切換遲緩引起的用戶等待,這兩個方法中的代碼應該相當地輕量化。
啟動模式是什么?為什么要定義啟動模式呢?
簡單的說啟動模式就是定義activity實例與task的關聯方式。
定義啟動模式是為了實現一些默認啟動(standard)模式之外的需求:
1.讓某個activity啟動一個嶄新的task(而不是被放入到當前的task中)
2.讓activity啟動時只是調出已有的某個實例(而不是在back stack棧頂創建一個嶄新的實例)
3.或者,你想在用戶離開task時,只保留根activity,而back stack中的其他activity都要清空。
以上的這些需求,就是啟動模式,所起到的作用。
怎樣定義啟動模式?
定義啟動模式的方法有兩種:
1.在manifest 文件中,通過Acitivty的xml標簽來改變任務棧的默認行為(啟動模式):
- 使用
android:launchMode="standard|singleInstance|singleTask|singleTop"
來控制Activity任務棧。在 manifest 文件中activity聲明時,利用 activity 元素的 launchMode 屬性來設定 activity 與 task 的關系。
** 任務棧 是一種后進先出的結構。位于棧頂的Activity處于焦點狀態,當按下back按鈕的時候,棧內的Activity會一個一個的出棧,并且調用其onDestory()方法。如果棧內沒有Activity,那么系統就會回收這個棧,每個App默認只有一個棧,以App的包名來命名。
** <1>.standard : 標準模式,每次啟動Activity都會創建一個新的Activity實例,并且將其壓入任務棧棧頂,而不管這個Activity是否已經存在。Activity的啟動三回調(onCreate() -> onStart() -> onResume()都會執行)。
** <2>.singleTop :** 棧頂復用模式,這種模式下,如果新Activity已經位于任務棧的棧頂,那么此Activity不會被重新創建,所以它的啟動三回調就不會執行,同時Activity的onNewIntent()方法會被回調,如果Activity已經存在但是不在棧頂,那么作用與standard模式一樣。
** <3>.singleTask:** 棧內復用模式,創建這樣的Activity的時候,系統會先確認它所需任務棧已經創建,否則先創建任務棧,然后放入Activity,如果棧中已經有一個Activity實例,那么這個Activity就會被調到棧頂,onNewIntent(),并且singleTask會清理在當前Activity尚明的所有Activity。(clear top)
** <4>.singleInstance :**加強版的singleTask模式,這種模式的Activity只能單獨位于一個任務棧內,由于棧內復用的特性,后續請求均不會創建新的Activity,除非這個獨特的任務棧被系統銷毀了。
Activity的堆棧管理以ActivityRecord為單位,所有的ActivityRecord都放在一個List里面,可以認為一個ActivityRecord就是一個Activity棧。
2.使用 Intent 標志
在要啟動activity時,你可以在傳給startActivity()的intent中包含相應標志,以修改activity與task的默認關系。
代碼如下:
Intent i = new Intent(this,NewActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);```
還可以通過標志修改默認模式,如下所示:
* **FLAG_ACTIVITY_NEW_TASK**
與“singleTask”模式相同,在新的task中啟動activity。如果要啟動的activity已經運行于某個task中,則那個task將調入前臺。
* **FLAG_ACTIVITY_SINGLE_TOP**
與“singleTop”模式相同,如果要啟動的activity位于back stack棧頂,系統不會重新創建目標Activity實例,而是直接復用Task棧頂的Activity。
* **FLAG_ACTIVITY_CLEAR_TOP**
此種模式在launchMode中沒有對應的屬性值。如果要啟動的activity已經在當前task中運行,則不再啟動一個嶄新的實例,且所有在其上面的activity將被銷毀。
**關于啟動模式的一些建議:**
一般不要改變activity和task默認的工作方式。如果你確定有必要修改默認方式時,請保持謹慎,并確保activity在啟動和從其他activity返回時的可用性,多做測試和安全方面的工作。
#####Activity緩存方法:
**情景再現:**有a,b兩個Activity,當從a進入b之后一段時間,可能系統會把a回收,這時候按back鍵,執行的不是a的onRestart(),而是onCreate()方法,a被重新創建一次,這時a種的臨時數據和狀態可能丟失了。
**解決辦法:**可以用Activity的 onSaveInstanceState()回調方法保存臨時的數據和狀態,這個方法一定會在活動被回收之前調用。方法中有一個Bundle參數,putString(),putInt()等方法需要傳入兩個參數,一個鍵一個值。數據保存之后會在onCreate()中恢復,onCreate也有一個Bundle類型的參數。
**實例代碼:**
```@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//這里,當Acivity第一次被創建的時候為空
//所以我們需要判斷一下
if( savedInstanceState != null ){
savedInstanceState.getString("anAnt");
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("anAnt","Android");
}```
* **onSaveInstanceState(Bundle outState)**
當某個activity變得“容易”被系統銷毀時,該activity的onSaveInstanceState就會被執行,除非該activity是被用戶主動銷毀的,例如當用戶按Back鍵的時候。
注意上面的雙引號,何為“容易”?言下之意就是該activity還沒有被銷毀,而僅僅是一種可能性。這種可能性有哪些呢?下面,我們通過重新一個activity的所有的生命周期的onXxx()方法,包括onSaveInstanceState()和onRestoreInstanceSatae()方法,我們可以清楚地知道,當某個activity(假定為activity A)顯示在當前task的最上層時,其onSaveInstanceState()方法會在什么時候被執行,有這么幾種情況:
**1.當用戶按下Home鍵時。**
這中情況是用戶顯而易見的操作,系統不知道你按下Home鍵后要運行多少其他的程序,自然也不知道activity A是否會被銷毀,故系統會調用onSaveInstanceState(),讓用戶有機會保存某些非永久性的數據。以下的幾種情況得分析都遵循該原則。
**2.長按Home鍵,選擇運行其他的程序時。**
**3.按下電源鍵(關閉屏幕顯示)時。**
**4.從activity A中啟動一個新的activity時。**
**5.屏幕方向切換時,例如從豎屏切換到橫屏時。**(如果不指定configchange屬性)在屏幕切換之前,系統會銷毀activity A,在屏幕切換之后系統又會自動地創建activity A,所以onSaveInstanceState()一定會被執行。
**總而言之,onSaveInstanceState()的調用遵循一個重要原則**,即當系統“未經過許可”時銷毀了你的activity,則onSaveInstanceState()會被系統調用,這是系統的責任。因為它必須要提供一個機會讓你保存你的數據(當然你不保存那就隨你了)。
**另外,需要注意一下幾點:**
**<注意點 1>:**布局中的每一個View默認實現了onSaveInstanceState()方法,這樣的話,這個UI的任何改變都會自動地存儲和在activity重新創建的時候自動地恢復。但是這種情況只有在你的這個UI提供唯一的ID之后才起作用,如果沒有提供ID,app將不會存儲它的狀態。
**<注意點 2>:**由于默認的onSaveInstanceState()方法的實現幫助UI存儲它的狀態,所以如果你需要覆蓋這個方法去存儲額外的轉態信息,你應該在執行任何代碼之前都調用父類的onSaveInstanceState()方法(super.onSaveInstanceState())。既然有現成的可用,那么我們到底還要不要自己實現onSaveInstanceState()?這就得看情況了,如果你自己的派生類中有變量影響到UI,或影響到你的程序的行為,當然就要把這個變量也保存了,那么就需要自己去實現了,否則就不需要了。
**<注意點 3>:**由于onSaveInstanceState()方法調用的不正確性,你應該子使用這個方法去記錄activity的瞬間狀態(UI狀態)。不應該用這個方法去存儲持久化數據。當用戶離開這個activity的時候應該在onPause()方法中存儲持久化數據(例如應該被存儲到數據庫中的數據)。
**<注意點 4>:**onSaveInsatnceState()如果被調用,這個方法會在onStop()前被觸發,但是系統并不保證是否在onPause()之前或者之后觸發。
* **onRestoreInstanceState(Bundle outState)**
至于onRestoreInstanceState()方法,需要注意的是,**onSaveInstanceState()方法和onRestoreInstanceState()方法“不一定”是成對被調用的。(通過代碼實踐得到的結論,并不一定會兩個方法成對被調用)**
**onRestoreInstanceState()被調用的前提是**:activity A“確實”被系統銷毀了,而如果僅僅是停留在有這種可能性的情況下,則該方法不會被調用,例如,當正在顯示的activity A的時候,用戶按下Home鍵回到主界面,然后用戶緊接著又返回到activty A,這種情況下activity A一般不會因為內存的原因被系統銷毀,故activity A的onRestoreInstanceState()方法不會被執行。
**另外,onRestoreInstanceState()方法的bundle參數也會傳遞到onCreate()方法中**,你也可以選擇在onCreate()方法中做數據的還原。還有onRestoreInstanceState()在onStart()方法之后執行。至于這兩個函數的使用,給出示范代碼(注:要特別留意自定義代碼在調用super的前或后):
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// etc.在super() 前 些自己的自定義的代碼
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// etc.在super() 后 些自己的自定義的代碼
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}
#####Fragment的生命周期和Activity之間的關系:
先上一張Fragment和Activity生命周期綜合圖:

* **為什么程序要在Service中創建子線程,而不是Activity中創建呢?**
這是因為Activty很難對Thread進行控制,當Activity被銷毀之后,就沒有任何其他的辦法可以再重新獲取到之前創建的子線程的實例。而且在一個Activity中創建的子線程,在另一個Activity中是無法對其進行操作的。但是Service就不同了,所有的Activity都可以與Service進行關聯,然后可以很方便地操作其中的方法,即使Activity被銷毀了,之后只要重新與Service建立關聯,就又能夠獲取到原有的Service中的Binder的實例了。因此,使用Service來處理后臺任務,Activity就可以放心的finish(),完全不需要擔心無法對后臺任務進行控制的情況了。
* **Intent的使用方法,可以傳遞哪些數據類型。**
通過查詢Intent/Bundle的API文檔,我們可以獲知,Intent/Bundle支持傳遞基本類型的數據和基本類型的數組數據,以及String/CharSequence類型的數據和String/CharSequence類型的數組數據。而對于其它類型的數據貌似無能為力,其實不然,我們可以在Intent/Bundle的API中看到Intent/Bundle還可以傳遞Parceable(包裹化,郵包)和Serializable(序列化)類型的數據,以及它們的數據/列表數據。
所以要讓非基本類型的和非String/CharSequence類型的數據通過Intent/Bundle來進行傳輸,我們需要在數據類型中實現Parceable接口或是實現Serializable接口。
[http://blog.csdn.net/kkk0526/article/details/7214247](http://blog.csdn.net/kkk0526/article/details/7214247)
#####解析Intent Filter:
android的3個核心組件---Activity,Services,Braodcast Receiver---是通過Intent傳遞消息的。Intent消息用于在運行時綁定不同的組件。在Android的AndroidMainfest.xml配置文件中可以通過intent-filter節點為一個Activity指定其Intent Filter,以便于告訴該Activity可以響應什么類型的Intent。
* intent-filter的三大屬性:
**Action :**一個Intent Filter可以包含多個Action,Action列表用于標志Activity所能接受的“動作”,它是一個用戶自定義的字符串。
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<action android:name="com.wang.app" />
……
</intent-filter>```
也可以在代碼中使用以下語句便可以啟動該Intent對象:
Intent i=new Intent();
i.setAction("com.wang.app");```
Action列表中包含了"com.wang.app"的Activity都將會匹配成功。
**URL :**在inter-filter節點中,通過data節點匹配外部數據,也就是通過URL攜帶外部數據給目標組件。
<data android:mimeType="mimeType"
android:scheme="scheme"
android:host="host"
android:port="port"
android:path="path"/>```
注意:只有data的所有的屬性都匹配成功時,URL數據匹配才會成功。
Category :為組件定義一個類別列表,當Intent中包含這個類別列表的所有項目時,才會匹配成功。
<intent-filter . . . >
<action android:name="code.android.intent.action.MAIN" />
<category android:name="code.android.intent.category.LAUNCHER" />
</intent-filter>```
**Activity中Intent Filter的匹配過程:**
(1)加載所有的Intent Filter列表
(2)去掉action匹配失敗的Intent Filter
(3)去掉url匹配失敗的Intent Filter
(4)去掉Category匹配失敗的Intent Filter
(5)判斷剩下的Intent Filter數目是否為0,如果為0查找失敗返回異常;如果大于0,就按優先級排序,返回最高優先級的Intent Filter。
#####開發中Activity的一些問題:
一般設置Activity為非公開的
<activity
......
android:exported="false" /> ```
注意:非公開的Activity不能設置intent-filter,以免被其他的activity喚醒(如果擁有相同的intent-filter)。
- 不要指定activity的taskAffinity屬性。
- 不要設置activity的LaunchMode(保持默認),注意Activity的intent最好也不要設定為FLAG_ACTIVITY_NEW_TASK
- 在匿名內部類中使用this時,加上activity類名(類名.this,不一定是當前的activity)
- 設置activity全屏:在其onCreate()方法中加入代碼:
// 設置全屏模式
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 去除標題欄
requestWindowFeature(Window.FEATURE_NO_TITLE);```
**結束語:我害怕,自己在最該努力的時光中,卻選擇了安逸。 ---王令
QQ:2585085940
郵箱:wang91ling@163.com
歡迎大家光臨寒舍。**