安卓開發筆記——重識Activity

  Activity並非什麼新鮮的東西,老生常談,這裏只是隨筆記錄一些筆記。java

  每當提及Activity,感受最關注的仍是它的生命週期,由於要使咱們的應用程序更加健壯,客戶體驗更加良好,若是對生命週期不熟悉,那是不可能完成的任務。好了,言歸正傳,開始筆記,儘量用最精簡的言語來闡述最實用的東西。android

  準備寫幾篇文章,這是第一篇只談生命週期的普通用法,不涉及到複雜點的知識,好比任何棧回退棧等操做。設計模式

 

一、一張來自谷歌官方文檔的Activity的生命週期圖:app

 

  直接來個MainActivity,覆寫以上全部方法並在方法裏打印出Log日誌,給定一個按鈕,點擊能夠跳轉到第二個Activity:ide

 1 package com.lcw.rabbit.activitydemo;
 2 
 3 import android.app.Activity;
 4 import android.content.Intent;
 5 import android.os.Bundle;
 6 import android.util.Log;
 7 import android.view.View;
 8 import android.widget.Button;
 9 
10 public class MainActivity extends Activity {
11 
12     private static final String TAG = "Rabbit";
13 
14     private Button mbButton;
15 
16     @Override
17     protected void onCreate(Bundle savedInstanceState) {
18         super.onCreate(savedInstanceState);
19         setContentView(R.layout.activity_main);
20         mbButton = (Button) findViewById(R.id.bt_button);
21         mbButton.setOnClickListener(new View.OnClickListener() {
22             @Override
23             public void onClick(View v) {
24                 startActivity(new Intent(MainActivity.this, SecondActivity.class));
25             }
26         });
27 
28         Log.i(TAG, "1----------onCreate");
29     }
30 
31     @Override
32     protected void onStart() {
33         super.onStart();
34         Log.i(TAG, "1----------onStart");
35     }
36 
37     @Override
38     protected void onResume() {
39         super.onResume();
40         Log.i(TAG, "1----------onResume");
41     }
42 
43     @Override
44     protected void onPause() {
45         super.onPause();
46         Log.i(TAG, "1----------onPause");
47     }
48 
49     @Override
50     protected void onStop() {
51         super.onStop();
52         Log.i(TAG, "1----------onStop");
53     }
54 
55     @Override
56     protected void onRestart() {
57         super.onRestart();
58         Log.i(TAG, "1----------onRestart");
59     }
60 
61     @Override
62     protected void onDestroy() {
63         super.onDestroy();
64         Log.i(TAG, "1----------onDestroy");
65     }
66 }
MainActivity.java

  再來個SecondActivity,同樣覆寫以上全部方法:佈局

 1 package com.lcw.rabbit.activitydemo;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.util.Log;
 6 
 7 public class SecondActivity extends Activity {
 8 
 9     private static final String TAG = "Rabbit";
10 
11     @Override
12     protected void onCreate(Bundle savedInstanceState) {
13         super.onCreate(savedInstanceState);
14         setContentView(R.layout.activity_second);
15         Log.i(TAG, "2----------onCreate");
16     }
17 
18     @Override
19     protected void onStart() {
20         super.onStart();
21         Log.i(TAG, "2----------onStart");
22     }
23 
24     @Override
25     protected void onResume() {
26         super.onResume();
27         Log.i(TAG, "2----------onResume");
28     }
29 
30     @Override
31     protected void onPause() {
32         super.onPause();
33         Log.i(TAG, "2----------onPause");
34     }
35 
36     @Override
37     protected void onStop() {
38         super.onStop();
39         Log.i(TAG, "2----------onStop");
40     }
41 
42     @Override
43     protected void onRestart() {
44         super.onRestart();
45         Log.i(TAG, "2----------onRestart");
46     }
47 
48     @Override
49     protected void onDestroy() {
50         super.onDestroy();
51         Log.i(TAG, "2----------onDestroy");
52     }
53 
54 
55 }
SecondActivity.java

   

 

二、Activity的流程之旅:(參照上面的流程圖來理解)this

實驗一:spa

  當咱們進入第一個Activity的時候,咱們的Log日誌依次打印出了:onCreate,onStart,onResume方法。設計

結論:3d

  當咱們第一次啓動Activity的時候會調用onCreate方法,而後在界面展現出來的時候調用了onStart方法,當用戶獲取焦點的時候調用onResume方法。

實驗二: 

   當咱們按下Back鍵的時候,咱們的Log日誌依次打印出了:onPause,onStop,onDestroy方法。

結論:

  當Activity處於暫停(未被徹底遮擋,好比彈出對話框狀態)的時候會調用onPause,當徹底不可見(被隱藏)的時候會調用onStop方法,當被銷燬的時候會調用onDestroy方法。

實驗三:

  當咱們從新進入Activity,它依舊是調用onCreate,onStart,onResume方法,當咱們點擊按鈕跳轉第二個Activity的時候,咱們的Log日誌依次打印出了:onPause,而後第二個Activity打印出依次onCreate,onStart,onResume,而後第一個Activity再調用onStop方法。

結論:

  當Activity處於暫停(未被徹底遮擋)的時候會調用onPause,而後啓動第二個Activity,執行第二個Activity被建立的生命週期onCreate,onStart,onPause方法,當第一個Activity徹底不可見(被隱藏)的時候會調用onStop方法。

實驗四:

  當咱們按下Back鍵返回到第一個Activity的時候,Log日誌依次打印出:第二個Activity的onPause,而後進入第一個Activity的onReStart,onStrat,onResume,而後第二個Activity調用了onStop,onDestroy。

結論:

  當咱們按下Back鍵返回到第一個Activity的時候,第二個Activity會先調用onPause暫停,因爲第一個Activity剛纔沒被調用onDestroy銷燬,因此這裏沒有從新調用onCreate方法建立而是調用了onReStart方法,而後展現界面onStart,獲取屏幕焦點onResume,而後第二個Activity被徹底覆蓋執行onStop,而後被銷燬onDestroy。

 

三、Activity設計模式之美

疑問1:  

  爲何不能直接去啓動第二個Activiity?

答:

  其實你們能夠這樣來理解,當我啓動一個正在執行音樂播放的Activity的時候,忽然有一個電話打進來了,電話也是一個Activity,那麼在沒有對第一個播放音樂的Activity進行暫停操做,就接通了電話,那麼是否是咱們在邊通話的時候還會邊放着音樂啊?很顯然這是不符合常理的。

 

疑問2:

  當咱們啓動別的Activity的時候,爲何不先把一個Activity徹底銷燬了,而後再去啓動另外一個?

答:

  仍是剛纔打電話的例子,若是咱們直接徹底銷燬了前面一個Activity,那麼咱們在接電話的時候固然就是舒服,由於沒有音樂吵你了,可是當咱們接完電話呢?咱們是否是又要去從新打開那個音樂的Activity,而後再重投開始聽?而後恰好又來一個電話呢?我想這時的你應該要摔電話了。

  再來,若是咱們直接去銷燬了這個Activity,那恰好這個電話Activity因爲不知名緣由發生問題呢?那麼此時電話的Activity沒啓動起來,音樂的Activity又銷燬了,那麼用戶的手機屏幕就會出現黑屏(閃屏),這點在用戶體驗上是很是很差的。

  可能敏捷點的朋友已經想到這個暫停方法onPause的好處了,當電話來的時候,咱們去暫停音樂Activity的同時調用了onPause方法,咱們就能夠在這個方法裏面去記錄一些東西了,好比當前音樂的播放進度,當咱們接完電話,回到音樂Activity的時候,咱們會調用(實驗四)的方法,咱們能夠在onRestart或者onResume中根據剛記錄下來的播放進度去繼續播放音樂。

  因此Activity的生命週期這樣去設計是很是合理的。

 

四、Activity交互實例

  光說不練假把式,接下來上個示例,就以播放音樂爲例子,註釋很全,這裏就再也不多說了,要注意的是,咱們進行了多媒體操做,咱們須要在onDestroy的時候釋放資源對象,不然會佔着內存,程序會愈來愈卡。

 1 package com.lcw.rabbit.activitydemo;
 2 
 3 import android.app.Activity;
 4 import android.content.Intent;
 5 import android.media.MediaPlayer;
 6 import android.os.Bundle;
 7 import android.util.Log;
 8 import android.view.View;
 9 import android.widget.Button;
10 
11 public class MainActivity extends Activity {
12 
13     private static final String TAG = "Rabbit";
14 
15     private Button mbButton;
16     private MediaPlayer mMediaPlayer;
17     private int mCurrentPosition;
18 
19     @Override
20     protected void onCreate(Bundle savedInstanceState) {
21         super.onCreate(savedInstanceState);
22         setContentView(R.layout.activity_main);
23         //點擊按鈕跳轉第二個Activity
24         mbButton = (Button) findViewById(R.id.bt_button);
25         mbButton.setOnClickListener(new View.OnClickListener() {
26             @Override
27             public void onClick(View v) {
28                 startActivity(new Intent(MainActivity.this, SecondActivity.class));
29             }
30         });
31 
32         //播放音樂
33         mMediaPlayer=MediaPlayer.create(this,R.raw.music);
34         mMediaPlayer.start();
35 
36         Log.i(TAG, "1----------onCreate");
37     }
38 
39     @Override
40     protected void onStart() {
41         super.onStart();
42         Log.i(TAG, "1----------onStart");
43     }
44 
45     @Override
46     protected void onResume() {
47         super.onResume();
48         if(mCurrentPosition!=0){
49             //若是當前有記錄進度,繼續播放
50             mMediaPlayer.seekTo(mCurrentPosition);
51             mMediaPlayer.start();
52         }
53         Log.i(TAG, "1----------onResume");
54     }
55 
56     @Override
57     protected void onPause() {
58         super.onPause();
59         //記錄當前進度
60         mCurrentPosition=mMediaPlayer.getCurrentPosition();
61         mMediaPlayer.pause();
62         Log.i(TAG, "1----------onPause");
63     }
64 
65     @Override
66     protected void onStop() {
67         super.onStop();
68         Log.i(TAG, "1----------onStop");
69     }
70 
71     @Override
72     protected void onRestart() {
73         super.onRestart();
74         Log.i(TAG, "1----------onRestart");
75     }
76 
77     @Override
78     protected void onDestroy() {
79         super.onDestroy();
80         //釋放資源
81         mMediaPlayer.release();
82         mMediaPlayer=null;
83         Log.i(TAG, "1----------onDestroy");
84     }
85 }
MainActivity.java

 

五、關於Activity數據狀態的保存

  因爲手機是便捷式移動設備,掌握在用戶的手中,它的展現方向咱們是沒法預知的,具備不肯定性。平時咱們拿着手機多數爲豎屏,但有時候咱們感受累了也會躺着去使用手機,那麼這時手機屏幕的展現方向可能已經被用戶切換成橫屏,因爲豎屏和橫屏的界面寬高比例不一樣,那麼咱們的佈局界面就會發生改變,因此是件很麻煩的事情,咱們須要去準備兩套UI佈局,固然不少時候咱們爲了節省設計成本,只准備一套UI佈局(豎屏或者橫屏),使程序固定在一個方向,讓其不跟隨着屏幕的旋轉而旋轉。在這裏咱們先不去管這些東西,咱們來看看當屏幕旋轉的時候,Activity的生命週期是怎麼走的:

實驗五:

  啓動一個Activity,對屏幕進行翻轉,觀看生命週期的變化

結論:

  在咱們翻轉屏幕的時候,會銷燬當前的Activity,而後重建Activity。

  對Activity進行重建的時候,咱們的數據就會丟失,不少時候,當咱們切換到別的Activity的時候,須要保存當前Activity的狀態或者是臨時數據,那麼咱們該怎麼辦呢?

  咱們在Activity裏再覆寫這兩個方法:

 1     @Override
 2     protected void onSaveInstanceState(Bundle outState) {
 3         super.onSaveInstanceState(outState);
 4         Log.i(TAG, "1----------onSaveInstanceState");
 5     }
 6 
 7     @Override
 8     protected void onRestoreInstanceState(Bundle savedInstanceState) {
 9         super.onRestoreInstanceState(savedInstanceState);
10         Log.i(TAG, "1----------onRestoreInstanceState");
11     }

  而後咱們再來看下這兩個方法是何時被調用的:

一、當正常進入退出的時候,生命週期依舊正常,這兩個方法沒有被調用:

 二、當咱們正常進入一個Activity點擊按鈕跳轉到別的Activity的時候,咱們會發現onSaveInstanceState在第二個Activity獲取屏幕焦點(onResume)以後,在第一個Activity執行onPause以後,onStop以前調用了此方法,當從第二個Activity切換回來的時候就重複執行着實驗四。

   細心的朋友可能已經發現,onSaveInstanceState方法裏有個Bundle類型的回調參數,在onCreate裏面也有個Bundle類型的參數,沒錯,答案就在這裏,若是咱們要對Activity的數據或者狀態進行臨時性的保存時,咱們能夠在onSaveInstaceState存入參數,相似這樣的:

1     @Override
2     protected void onSaveInstanceState(Bundle outState) {
3         super.onSaveInstanceState(outState);
4         outState.putString("name","Rabbit");
5         Log.i(TAG, "1----------onSaveInstanceState");
6     }

  在onCreate裏獲取:

 1     @Override
 2     protected void onCreate(Bundle savedInstanceState) {
 3         super.onCreate(savedInstanceState);
 4         setContentView(R.layout.activity_main);
 5         //獲取保存數據
 6         if (savedInstanceState!=null){
 7             Log.i(TAG, "I am "+savedInstanceState.get("name") );
 8         }
 9 
10         //點擊按鈕跳轉第二個Activity
11         mbButton = (Button) findViewById(R.id.bt_button);
12         mbButton.setOnClickListener(new View.OnClickListener() {
13             @Override
14             public void onClick(View v) {
15                 startActivity(new Intent(MainActivity.this, SecondActivity.class));
16             }
17         });
18 
19 
20         Log.i(TAG, "1----------onCreate");
21     }

  在onRestoreInstanceState裏取:

1     @Override
2     protected void onRestoreInstanceState(Bundle savedInstanceState) {
3         super.onRestoreInstanceState(savedInstanceState);
4         //獲取保存數據
5         if (savedInstanceState!=null){
6             Log.i(TAG, "I am "+savedInstanceState.get("name") );
7         }
8         Log.i(TAG, "1----------onRestoreInstanceState");
9     }

 

  無圖無真相,來看下實驗結果圖,我進入了Activity對屏幕翻轉,觸發Activity重建,能夠看到數據已經被保存了。

  但這裏有個疑問,當咱們不翻轉屏幕,也就是不觸發Activity重建的時候,咱們是沒有執行onCreate,onRestoreInstanceState方法的,因此這個Bundle對象咱們不必定是能夠拿到的,那數據保存不就變得很不可靠了嗎?

  沒錯,因爲Activity重建的不肯定,因此saveInstanceState保存的數據通常都是臨時性的,真正持久化操做咱們應該在onPause方法裏操做。

 

這裏額外的要提到一點,onRestoreInstanceState方法在兩種狀態下會被調用:

一、在Activity被覆蓋或退居後臺以後,系統資源不足將其殺死,而後用戶又回到了此Activity,此方法會被調用;

二、在用戶改變屏幕方向時,重建的過程當中,此方法會被調用。

 

六、關於屏幕方向改變Activity會重建的應對策略:

一、

  指定爲豎屏:在AndroidManifest.xml中對指定的Activity設置:

android:screenOrientation="portrait"

  或者在onCreate方法中指定:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);  //豎屏  

   指定爲橫屏:在AndroidManifest.xml中對指定的Activity設置:

android:screenOrientation="landscape"

  或者在onCreate方法中指定:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //橫屏  

 

二、

  鎖定屏幕雖然能夠實現咱們想要的效果,但並非很好的一種作法,爲了不這樣銷燬重建的過程,咱們能夠在AndroidMainfest.xml中對對應的<activity>配置:

android:configChanges="orientation"

   若是是Android4.0,則是:

android:configChanges="orientation|keyboardHidden|screenSize"

  而後咱們在Activity裏重寫onConfigurationChanged方法:

1     @Override
2     public void onConfigurationChanged(Configuration newConfig) {
3         super.onConfigurationChanged(newConfig);
4         Log.i(TAG, "1----------onConfigurationChanged");
5     }

  這樣Activity在翻轉屏幕的時候就不會被銷燬重建了,只是調用了onConfigurationChanged方法。

 

總結:

作工做中,你可能感興趣的三個關鍵環① 完整生命週期② 可見生命週期③ 可交互生命週期 
如圖所示,圖中的週期都是大的包括小的:

在實際工做中的使用
①onResume可見, 可交互.。把動態刷新的操做啓動。
②onPause部分可見, 不可交互. 把動態刷新的一些操做, 給暫停了。
③onCreate 初始化一些大量的數據
④onDestroy 把數據給釋放掉, 節省內存。

 

  好了,今天先寫到這裏,關於Activity單單瞭解這些是遠遠不夠的,下篇文章講關於Activity的任務棧已經回退棧等操做。

 

 

做者:李晨瑋
出處:http://www.cnblogs.com/lichenwei/本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文連接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,往後必有一番做爲!旁邊有「推薦」二字,你就順手把它點了吧,相得準,我分文不收;相不許,你也好回來找我!

相關文章
相關標籤/搜索