注:本文主要來自網易的一個博主的文章,經過閱讀,總結,故留下文章在此
Android四大基本元件分別是Activity,Service服務,Content Provider內容提供者,BroadcastReceiver廣播接收器。
一:瞭解四大基本元件
Activity :
應用程式中,一個Activity通常就是一個單獨的螢幕,它上面可以顯示一些控制元件也可以監聽並處理使用者的事件做出響應。
Activity之間通過Intent進行通訊。在Intent 的描述結構中,有兩個最重要的部分:動作和動作對應的資料。
典型的動作型別有:M AIN(activity的門戶)、VIEW、PICK、EDIT 等。而動作對應的資料則以URI 的形式進行表示。例如:要檢視一個人的聯絡方式,你需要建立一個動作型別為VIEW 的intent,以及一個表示這個人的URI。
與之有關係的一個類叫IntentFilter。相對於intent 是一個有效的做某事的請求,一個intentfilter 則用於描述一個activity(或者IntentReceiver)能夠操作哪些intent。一個activity 如果要顯示一個人的聯絡方式時,需要宣告一個IntentFilter,這個IntentFilter 要知道怎麼去處理VIEW 動作和表示一個人的URI。IntentFilter 需要在AndroidManifest.xml 中定義。通過解析各種intent,從一個螢幕導航到另一個螢幕是很簡單的。當向前導航時,activity 將會呼叫startActivity(Intent myIntent)方法。然後,系統會在所有安裝的應用程式中定義的IntentFilter 中查詢,找到最匹配myIntent 的Intent 對應的activity。新的activity 接收到myIntent 的通知後,開始執行。當startActivity 方法被呼叫將觸發解析myIntent 的動作,這個機制提供了兩個關鍵好處:
A、Activities 能夠重複利用從其它元件中以Intent 的形式產生的一個請求;
B、Activities 可以在任何時候被一個具有相同IntentFilter 的新的Activity 取代。
AndroidManifest檔案中含有如下過濾器的Activity元件為預設啟動類當程式啟動時系統自動呼叫它
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
BroadcastReceive廣播接收器:
你的應用可以使用它對外部事件進行過濾只對感興趣的外部事件(如當電話呼入時,或者資料網路可用時)進行接收並做出響應。廣播接收器沒有使用者介面。然而,它們可以啟動一個activity或serice 來響應它們收到的資訊,或者用NotificationManager 來通知使用者。通知可以用很多種方式來吸引使用者的注意力──閃動背燈、震動、播放聲音等。一般來說是在狀態列上放一個持久的圖示,使用者可以開啟它並獲取訊息。
廣播型別:
普通廣播,通過Context.sendBroadcast(Intent myIntent)傳送的
有序廣播,通過Context.sendOrderedBroadcast(intent, receiverPermission)傳送的,該方法第2個引數決定該廣播的級別,級別數值是在 -1000 到 1000 之間 , 值越大 , 傳送的優先順序越高;廣播接收者接收廣播時的級別級別(可通過intentfilter中的priority進行設定設為2147483647時優先順序最高),同級別接收的先後是隨機的, 再到級別低的收到廣播,高級別的或同級別先接收到廣播的可以通過abortBroadcast()方法截斷廣播使其他的接收者無法收到該廣播,還有其他建構函式
非同步廣播,通過Context.sendStickyBroadcast(Intent myIntent)傳送的,還有sendStickyOrderedBroadcast(intent, resultReceiver, scheduler, initialCode, initialData, initialExtras)方法,該方法具有有序廣播的特性也有非同步廣播的特性;傳送非同步廣播要: <uses-permission android:name="android.permission.BROADCAST_STICKY" />許可權,接收並處理完Intent後,廣播依然存在,直到你呼叫removeStickyBroadcast(intent)主動把它去掉
注意:傳送廣播時的intent引數與Contex.startActivity()啟動起來的Intent不同,前者可以被多個訂閱它的廣播接收器呼叫,後者只能被一個(Activity或service)呼叫
監聽廣播Intent步驟:
1> 寫一個繼承BroadCastReceiver的類,重寫onReceive()方法,廣播接收器僅在它執行這個方法時處於活躍狀態。當onReceive()返回後,它即為失活狀態,注意:為了保證使用者互動過程的流暢,一些費時的操作要放到執行緒裡,如類名SMSBroadcastReceiver
2> 註冊該廣播接收者,註冊有兩種方法程式動態註冊和AndroidManifest檔案中進行靜態註冊(可理解為系統中註冊)如下:
靜態註冊,註冊的廣播,下面的priority表示接收廣播的級別"2147483647"為最高優先順序
<receiver android:name=".SMSBroadcastReceiver" >
<intent-filter android:priority = "2147483647" >
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver >
動態註冊,一般在Activity可互動時onResume()內註冊BroadcastReceiver
IntentFilter intentFilter=new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(mBatteryInfoReceiver ,intentFilter); //反註冊
unregisterReceiver(receiver);
注意:
1.生命週期只有十秒左右,如果在 onReceive() 內做超過十秒內的事情,就會報ANR(Application No Response) 程式無響應的錯誤資訊,如果需要完成一項比較耗時的工作 , 應該通過傳送 Intent 給 Service, 由Service 來完成 . 這裡不能使用子執行緒來解決 , 因為 BroadcastReceiver 的生命週期很短 , 子執行緒可能還沒有結束BroadcastReceiver 就先結束了 .BroadcastReceiver 一旦結束 , 此時 BroadcastReceiver 的所在程序很容易在系統需要記憶體時被優先殺死 , 因為它屬於空程序 ( 沒有任何活動元件的程序 ). 如果它的宿主程序被殺死 , 那麼正在工作的子執行緒也會被殺死 . 所以採用子執行緒來解決是不可靠的
2. 動態註冊廣播接收器還有一個特點,就是當用來註冊的Activity關掉後,廣播也就失效了。靜態註冊無需擔憂廣播接收器是否被關閉,只要裝置是開啟狀態,廣播接收器也是開啟著的。也就是說哪怕app本身未啟動,該app訂閱的廣播在觸發時也會對它起作用
系統常見廣播Intent,如開機啟動、電池電量變化、時間改變等廣播
Service 服務:
一個Service 是一段長生命週期的,沒有使用者介面的程式,可以用來開發如監控類程式。
比較好的一個例子就是一個正在從播放列表中播放歌曲的媒體播放器。在一個媒體播放器的應用中,應該會有多個activity,讓使用者可以選擇歌曲並播放歌曲。然而,音樂重放這個功能並沒有對應的activity,因為使用者當然會認為在導航到其它螢幕時音樂應該還在播放的。在這個例子中,媒體播放器這個activity 會使用Context.startService()來啟動一個service,從而可以在後臺保持音樂的播放。同時,系統也將保持這個service 一直執行,直到這個service 執行結束。另外,我們還可以通過使用Context.bindService()方法,連線到一個service 上(如果這個service 還沒有執行將啟動它)。當連線到一個service 之後,我們還可以service 提供的介面與它進行通訊。拿媒體播放器這個例子來說,我們還可以進行暫停、重播等操作。
Service使用步驟如下
1>繼承service類
2>AndroidManifast.xml配置清單檔案中<application>節點裡對服務進行配置
<service name=".SMSService"/>
服務不能自己執行,需要通過Contex.startService()或Contex.bindService()啟動服務
通過startService()方法啟動的服務於呼叫者沒有關係,即使呼叫者關閉了,服務仍然執行想停止服務要呼叫Context.stopService(),此時系統會呼叫onDestory(),使用此方法啟動時,服務首次啟動系統先呼叫服務的onCreate()-->onStart(),如果服務已經啟動再次呼叫只會觸發onStart()方法
使用bindService()啟動的服務與呼叫者繫結,只要呼叫者關閉服務就終止,使用此方法啟動時,服務首次啟動系統先呼叫服務的onCreate()-->onBind(),如果服務已經啟動再次呼叫不會再觸發這2個方法,呼叫者退出時系統會呼叫服務的onUnbind()-->onDestory(),想主動解除繫結可使用Contex.unbindService(),系統依次呼叫onUnbind()-->onDestory();
Content Provider內容提供者 :
android平臺提供了Content Provider使一個應用程式的指定資料集提供給其他應用程式。這些資料可以儲存在檔案系統中、在一個SQLite資料庫、或以任何其他合理的方式,
其他應用可以通過ContentResolver類(見ContentProviderAccessApp例子)從該內容提供者中獲取或存入資料.(相當於在應用外包了一層殼),
只有需要在多個應用程式間共享資料是才需要內容提供者。例如,通訊錄資料被多個應用程式使用,且必須儲存在一個內容提供者中
它的好處:統一資料訪問方式。
android系統自帶的內容提供者(頂級的表示資料庫名,非頂級的都是表名)這些內容提供者在SDK文件的android.provider Java包中都有介紹。見:http://developer.android.com/reference/android/provider/package-summary.html
├────Browser
├────CallLog
├────Contacts
│ ├────Groups
│ ├────People
│ ├────Phones
│ └────Photos
├────Images
│ └────Thumbnails
├────MediaStore
│ ├────Albums
│ ├────Artists
│ ├────Audio
│ ├────Genres
│ └────Playlists
├────Settings
└────Video
CallLog:地址和接收到的電話資訊
Contact.People.Phones:儲存電話號碼
Setting.System:系統設定和偏好設定
使用Content Provider對外共享資料的步驟
1>繼承ContentProvider類並根據需求重寫以下方法:

public boolean onCreate();//處理初始化操作 /**
* 插入資料到內容提供者(允許其他應用向你的應用中插入資料時重寫)
* @param uri
* @param initialValues 插入的資料
* @return
*/
public Uri insert(Uri uri, ContentValues initialValues); /**
* 從內容提供者中刪除資料(允許其他應用刪除你應用的資料時重寫)
* @param uri
* @param selection 條件語句
* @param selectionArgs 引數
* @return
*/
public int delete(Uri uri, String selection, String[] selectionArgs); /**
* 更新內容提供者已存在的資料(允許其他應用更新你應用的資料時重寫)
* @param uri
* @param values 更新的資料
* @param selection 條件語句
* @param selectionArgs 引數
* @return
*/
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs); /**
* 返回資料給呼叫者(允許其他應用從你的應用中獲取資料時重寫)
* @param uri
* @param projection 列名
* @param selection 條件語句
* @param selectionArgs 引數
* @param sortOrder 排序
* @return
*/
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) ; /**
* 用於返回當前Uri所代表資料的MIME型別
* 如果操作的資料為集合型別(多條資料),那麼返回的型別字串應該為vnd.android.cursor.dir/開頭
* 例如要得到所有person記錄的Uri為content://com.bravestarr.provider.personprovider/person,
* 那麼返回的MIME型別字串應該為"vnd.android.cursor.dir/person"
* 如果操作的資料為單一資料,那麼返回的型別字串應該為vnd.android.cursor.item/開頭
* 例如要得到id為10的person記錄的Uri為content://com.bravestarr.provider.personprovider/person/10,
* 那麼返回的MIME型別字串應該為"vnd.android.cursor.item/person"
* @param uri
*/
public String getType(Uri uri)

這些方法中的Uri引數,得到後需要進行解析然後做對應處理,Uri表示要操作的資料,包含兩部分資訊:
1.需要操作的contentprovider
2.對contentprovider中的什麼資料進行操作,一個Uri格式:結構頭://authorities(域名)/路徑(要操作的資料,根據業務而定)
content://com.bravestarr.provider.personprovider/person/10
說明:contentprovider的結構頭已經由android規定為content://
authorities用於唯一標識這個contentprovider程式,外部呼叫者可以根據這個找到他
路徑表示我們要操作的資料,路徑的構建根據業務而定.路徑格式如下:
要操作person錶行號為10的記錄,可以這樣構建/person/10
要操作person表的所有記錄,可以這樣構建/person
2>在AndroidManifest.xml中使用<provider>對ContentProvider進行配置註冊(內容提供者註冊它自己就像網站註冊域名),ContentProvider採用authoritie(原意授權,可理解為域名)作為唯一標識,方便其他應用能找到

<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<!-- authorities屬性命名建議:公司名.provider.SomeProvider-->
<provider android:name=".PersonProvider" android:authorities="com.bravestarr.provider.personprovider"/>
...
</application>

關於四大基本元件的一個總結:
1> 4大元件的註冊
4大基本元件都需要註冊才能使用,每個Activity、service、Content Provider內容提供者都需要在AndroidManifest檔案中進行配置AndroidManifest檔案中未進行宣告的activity、服務以及內容提供者將不為系統所見,從而也就不可用,而BroadcastReceive廣播接收者的註冊分靜態註冊(在AndroidManifest檔案中進行配置)和通過程式碼動態建立並以呼叫Context.registerReceiver()的方式註冊至系統。需要注意的是在AndroidManifest檔案中進行配置的廣播接收者會隨系統的啟動而一直處於活躍狀態,只要接收到感興趣的廣播就會觸發(即使程式未執行)
AndroidManifest檔案中進行註冊格式如下:
<activity>元素的name 屬性指定了實現了這個activity 的Activity 的子類。icon 和label 屬性指向了包含展示給使用者的此activity 的圖示和標籤的資原始檔。
<service> 元素用於宣告服務
<receiver> 元素用於宣告廣播接收器
<provider> 元素用於宣告內容提供者
2> 4大元件的啟用
• 容提供者的啟用:當接收到ContentResolver 發出的請求後,內容提供者被啟用。而其它三種元件──activity、服務和廣播接收器被一種叫做intent 的非同步訊息所啟用
• Activity的啟用通過傳遞一個Intent 物件至Context.startActivity()或Activity.startActivityForResult()以載入(或指定新工作給)一個activity。相應的activity 可以通過呼叫getIntent() 方法來檢視啟用它的intent。如果它期望它所啟動的那個activity 返回一個結果,它會以呼叫startActivityForResult()來取代startActivity()。比如說,如果它啟動了另外一個Activity 以使使用者挑選一張照片,它也許想知道哪張照片被選中了。結果將會被封裝在一個Intent 物件中,並傳遞給發出呼叫的activity 的onActivityResult() 方法。
• 服務的啟用可以通過傳遞一個Intent 物件至Context.startService()或Context.bindService()前者Android 呼叫服務的onStart()方法並將Intent 物件傳遞給它,後者Android 呼叫服務的onBind()方法將這個Intent 物件傳遞給它
• 傳送廣播可以通過傳遞一個Intent 物件至給Context.sendBroadcast() 、
Context.sendOrderedBroadcast()或Context.sendStickyBroadcast()Android 會呼叫所有對此廣播有興趣的廣播接收器的onReceive()方法,將intent 傳遞給它們
3> 四大元件的關閉
內容提供者僅在響應ContentResolver 提出請求的時候啟用。而一個廣播接收器僅在響應廣播資訊的時候啟用。所以,沒有必要去顯式的關閉這些元件。
Activity關閉:可以通過呼叫它的finish()方法來關閉一個activity
服務關閉:對於通過startService()方法啟動的服務要呼叫Context.stopService()方法關閉服務,使用bindService()方法啟動的服務要呼叫Contex.unbindService ()方法關閉服務
二:四大元件的生命週期
介紹生命週期之前,先提一下任務的概念
任務其實就是activity 的棧它由一個或多個Activity組成的共同完成一個完整的使用者體驗, 換句話說任務就是” 應用程式” (可以是一個也可以是多個,比如假設你想讓使用者看到某個地方的街道地圖。而已經存在一個具有此功能的activity 了,那麼你的activity 所需要做的工作就是把請求資訊放到一個Intent 物件裡面,並把它傳遞給startActivity()。於是地圖瀏覽器就會顯示那個地圖。而當用戶按下BACK 鍵的時候,你的activity 又會再一次的顯示在螢幕上,此時任務是由2個應用程式中的相關activity組成的)棧底的是啟動整個任務的Activity,棧頂的是當前執行的使用者可以互動的Activity,當一個activity 啟動另外一個的時候,新的activity 就被壓入棧,併成為當前執行的activity。而前一個activity 仍保持在棧之中。當用戶按下BACK 鍵的時候,當前activity 出棧,而前一個恢復為當前執行的activity。棧中儲存的其實是物件,棧中的Activity 永遠不會重排,只會壓入或彈出,所以如果發生了諸如需要多個地圖瀏覽器的情況,就會使得一個任務中出現多個同一Activity 子類的例項同時存在。
任務中的所有activity 是作為一個整體進行移動的。整個的任務(即activity 棧)可以移到前臺,或退至後臺。舉個例子說,比如當前任務在棧中存有四個activity──三個在當前activity 之下。當用戶按下HOME 鍵的時候,回到了應用程式載入器,然後選擇了一個新的應用程式(也就是一個新任務)。則當前任務遁入後臺,而新任務的根activity 顯示出來。然後,過了一小會兒,使用者再次回到了應用程式載入器而又選擇了前一個應用程式(上一個任務)。於是那個任務,帶著它棧中所有的四個activity,再一次的到了前臺。當用戶按下BACK 鍵的時候,螢幕不會顯示出使用者剛才離開的activity(上一個任務的根
activity)。取而代之,當前任務的棧中最上面的activity 被彈出,而同一任務中的上一個activity 顯示了出來。
Activity棧:先進先出規則
Android系統是一個多工(Multi-Task)的作業系統,可以在用手機聽音樂的同時,也執行其他多個程式。每多執行一個應用程式,就會多耗費一些系統記憶體,當同時執行的程式過多,或是關閉的程式沒有正確釋放掉記憶體,系統就會覺得越來越慢,甚至不穩定。
為了解決這個問題, Android 引入了一個新的機制-- 生命週期(Life Cycle)。
Android 應用程式的生命週期是由Android 框架進行管理,而不是由應用程式直接控
制。通常,每一個應用程式(入口一般會是一個Activity 的onCreate 方法),都會產生
一個程序(Process)。當系統記憶體即將不足的時候,會依照優先順序自動進行程序(process)的回收。不管是使用者或開發者, 都無法確定的應用程式何時會被回收。所以為了很好的防止資料丟失和其他問題,瞭解生命週期很重要。
Activity生命週期:
圖3.1activity生命週期圖
Activity整個生命週期的4種狀態、7個重要方法和3個巢狀迴圈
1> 四種狀態
- 活動(Active/Running)狀態
當Activity執行在螢幕前臺(處於當前任務活動棧的最上面),此時它獲取了焦點能響應使用者的操作,屬於執行狀態,同一個時刻只會有一個Activity 處於活動(Active)或執行
(Running)狀態
- 暫停(Paused)狀態
當Activity失去焦點但仍對使用者可見(如在它之上有另一個透明的Activity或Toast、AlertDialog等彈出視窗時)它處於暫停狀態。暫停的Activity仍然是存活狀態(它保留著所有的狀態和成員資訊並保持和視窗管理器的連線),但是當系統記憶體極小時可以被系統殺掉
3. 停止(Stopped)狀態
完全被另一個Activity遮擋時處於停止狀態,它仍然保留著所有的狀態和成員資訊。只是對使用者不可見,當其他地方需要記憶體時它往往被系統殺掉
4. 非活動(Dead)狀態
Activity 尚未被啟動、已經被手動終止,或已經被系統回收時處於非活動的狀態,要手動終止Activity,可以在程式中呼叫"finish"方法。
如果是(按根據記憶體不足時的回收規則)被系統回收,可能是因為記憶體不足了
記憶體不足時,Dalvak 虛擬機器會根據其記憶體回收規則來回收記憶體:
1. 先回收與其他Activity 或Service/Intent Receiver 無關的程序(即優先回收獨
立的Activity)因此建議,我們的一些(耗時)後臺操作,最好是作成Service的形式
2.不可見(處於Stopped狀態的)Activity
3.Service程序(除非真的沒有記憶體可用時會被銷燬)
4.非活動的可見的(Paused狀態的)Activity
5.當前正在執行(Active/Running狀態的)Activity
2> 7個重要方法,當Activity從一種狀態進入另一狀態時系統會自動呼叫下面相應的方
法來通知使用者這種變化
當Activity第一次被例項化的時候系統會呼叫,
整個生命週期只調用1次這個方法
通常用於初始化設定: 1、為Activity設定所要使用的佈局檔案2、為按鈕繫結監聽器等靜態的設定操作
onCreate(Bundle savedInstanceState);
當Activity可見未獲得使用者焦點不能互動時系統會呼叫
onStart();
當Activity已經停止然後重新被啟動時系統會呼叫
onRestart();
當Activity可見且獲得使用者焦點能互動時系統會呼叫
onResume();
當系統啟動另外一個新的Activity時,在新Activity啟動之前被系統呼叫儲存現有的Activity中的持久資料、停止動畫等,這個實現方法必須非常快。當系統而不是使用者自己出於回收記憶體時,關閉了activity 之後。使用者會期望當他再次回到這個activity 的時候,它仍保持著上次離開時的樣子。此時用到了onSaveInstanceState(),方法onSaveInstanceState()用來儲存Activity被殺之前的狀態,在onPause()之前被觸發,當系統為了節省記憶體銷燬了Activity(使用者本不想銷燬)時就需要重寫這個方法了,當此Activity再次被例項化時會通過onCreate(Bundle savedInstanceState)將已經儲存的臨時狀態資料傳入因為onSaveInstanceState()方法不總是被呼叫,觸發條件為(按下HOME鍵,按下電源按鍵關閉螢幕,橫豎屏切換情況下),你應該僅重寫onSaveInstanceState()來記錄activity的臨時狀態,而不是持久的資料。應該使用onPause()來儲存持久資料。
onPause();
當Activity被新的Activity完全覆蓋不可見時被系統呼叫
onStop();
當Activity(使用者呼叫finish()或系統由於記憶體不足)被系統銷燬殺掉時系統呼叫,(整個生命週期只調用1次)用來釋放onCreate ()方法中建立的資源,如結束執行緒等
onDestroy();
3> 3個巢狀迴圈
1.Activity完整的生命週期:從第一次呼叫onCreate()開始直到呼叫onDestroy()結束
2.Activity的可視生命週期:從呼叫onStart()到相應的呼叫onStop()
在這兩個方法之間,可以保持顯示Activity所需要的資源。如在onStart()中註冊一個廣播接收者監聽影響你的UI的改變,在onStop() 中登出。
3.Activity的前臺生命週期:從呼叫onResume()到相應的呼叫onPause()。
舉例說明:
例1:有3個Acitivity,分別用One,Two(透明的),Three表示,One是應用啟動時的主Activity
啟動第一個介面Activity One時,它的次序是
onCreate (ONE) - onStart (ONE) - onResume(ONE)
點"開啟透明Activity"按鈕時,這時走的次序是
onPause(ONE) - onCreate(TWO) - onStart(TWO) - onResume(TWO)
再點back回到第一個介面,Two會被殺這時走的次序是
onPause(TWO) - onActivityResult(ONE) - onResume(ONE) - onStop(TWO) - onDestroy(TWO)
點"開啟全屏Activity"按鈕時,這時走的次序是
onPause(ONE) - onCreate(Three) - onStart(Three) - onResume(Three) - onStop(ONE)
再點back回到第一個介面,Three會被殺這時走的次序是
onPause(Three) - onActivityResult(ONE) - onRestart(ONE) - onStart(ONE)- onResume(ONE) - onStop(Three) - onDestroy(Three)
再點back退出應用時,它的次序是
onPause(ONE) - onStop(ONE) - onDestroy(ONE)
例2:橫豎屏切換時候Activity的生命週期
他切換時具體的生命週期是怎麼樣的:
1、新建一個Activity,並把各個生命週期打印出來
2、執行Activity,得到如下資訊
onCreate-->
onStart-->
onResume-->
3、按crtl+f12切換成橫屏時
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
4、再按crtl+f12切換成豎屏時,發現列印了兩次相同的log
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
5、修改AndroidManifest.xml,把該Activity新增android:configChanges="orientation",執行步驟3
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
6、再執行步驟4,發現不會再列印相同資訊,但多列印了一行onConfigChanged
onSaveInstanceState-->
onPause-->
onStop-->
onDestroy-->
onCreate-->
onStart-->
onRestoreInstanceState-->
onResume-->
onConfigurationChanged-->
7、把步驟5的android:configChanges="orientation" 改成 android:configChanges="orientation|keyboardHidden",執行步驟3,就只打印onConfigChanged
onConfigurationChanged-->
8、執行步驟4
onConfigurationChanged-->
onConfigurationChanged-->
總結:
1、不設定Activity的android:configChanges時,切屏會重新呼叫各個生命週期,切橫屏時會執行一次,切豎屏時會執行兩次
2、設定Activity的android:configChanges="orientation"時,切屏還是會重新呼叫各個生命週期,切橫、豎屏時只會執行一次
3、設定Activity的android:configChanges="orientation|keyboardHidden"時,切屏不會重新呼叫各個生命週期,只會執行onConfigurationChanged方法
總結一下整個Activity的生命週期
補充一點,當前Activity產生事件彈出Toast和AlertDialog的時候Activity的生命週期不會有改變
Activity執行時按下HOME鍵(跟被完全覆蓋是一樣的):onSaveInstanceState --> onPause --> onStop,再次進入啟用狀態時: onRestart -->onStart--->onResume
BroadcastReceive廣播接收器生命週期:
生命週期只有十秒左右,如果在 onReceive() 內做超過十秒內的事情,就會報ANR(Application No Response) 程式無響應的錯誤資訊
它的生命週期為從回撥onReceive()方法開始到該方法返回結果後結束
Service服務生命週期:
圖3.2service生命週期圖
Service完整的生命週期:從呼叫onCreate()開始直到呼叫onDestroy()結束
Service有兩種使用方法:
1>以呼叫Context.startService()啟動,而以呼叫Context.stopService()結束
2>以呼叫Context.bindService()方法建立,以呼叫Context.unbindService()關閉
service重要的生命週期方法
當用戶呼叫startService ()或bindService()時,Service第一次被例項化的時候系統會呼叫,整個生命週期只調用1次這個方法,通常用於初始化設定。注意:多次呼叫startService()或bindService()方法不會多次觸發onCreate()方法
void onCreate()
當用戶呼叫stopService()或unbindService()來停止服務時被系統呼叫,(整個生命週期只調用1次)用來釋放onCreate()方法中建立的資源
void onDestroy()
通過startService()方法啟動的服務
初始化結束後系統會呼叫該方法,用於處理傳遞給startService()的Intent物件。如音樂服務會開啟Intent 來探明將要播放哪首音樂,並開始播放。注意:多次呼叫startService()方法會多次觸發onStart()方法
void onStart(Intent intent)
通過bindService ()方法啟動的服務
初始化結束後系統會呼叫該方法,用來繫結傳遞給bindService 的Intent 的物件。注意:多次呼叫bindService()時,如果該服務已啟動則不會再觸發此方法
IBinder onBind(Intent intent)
使用者呼叫unbindService()時系統呼叫此方法,Intent 物件同樣傳遞給該方法
boolean onUnbind(Intent intent)
如果有新的客戶端連線至該服務,只有當舊的呼叫onUnbind()後,新的才會呼叫該方法
void onRebind(Intent intent)
補充:onCreate(Bundle savedInstanceState)與onSaveInstanceState(Bundle savedInstanceState)配合使用,見如下程式碼,達到顯示activity被系統殺死前的狀態

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (null != savedInstanceState) {
String _userid = savedInstanceState.getString("StrUserId");
String _uid = savedInstanceState.getString("StrUid");
String _serverid = savedInstanceState.getString("StrServerId");
String _servername = savedInstanceState.getString("StrServerName");
int _rate = savedInstanceState.getInt("StrRate");
//updateUserId(_userid);
//updateUId(_uid);
//updateServerId(_serverid);
//updateUserServer(_servername);
//updateRate(_rate);
}
} @Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("StrUserId", getUserId());
savedInstanceState.putString("StrUid", getUId());
savedInstanceState.putString("StrServerId", getServerId());
savedInstanceState.putString("StrServerName", getServerName());
savedInstanceState.putInt("StrRate", getRate());
}

引發activity摧毀和重建的其他情形
除了系統處於記憶體不足的原因會摧毀activity之外, 某些系統設定的改變也會導致activity的摧毀和重建. 例如改變螢幕方向(見上例), 改變裝置語言設定, 鍵盤彈出等.