編輯:關於Android編程
本文介紹Android中關於Activity的兩個神秘方法:onSaveInstanceState()和onRestoreInstanceState(),並且在介紹這兩個方法之後,再分別來實現使用InstanceState保存和恢復數據功能、Android實現屏幕旋轉異步下載效果這樣兩個示例。
首先來介紹onSaveInstanceState()和onRestoreInstanceState()。關於這兩個方法,一些朋友可能在Android開發過程中很少用到,但在有時候掌握其用法會幫我們起到比較好的效果。尤其是在應用程序在不知道的情況下退出後,如何實現其數據保存的功能。先來讓我們看下這兩個方法的有什麼樣的作用。
這就是onSaveInstanceState() 和 onRestoreInstanceState() 兩個函數的基本作用和用法。
總而言之,onSaveInstanceState()的調用遵循一個重要原則,即當系統存在“未經你許可”時銷毀了我們的activity的可能時,則onSaveInstanceState()會被系統調用,這是系統的責任,因為它必須要提供一個機會讓你保存你的數據(當然你不保存那就隨便你了)。如果調用,調用將發生在onPause()或onStop()方法之前。(雖然測試時發現多數在onPause()前)
onRestoreInstanceState()在onStart() 和 onPostCreate(Bundle)之間調用。
由上所述, 如果我們需要覆寫onSaveInstanceState()方法, 一般會在第一行代碼中調用該方法的默認實現:super.onSaveInstanceState(outState)。
由於onSaveInstanceState()方法方法不一定會被調用,因此不適合在該方法中保存持久化數據,例如向數據庫中插入記錄等.。保存持久化數據的操作應該放在onPause()中。若是永久性值,則在onPause()中保存;若大量,則另開線程吧,別阻塞UI線程。
另外,當屏幕的方向發生了改變,Activity會被摧毀並且被重新創建,如果你想在Activity被摧毀前緩存一些數據,並且在Activity被重新創建後恢復緩存的數據。可以重寫Activity的 onSaveInstanceState() 和 onRestoreInstanceState()方法,如下代碼所示:
import android.R;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
/**
* Android使用InstanceState保存和恢復數據
*/
public class MainActivity extends Activity {
private String message = "";
private EditText text = null;
private Button button = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
text = (EditText) findViewById(R.id.editText1);
button = (Button) findViewById(R.id.btnSave);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "保存",
Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onResume() {
super.onResume();
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG)
.show();
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("message", text.getText().toString());
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
message = savedInstanceState.getString("message");
}
}
還有需要注意的是,onSaveInstanceState()方法並不是一定會被調用的,因為有些場景是不需要保存狀態數據的。比如用戶按下BACK鍵退出activity時,用戶顯然想要關閉這個activity,此時是沒有必要保存數據以供下次恢復的,也就是onSaveInstanceState()方法不會被調用。如果調用onSaveInstanceState()方法,調用將發生在onPause()或onStop()方法之前。
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
}
我們可以用旋轉屏幕來測試我們的程序保存狀態的能力。因為一般旋轉時,activity會被重新執行onCreate(),onStart()等操作,程序此時能夠保存狀態是很重要的。可以考慮使用該方法保存數據。當然了在旋轉屏幕時我們除了使用onSaveInstanceState()外,還可以使用onRetainNonConfigurationInstance()和getLastNonConfigurationInstance()這兩個方法來保存切換屏幕的狀態。與onSaveInstanceState()不同的是,onRetainNonConfigurationInstance()和getLastNonConfigurationInstance() 方法主要用於屏幕之間的旋轉操作時保存數據。
@Override
public Object onRetainNonConfigurationInstance() {
// 在這裡設置需要保存的內容,在切換時不是bundle了,我們可以直接通過object來代替。
returnsuper.onRetainNonConfigurationInstance();
}
在恢復屏幕時可以不使用onRestoreInstanceState(),而使用getLastNonConfigurationInstance()來代替。我們可以直接在oncreate()方法中獲取上次保存的對象。
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 獲取上次切換屏幕保存的對象
Object obj = getLastNonConfigurationInstance();
}
對於我們的程序而言,或多或少的都需要進行Activity之間的跳轉操作,其中有一些是為了獲得系統中的資源或是一些必要信息,而一般是通過啟動Activity(常用startActivity()和startActivityForResult()函數)來進行操作。在這個跳轉的期間,我們當前的Activity暫時失去了焦點,處於不可操作狀態,可在此之前先通過onSaveInstanceState()方法來保存一些暫時時的數據。當回到先前的Activity時,先前的Activity重新獲取了焦點,系統就是觸發onRestoreInstanceState()方法,可獲取失去焦點前的一些數據。onRetainNonConfigurationInstance()方法也具有類似的功能來處理這樣的數據操作。先前說過,onRetainNonConfigurationInstance()方法主要是用於屏幕的旋轉操作。
說到這裡了,可能有的人就要問了,既然onSaveInstanceState()和onRetainNonConfigurationInstance()都可以實現保存數據的功能,如果是兩個同時使用時,執行順序是哪個在先,哪個在後呢?根據Android官方網站上介紹,如果兩個方法同時出現時,onSaveInstanceState()方法執行在先,而onRetainNonConfigurationInstance()方法執行在後。它們的執行順序都在onStop()和onDestroy()之間,關於這點需要我們大家注意。
之前在其它網站上看到有的朋友說:"onSaveInstanceState()和onRetainNonConfigurationInstance()既然都可以實現保存數據的功能,而且onSaveInstanceState()相比onRetainNonConfigurationInstance()方法可以實現更多情況下的數據保存功能,那麼onRetainNonConfigurationInstance()豈不是多余的嗎?"。關於這點,從設計的角度看,onRetainNonConfigurationInstance()並不是多余的函數。一般情況下,如果我們要保存的數據不太大,而且適合放在Bundle中,那麼使用onSaveInstanceState()是比較合適的;如果要保存的數據不適合放在Bundle中(比如:一個socket)或是數據比較大(比如:一個Bitmap),那麼這個時間我們就應該使用onRetainNonConfigurationInstance(),而且我們使用onRetainNonConfigurationInstance()可以保存任何類型的對象,像AsyncTask和SQLiteDatabse,我們都可以進行保存。這些類型的數據可能會被一個新的Activity實例所重新使用。所以onSaveInstanceState()和onRetainNonConfigurationInstance()在我們的程序中扮演的是不同的角色,需要在不同的時機下調用,用來處理不同類型的數據。
例如下面代碼所示要保存一個復雜的數據:
import android.graphics.Bitmap;
public class DataHolder {
int a;
Bitmap b;
String s;
public Object onRetainNonConfigurationInstance() {
DataHolder dh = new DataHolder();
dh.a = a;
dh.b = b;
dh.s = s;
return dh;
}
}
不過呢,onRetainNonConfigurationInstance()在新版本的SDK中是一個過時的方法,我們可以用setRetainInstance(boolean)來代替onRetainNonConfigurationInstance(),在舊的平台中我們仍然可以使用onRetainNonConfigurationInstance()。如果是部分朋友不知道如何使用setRetainInstance(boolean)來保存自定義的對象數據,可以使用onRetainCustomNonConfigurationInstance()來代表onRetainNonConfigurationInstance(),同時使用getLastCustomNonConfigurationInstance()代替getLastNonConfigurationInstance()。
下面是一個使用onRetainNonConfigurationInstance()和getLastNonConfigurationInstance()來實現屏幕旋轉時異步下載更新進度條並保存數據的效果,代碼如下所示:
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
/**
* Android實現屏幕旋轉異步下載效果
*
* @Description: Android實現屏幕旋轉異步下載效果
*
* @File: RotationAsyncActivity.java
*
* @Package com.rotation.demo
*
* @Author Hanyonglu
*
* @Date 2012-03-28 下午08:14:57
*
* @Version V1.0
*/
public class RotationAsyncActivity extends Activity {
// 進度條
private ProgressBar progressBar = null;
// 異步任務類
private RotationAsyncTask asyncTask = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
progressBar = (ProgressBar) findViewById(R.id.progress);
// 獲取對象
asyncTask = (RotationAsyncTask) getLastNonConfigurationInstance();
if (asyncTask == null) {
asyncTask = new RotationAsyncTask(this);
asyncTask.execute();
} else {
asyncTask.attach(this);
updateProgress(asyncTask.getProgress());
if (asyncTask.getProgress() >= 100) {
markAsDone();
}
}
}
/**
* 保存對象
*/
@Override
public Object onRetainNonConfigurationInstance() {
asyncTask.detach();
return asyncTask;
}
private void updateProgress(int progress) {
progressBar.setProgress(progress);
}
private void markAsDone() {
findViewById(R.id.completed).setVisibility(View.VISIBLE);
}
// 異步任務類
private static class RotationAsyncTask extends AsyncTask {
private RotationAsyncActivity activity = null;
private int progress = 0;
/**
* 默認的構造器
*/
public RotationAsyncTask() {
// TODO Auto-generated constructor stub
}
/**
* 帶參構造器
*
* @param activity
*/
public RotationAsyncTask(RotationAsyncActivity activity) {
attach(activity);
}
@Override
protected Void doInBackground(Void... unused) {
for (int i = 0; i < 20; i++) {
SystemClock.sleep(500);
publishProgress();
}
return null;
}
@Override
protected void onProgressUpdate(Void... unused) {
if (activity == null) {
Log.w("RotationAsyncActivity", "onProgressUpdate()");
} else {
progress += 5;
activity.updateProgress(progress);
}
}
@Override
protected void onPostExecute(Void unused) {
if (activity == null) {
Log.w("RotationAsyncActivity", "onPostExecute()");
} else {
activity.markAsDone();
}
}
protected void detach() {
activity = null;
}
protected void attach(RotationAsyncActivity activity) {
this.activity = activity;
}
protected int getProgress() {
return progress;
}
}
}
我們在運行示例時就會發現,無論怎樣旋轉屏幕都不會影響進度條的更新與下載,需要解釋下的是這裡我並沒有設置下載功能,有興趣或需要的朋友自己添加即可。實現效果圖如下所示:以上就是在Android中關於InstanceState保存數據和恢復數據的過程,在這裡我想再重復一遍:onSaveInstanceState()和onRestoreInstanceState()機制來保存數據時,它僅在非用戶顯式的指令殺死應用程序時保存和恢復數據。我們可以使用它在我們的程序中來保存數據,可以作為保存數據的一種方式,但在使用過程中需要注意其使用原理和方法。
Android PreferenceActivity與PreferenceFragment詳解及簡單實例
Android PreferenceActivity與PreferenceFragment前言轉來轉去又回到了Android,閒話少說,這裡是參考Androi
Android應用開發SharedPreferences存儲數據的使用方法
SharedPreferences是Android中最容易理解的數據存儲技術,實際上SharedPreferences處理的就是一個key-value(鍵值對)。Shar
Android 系統狀態欄一體化
Android4.4新特性,系統狀態欄一體化。 實現的步驟主要有以下幾點: 1.android4.4 以上版本 2.設置app全屏: 方法:在AndroidManifes
Android開發之自定義View(視圖)用法詳解
本文實例講述了Android開發之自定義View(視圖)用法。分享給大家供大家參考,具體如下:View類是Android的一個超類,這個類幾乎包含了所有的屏幕類型。每一個