編輯:關於android開發
想要學好安卓開發,就必須理解安卓軟件的生命周期,明白一個活動的創建、啟動、停止、暫停、重啟和銷毀的過程,知道各個階段會調用什麼函數進行處理不同的情況,這裡我們就來說說Activity的生命周期。
Android 中的活動是層疊的,我們每啟動一個新的活動,就會覆蓋在原活動之上,然後點擊 Back 鍵會銷毀最上面的活動,下面的一個活動就會重新顯示出來。
其實 Android 是使用任務來管理活動的,一個任務就是一組存放在棧裡的活動的集合,這個棧也被稱作活動棧 。棧是一種後進先出的數據結構,在默認情況下,每當我們啟動了一個新的活動,它會在返回棧中入棧,並處於棧頂的位置。而每當我們按下 Back 鍵或調用 finish()方法去銷毀一個活動時,處於棧頂的活動會出棧,這時前一個入棧的活動就會重新處於棧頂的位置。系統總是會顯示處於棧頂的活動給用戶。
每個活動在其生命周期中最多可能會有四種狀態。
運行狀態
當一個活動位於活動棧的棧頂時,這時活動就處於運行狀態。系統最不願意回收的就是處於運行狀態的活動,因為這會帶來非常差的用戶體驗。
暫停狀態
當一個活動不再處於棧頂位置,但仍然可見時,這時活動就進入了暫停狀態。你可能會覺得既然活動已經不在棧頂了,還怎麼會可見呢?這是因為並不是每一個活動都會占滿整個屏幕的,比如對話框形式的活動只會占用屏幕中間的部分區域,你很快就會在後面看到這種活動。處於暫停狀態的活動仍然是完全存活著的,系統也不願意去回收這種活動(因為它還是可見的,回收可見的東西都會在用戶體驗方面有不好的影響) ,只有在內存極低的情況下,系統才會去考慮回收這種活動。
停止狀態
當一個活動不再處於棧頂位置,並且完全不可見的時候,就進入了停止狀態。系統仍然會為這種活動保存相應的狀態和成員變量,但是這並不是完全可靠的,當其他地方需要內存時,處於停止狀態的活動有可能會被系統回收。
銷毀狀態
當一個活動從活動棧中移除後就變成了銷毀狀態。系統會最傾向於回收處於這種狀態的活動,從而保證手機的內存充足。
Activity 類中定義了七個回調方法,覆蓋了活動生命周期的每一個環節,下面我來一一介紹下這七個方法。
1. onCreate()
這個方法你已經看到過很多次了,每個活動中我們都重寫了這個方法,它會在活動第一次被創建的時候調用。你應該在這個方法中完成活動的初始化操作,比如說加載布局、綁定事件等。
2. onStart()
這個方法在活動由不可見變為可見的時候調用。
3. onResume()
這個方法在活動准備好和用戶進行交互的時候調用。此時的活動一定位於活動棧的棧頂,並且處於運行狀態。
4. onPause()
這個方法在系統准備去啟動或者恢復另一個活動的時候調用。我們通常會在這個方法中將一些消耗 CPU 的資源釋放掉,以及保存一些關鍵數據,但這個方法的執行速度一定要快,不然會影響到新的棧頂活動的使用。
5. onStop()
這個方法在活動完全不可見的時候調用。它和 onPause()方法的主要區別在於,如果啟動的新活動是一個對話框式的活動,那麼 onPause()方法會得到執行,而 onStop()方法並不會執行。
6. onDestroy()
這個方法在活動被銷毀之前調用,之後活動的狀態將變為銷毀狀態。
7. onRestart()
這個方法在活動由停止狀態變為運行狀態之前調用,也就是活動被重新啟動了。
以上七個方法中除了 onRestart()方法,其他都是兩兩相對的,從而又可以將活動分為三種生存期。
1. 完整生存期
活動在 onCreate()方法和 onDestroy()方法之間所經歷的,就是完整生存期。一般情況下,一個活動會在 onCreate()方法中完成各種初始化操作,而在 onDestroy()方法中完成釋放內存的操作。
2. 可見生存期
活動在 onStart()方法和 onStop()方法之間所經歷的,就是可見生存期。在可見生存期內,活動對於用戶總是可見的,即便有可能無法和用戶進行交互。我們可以通過這兩個方法,合理地管理那些對用戶可見的資源。比如在 onStart()方法中對資源進行加載,而在 onStop()方法中對資源進行釋放, 從而保證處於停止狀態的活動不會占用過多內存。
3. 前台生存期
活動在 onResume()方法和 onPause()方法之間所經歷的,就是前台生存期。在前台生存期內,活動總是處於運行狀態的,此時的活動是可以和用戶進行相互的,我們平時看到和接觸最多的也這個狀態下的活動。
講了這麼多理論知識,也是時候該實戰一下了,下面我們將通過一個實例,讓你可以更加直觀地體驗活動的生命周期。
這次我們不准備在 ActivityTest 這個項目的基礎上修改了,而是新建一個項目。因此,首先關閉 ActivityTest 項目,然後新建一個ActivityLifeCycleTest 項目。新建項目的過程你應該已經非常清楚了,不需要我再進行贅述,這次我們允許 ADT 幫我們自動創建活動,這樣可以省去不少工作,創建的活動名和布局名都使用默認值。這樣主活動就創建完成了,我們還需要分別再創建兩個子活動,NormalActivity 和DialogActivity,下面一步步來實現。
新建 normal_layout.xml 文件,代碼如下所示:
這個布局中我們就非常簡單地使用了一個 TextView,然後同樣的方法,我們再新建一個 dialog_layout.xml 文件,代碼如下所示:
兩個布局文件的代碼幾乎沒有區別,只是顯示的文字不同而已。然後新建 NormalActivity 繼承自 Activity,代碼如下所示:
public class NormalActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.normal_layout);
}
}
我們在 NormalActivity 中加載了 normal_layout 這個布局。同樣的方法,再新建 DialogActivity 繼承自 Activity,代碼如下所示:
public class DialogActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.dialog_layout);
}
}
我們在 DialogActivity 中加載了 dialog_layout 這個布局。其實從名字上你就可以看出,這兩個活動一個是普通的活動,一個是對話框式的活動。可是現在不管怎麼看,這兩個活動的代碼都幾乎都是一模一樣的,在哪裡有體現出將活動設成對話框式的呢?別著急,下面我們馬上開始設置。在AndroidManifest.xml 的標簽中添加如下代碼:
這裡分別為兩個活動進行注冊,但是 DialogActivity 的注冊代碼有些不同,它使用了一個 android:theme 屬性,這是用於給當前活動指定主題的,Android 系統內置有很多主題可以選擇,當然我們也可以定制自己的主題,而這裡@android:style/Theme.Dialog 則毫無疑問是讓DialogActivity 使用對話框式的主題。接下來我們修改 activity_main.xml,重新定制我們主活動的布局:
自動生成的布局代碼有些復雜, 這裡我們完全替換掉, 仍然還是使用最熟悉的 LinearLayout,然後加入了兩個按鈕,一個用於啟動NormalActivity,一個用於啟動 DialogActivity。
最後修改 MainActivity 中的代碼,如下所示:
public class MainActivity extends Activity {
public static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
Button startNormalActivity = (Button) findViewById(R.id.start_normal_activity);
Button startDialogActivity = (Button) findViewById(R.id.start_dialog_activity);
startNormalActivity.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,
NormalActivity.class);
startActivity(intent);
}
});
startDialogActivity.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,
DialogActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart");
}
}
在onCreate()方法中,我們分別為兩個按鈕注冊了點擊事件,點擊第一個按鈕會啟動NormalActivity,點擊第二個按鈕會啟動 DialogActivity。然後在 Activity 的七個回調方法中分別打印了一句話,這樣就可以通過觀察日志的方式來更直觀地理解活動的生命周期。
現在運行程序,效果如圖所示:
前面我們已經說過,當一個活動進入到了停止狀態,是有可能被系統回收的。那麼想象以下場景,應用中有一個活動 A,用戶在活動 A 的基礎上啟動了活動 B,活動 A 就進入了停止狀態,這個時候由於系統內存不足,將活動 A 回收掉了,然後用戶按下 Back 鍵返回活動 A, 會出現什麼情況呢?其實還是會正常顯示活動 A的, 只不過這時並不會執行 onRestart()方法,而是會執行活動 A 的 onCreate()方法,因為活動 A 在這種情況下會被重新創建一次。這樣看上去好像一切正常,可是別忽略了一個重要問題,活動 A 中是可能存在臨時數據和狀態的。打個比方,MainActivity 中有一個文本輸入框,現在你輸入了一段文字,然後啟動 NormalActivity,這時 MainActivity 由於系統內存不足被回收掉,過了一會你又點擊了Back 鍵回到 MainActivity,你會發現剛剛輸入的文字全部都沒了,因為 MainActivity 被重新創建了。
如果我們的應用出現了這種情況,是會嚴重影響用戶體驗的,所以必須要想想辦法解決這個問題。查閱文檔可以看出,Activity 中還提供了一個 onSaveInstanceState()回調方法,這個方法會保證一定在活動被回收之前調用,因此我們可以通過這個方法來解決活動被回收時臨時數據得不到保存的問題。onSaveInstanceState()方法會攜帶一個 Bundle 類型的參數,Bundle 提供了一系列的方法用於保存數據,比如可以使用 putString()方法保存字符串,使用 putInt()方法保存整型數據,以此類推。每個保存方法需要傳入兩個參數,第一個參數是鍵,用於後面從 Bundle 中取值,第二個參數是真正要保存的內容。
在 MainActivity 中添加如下代碼就可以將臨時數據進行保存:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String tempData = "Something you just typed";
outState.putString("data_key", tempData);
}
數據是已經保存下來了,那麼我們應該在哪裡進行恢復呢?細心的你也許早就發現,我們一直使用的 onCreate()方法其實也有一個 Bundle 類型的參數。這個參數在一般情況下都是null,但是當活動被系統回收之前有通過 onSaveInstanceState()方法來保存數據的話,這個參數就會帶有之前所保存的全部數據,我們只需要再通過相應的取值方法將數據取出即可。
修改 MainActivity 的 onCreate()方法,如下所示:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
String tempData = savedInstanceState.getString("data_key");
Log.d(TAG, tempData);
}
……
}
取出值之後再做相應的恢復操作就可以了,比如說將文本內容重新賦值到文本輸入框上,這裡我們只是簡單地打印一下。
不知道你有沒有察覺,使用 Bundle 來保存和取出數據是不是有些似曾相識呢?沒錯!我們在使用 Intent 傳遞數據時也是用的類似的方法。這裡跟你提醒一點,Intent 還可以結合Bundle 一起用於傳遞數據的,首先可以把需要傳遞的數據都保存在 Bundle 對象中,然後再將 Bundle 對象存放在 Intent 裡。到了目標活動之後先從 Intent 中取出 Bundle,再從 Bundle中一一取出數據。
Android Activity生命周期以及Fragment生命周期的區別與分析,androidfragmentAndroid Fragment生命周期圖: Activ
OrmLite數據庫的使用方法,ormlite數據庫第一步:導入架包 1、將orm的兩個支持包放入project視圖下的你的工程的l
android 自學日記(一) android 自學日記(一) 說起日記,記憶還是小學時候學校裡的作業,只記得當時通篇都是流水賬,例如“幾點起床,去哪裡玩了,干了啥
android開發之wheel控件使用詳解 出門在外生不起病呀,隨便兩盒藥60多塊錢。好吧,不廢話了,今天我們來看看wheel控件的使用,這是GitHub上的一個開源控件