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 }
再來個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 }
二、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 }
五、關於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/本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文連接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,往後必有一番做爲!旁邊有「推薦」二字,你就順手把它點了吧,相得準,我分文不收;相不許,你也好回來找我!