Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Activity數據保存探索

Activity數據保存探索

編輯:關於Android編程

在開發中發現一個問題:當一個我通過Intent開啟一個前面已經打開的activty的界面時,
新打開的activity的狀態會丟失。當時,當我直接按home減將acitvity置於後台,然後重新打開這個activity的時候,發現此時的activity的狀態是退出之前的狀態。但是,我現在我想達到的目的是,不管是以什麼方式打開這個活動,我都想要他恢復到之前的acitvity狀態,而不是新開一個新的activity。於是針對這現象,我尋求解決方案。

利用onSaveInstanceState和onRestoreInstanceState

在發現這個問題之後,我的第一感覺就是利用activity中的onRestoreInstanceState方法在打開新的activity時將保存的數據讀取並寫入到新的activity中,實現的原理就是我之前說的按下home鍵重新打開activity能恢復到原來的activity狀態的原理。在這裡我將展開講解activity中數據的保存機制。

activity的生命周期

要想完全理解activity中的儲存機制,就必須對activity的生命周期非常熟悉,在這裡我就簡單介紹一下acttivity的生命周期。這裡直接給出一張官方的activity生命周期圖:

這裡寫圖片描述

如果對activity的生命周期還是不太了解,可以參考我後面代碼裡面對每個activity周期的方法的注釋。這裡就不做過多的解釋。

### acitvity中的數據儲存機制

當一個activity被paused或者stopped時,activity的狀態可以被保存。 的確如此,因為 Activity 對象在paused或者stopped時仍然被保留在內存之中——它所有的成員信息和當前狀態都仍然存活。 這樣用戶在activity裡所作的改動全都還保存著,所以當activity返回到前台時(當它“resume“),那些改動仍然有效。
不過,如果系統是為了回收內存而銷毀activity,則這個 Activity 對象就會被銷毀,這樣系統就無法簡單地resume一下就能還原完整狀態的activity。 如果用戶要返回到這個activity的話,系統必須重新創建這個Activity 對象。可是用戶並不知道系統是先銷毀activity再重新創建了它的,所以,他很可能希望activity完全保持原樣。 這種情況下,你可以保證activity狀態的相關重要信息都由另一個回調方法保存下來了,此方法讓你能保存activity狀態的相關信息: onSaveInstanceState()。
在activity變得很容易被銷毀之前,系統會調用 onSaveInstanceState()方法。 調用時系統會傳入一個Bundle對象, 你可以利用 putString() 之類的方法,以鍵值對的方式來把activity狀態信息保存到該Bundle對象中。 然後,如果系統殺掉了你的application進程並且用戶又返回到你的activity,系統就會重建activity並將這個 Bundle 傳入onCreate() 和onRestoreInstanceState() 中,你就可以從 Bundle 中解析出已保存信息並恢復activity狀態。如果沒有儲存狀態信息,那麼傳入的 Bundle 將為null(當activity第一次被創建時就是如此)

這裡在給出官方的圖片來說明activity狀態的存儲這裡寫圖片描述
或許這個能更加直觀的表現 onSaveInstanceState()和onRestoreInstanceState()的調用這裡寫圖片描述vc/yt6LJ+rjEseQ8L3A+DQo8cD7L+dLU1NrJz8r2x+m/9s/Co6xhY3Rpdml0ebvhxKzIz7X308NvblNhdmVJbnN0YW5jZVN0YXRlKCnAtLGjtObK/b7do6zWwdPatffTw8uz0PKjrMfrss7V1ceww+a1xM28xqyho6GiPC9wPg0KPHA+PHN0cm9uZz69srW91eLA76Osvs2yu7XDsrvLtcu11tjQtG9uU2F2ZUluc3RhbmNlU3RhdGUoKbe9t6jBy6GjPC9zdHJvbmc+PGJyIC8+DQrI57n70OjSqrGjtOa27s3itcTK/b7dyrEsIL7N0OjSqriy0LRvblNhdmVJbnN0YW5jZVN0YXRlKCm3vbeooaO087zS0OjSqtei0uK1xMrHo7pvblNhdmVJbnN0YW5jZVN0YXRlKCm3vbeo1rvKyrrPsaO05suyzKzK/b7dLCCxyMjnVUm/2Lz+tcTXtMysLCCzydSxseTBv7XE1rW1yKOstviyu9OmuMPTw8C0saO05rPWvsO7r8r9vt2jrLPWvsO7r8r9vt3TprjDtbHTw7unwOu/qrWxx7C1xCBhY3Rpdml0ecqxo6zU2iBvblBhdXNlKCkg1tCxo7Tmo6ixyMjnvavK/b7dsaO05rW9yv2+3b/iu/LOxLz+1tCjqaGjy7W1vdXiwO+jrLu50qrLtdK7teO1xL7NysfU2m9uUGF1c2UoKdbQsrvKyrrP08PAtLGjtOaxyL3Pt9HKsbXEyv2+3aOsy/nS1NXitePSqsDtveKhozxiciAvPg0K08nT2m9uU2F2ZUluc3RhbmNlU3RhdGUoKbe9t6i3vbeosrvSu7aou+Gxu7X308MsINLytMuyu8rKus/U2rjDt723qNbQsaO05rPWvsO7r8r9vt0sIMD9yOfP8sr9vt2/4tbQsuXI67zHwry1yC4gsaO05rPWvsO7r8r9vt21xLLZ1/fTprjDt8XU2m9uUGF1c2UoKdbQoaPI9MrH08C+w9DU1rWjrNTy1NpvblBhdXNlKCnW0LGjtOaju8j0tPPBv6Os1PLB7b+qz9+zzLDJo6yx8NfoyPtVSc/fs8yhozwvcD4NCjxwPr2ytb3V4sDvo6y6w8/x09C148P3wcujrMTHw7TWu9KqztLDx9TaYWN0aXZpdHnW0M/Uyr61xLX308NvblNhdmVJbnN0YW5jZVN0YXRlKCmjrLK7vsO/ydLUuty6w7XEveK+9tXiuPbOysziw7SjrNPaysfO0tC0wcvI58/CtcS0+sLrLNXiwO/Kx01haW5BY3Rpdml0ebXExNrI3aGjPC9wPg0KPHByZSBjbGFzcz0="brush:java;"> package com.create.activitydata; import android.content.Intent; import android.os.PersistableBundle; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.CheckBox; import android.widget.EditText; /** * 測試保存Activity中的數據 */ public class MainActivity extends AppCompatActivity { private EditText editText; private CheckBox checkBox; private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText = (EditText)findViewById(R.id.text); checkBox = (CheckBox)findViewById(R.id.check); } /** * activity被停止後、又再次被啟動之前調用。 下一個回調方法總是onStart() */ @Override protected void onRestart() { //這裡可以手動添加讀取Activity的UI狀態代碼 super.onRestart(); } /** * activity要顯示給用戶之前調用。 *如果activity進入前台,則下一個回調方法是onResume(); *如果進入隱藏狀態,則下一個回調方法是onStop()。 */ @Override protected void onStart() { super.onStart(); } /** * activity開始與用戶交互之前調用。這時activity是在activity棧的頂端,用戶可以向其中輸入。 *下一個回調方法總是onPause()。 */ @Override protected void onResume() { super.onResume(); } /** * 當系統准備啟動另一個正在恢復的activity時調用。這個方法通常用於把未保 * 存的改動提交為永久數據、停止動畫播放、以及其它可能消耗CPU的工作等等。 它應該非常迅速 * 地完成工作,因為下一個activity在本方法返回前是不會被恢復運行的。 * 如果activity返回前台,則下一個回調方法是onResume();如果進入用戶不可見狀態,則下一個是onStop() */ @Override protected void onPause() { super.onPause(); //在這裡可以添加比較耗時,需要長久保存的數據。 } /** * 當activity不再對用戶可見時調用。原因可能是它即將被銷毀、或者其它activity(已有或新建的) * 被恢復運行並要覆蓋本activity。 *如果activity還會回來與用戶交互,則下一個回調方法是onRestart();如果這個activity即將消失, *則下一個回調方法是onDestroy() */ @Override protected void onStop() { //在這裡添加保存Activity狀態的代碼 super.onStop(); } @Override protected void onDestroy() { super.onDestroy(); } /** * 讀取數據 * 讀取和儲存Activity狀態的兩個方法並不在Activity生命周期中 * 但在Activity意外停止時系統會調用缺省的方法保存當前狀態,並在 * 下次打開Activity是調用onSaveInstanceState以恢復到原來的狀態 */ @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); Log.e(TAG,"onRestoreInstanceState"); } /** * 保存數據 * @param outState * @param outPersistentState */ @Override public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { super.onSaveInstanceState(outState, outPersistentState); Log.e(TAG,"onSaveInstanceState"); } public void toNext(View view){ Intent intent = new Intent(MainActivity.this,NextActivity.class); startActivity(intent); } }

但是在這裡我想說,我試了很久也沒能顯示的調用onSaveInstanceState()這些方法,對activity裡面的源代碼並沒有多大的理解,於是我放棄了用這種方法去恢復activity的狀態
(ps,對了,在這裡加一句,只有加了id的控件調用onSaveInstanceState()時才能正常保存,否則也會失敗,至於如何使這個方法失效,可以通過把android:saveEnabled 設置為”false”,或者調用 setSaveEnabled() 方法)

利用activity的啟動方式

在上面的方法失敗之後,我又需求別人的幫助。給出的思路就是,當利用Intent開啟一個已經被開啟過的activity時,沒必要再重新new一個activity了,而是直接打開之前的那麼activity。想到這,煥然大悟:這個我可以直接利用activity的啟動方式來時事當前的activity一直處於棧頂,當開啟他的時候,直接打開它而不是new一個activity。這樣就完美的解決了我的問題。僅僅一行代碼。

首先activity的啟動模式只有四種:
* standard
standard是活動默認的啟動模式,在不進行顯式指定的情況下,所有活動都會自動使用 這種啟動模式。在 standard模式(即默認情 況)下,每當啟動一個新的活動,它就會在返回棧中入棧,並處於棧頂的位置。對於使用 standard模式的活動,系統不會在乎這個活動是否已經在返回棧中存在,每次啟動都會創建 該活動的一個新的實例。
這裡寫圖片描述
* singleTop
當活動的啟動模式 指定為 singleTop,在啟動活動時如果發現返回棧的棧頂已經是該活動,則認為可以直接使用 它,不會再創建新的活動實例。
這裡寫圖片描述
* singleTask
使用 singleTop模式可以很好地解決重復創建棧頂活動的問題,但是正如你在上一節所 看到的,如果該活動並沒有處於棧頂的位置,還是可能會創建多個活動實例的。那麼有沒有 什麼辦法可以讓某個活動在整個應用程序的上下文中只存在一個實例呢?這就要借助 singleTask模式來實現了。當活動的啟動模式指定為 singleTask,每次啟動該活動時系統首先 會在返回棧中檢查是否存在該活動的實例,如果發現已經存在則直接使用該實例,並把在這 個活動之上的所有活動統統出棧,如果沒有發現就會創建一個新的活動實例。
這裡寫圖片描述
* singleInstance
singleInstance模式的活動會啟用一 個新的返回棧來管理這個活動(其實如果 singleTask模式指定了不同的 taskAffinity,也會啟 動一個新的返回棧),在這種模式下會有一個單獨的返回棧來管理這個活 動,不管是哪個應用程序來訪問這個活動,都共用的同一個返回棧,也就解決了共享活動實 例的問題。

留個漂亮的尾巴~

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved