編輯:關於Android編程
在不破壞封閉的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態,這樣,以後就可將該對象恢復到原先保存的狀態。
Originator:負責創建一個備忘錄,可以記錄、恢復自身的內部狀態。同時Originator可以根據需要決定Memento存儲自身的那些內部狀態。
Memento: 備忘錄角色,用於存儲Originator的內部狀態,並且可以防止Originator以外的對象訪問Memento。
Caretaker: 負責存儲備忘錄,不能對備忘錄的內容進行操作和訪問,只能夠將備忘錄傳遞給其它對象。
(1)、游戲類:Game情景如下:我們都玩過單機游戲,單機裡面有一個很重要的功能就是存檔,存儲當前游戲進度。下次再進入游戲時,恢復上一次的進度,繼續游戲。
public class Game {
private int mCheckpoint = 1; //分數
private int mLifeValue = 100; //生命
//玩游戲
public void play() {
mLifeValue -= 10;
mCheckpoint++;
}
public void quit() {
System.out.println("退出游戲");
}
//顯示當前游戲信息
public void showInfos() {
System.out.println(this.toString());
}
public Memoto createMemoto() {
//創建存檔
Memoto memoto = new Memoto();
memoto.mLifeValue = mLifeValue;
memoto.mChackpoint = mCheckpoint;
return memoto;
}
//恢復存檔
public void restoreMemoto(Memoto memoto) {
this.mCheckpoint = memoto.mChackpoint;
this.mLifeValue = memoto.mLifeValue;
System.out.println("恢復進度");
}
@Override
public String toString() {
return "當前生命值:" + mLifeValue + ",分數:" + mCheckpoint;
}
}
上面的代碼,最終要的部分就是createMemoto()方法,將當前狀態信息存儲在Memoto對象裡面。
(2)、備忘錄類:
public class Memoto {
public int mChackpoint;
public int mLifeValue;
public String mWeapon;
}
備忘錄類用來存儲游戲類裡面一個或至多個信息。
(3)、備忘錄操作者:Caretaker角色:
public class Caretaker {
Memoto memoto;//備忘錄
public void archive(Memoto memoto) {
this.memoto = memoto;
}
//獲取存檔
public Memoto getMemoto() {
return memoto;
}
}
該類的作用就是操作備忘錄類Memoto本身的,並不對備忘錄裡面的信息讀取操作。
(4)、測試類:
public class Client {
public static void main(String[] args) {
Game game = new Game();
//打游戲
game.play();
//存檔
Caretaker caretaker = new Caretaker();
caretaker.archive(game.createMemoto());
game.showInfos();
//退出游戲
game.quit();
System.out.println("-----");
//恢復游戲
Game newGame = new Game();
newGame.restoreMemoto(caretaker.getMemoto());
//顯示當前游戲信息
newGame.showInfos();
}
}
上面的測試類就是創建一個游戲類,接著游戲類修改自身的屬性,接著游戲類創建備份,然後新建游戲類,恢復備份,顯示游戲進度和之前的游戲對象進度一模一樣。
在Android源碼中,狀態模式的應用表現在Activity的狀態保存,在onSaveIinstanceState和onRestoreInstanceState方法中
當Activity不是正常方式退出,且Activity在隨後的時間內被系統殺死之前會調用這兩個方法讓開發人員可以有機會存儲Activity相關信息,並且下次再返回式恢復這些數據。
首先我們來說下onSaveInstanceState()方法裡面干了什麼事:
這是onSaveInstanceState()裡面的方法,
protected void onSaveInstanceState(Bundle outState) {
//1.存儲窗口的視圖樹狀態
outState.putBundle(WINDOW_HIERARCHY_TAG,
mWindow.saveHierarchyState());
//2.存儲Fragment的狀態
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
//3.調用Activity的ActivityLifecycleCallbacks的onSaveInstanceState函數進行狀態存儲
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
(1)、存儲窗口的視圖樹狀態 (2)、存儲Fragment的狀態 (3)、調用Activity的ActivityLifecycleCallbacks的onSaveInstanceState函數進行狀態存儲
上面三個步驟都是存儲,我們首先來分析下存儲窗口的視圖狀態:
mWindow.saveHierarchyState());
這句代碼就是存儲窗口的視圖樹狀態,這裡的mwindow對象是PhoneWindow ,我們在PhoneWindow找到這個方法:
saveHierachyState()方法簡化如下:
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
//存儲整顆視圖樹的結構
SparseArray states = new SparseArray();
mContentParent.saveHierarchyState(states);
outState.putSparseParcelableArray(VIEWS_TAG, states);
// 保存當前獲取了焦點的View
//存儲整個面板的狀態
//存儲ActionBar的狀態
return outState;
}
在以上saveHierarchyState函數中,主要存儲了與當前UI、ActionBar相關的View狀態。
這裡我們分析存儲整顆視圖樹:
代碼中的mContentParent就是我們通過Activity的setContentView設置的內容視圖,它是整個視圖樹的根節點。 mContentParent是一個ViewGroup對象,我們在ViewGroup的父類View中到saveHierarchyState()方法:
public void saveHierarchyState(SparseArray container) {
dispatchSaveInstanceState(container);
}
protected void dispatchSaveInstanceState(SparseArray container) {
//如果View沒有設置Id,那麼該View的狀態信息將不會被存儲
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
//調用那個onSaveInstanceState()方法獲取自身狀態信息
Parcelable state = onSaveInstanceState();
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
if (state != null) {
// Log.i("View", "Freezing #" + Integer.toHexString(mID)
// + ": " + state);
//存入View狀態信息,key為view的id。
container.put(mID, state);
}
}
}
上面的代碼意思大致如下:
如果View沒有設置id,那麼該View的狀態信息將不會被保存 調用onSaveInsttanceState()方法獲取自身狀態信息。 當前View的id為key,狀態信息為value,存入之前創建的SparseArray 中,本質上是一個Object數組。以上是View類的中saveHierarchySate函數中dispatchSaveInstanceState函數來存儲自身的狀態
如果是ViewGroup呢?下面是ViewGroup中的dispatchSaveInstanceState函數:
protected void dispatchSaveInstanceState(SparseArray container) {
//調用父類View的dispatchInstanceState方法存儲自身狀態
super.dispatchSaveInstanceState(container);
final int count = mChildrenCount;
final View[] children = mChildren;
//遍歷所有的子視圖調用其dispatchInstanceState方法存儲它們的狀態
for (int i = 0; i < count; i++) {
View c = children[i];
if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
c.dispatchSaveInstanceState(container);
}
}
}
上面的代碼做了以下兩件事:
首先調用父類View的dispatchSaveInstanceState方法存儲了自身的狀態信息 接著遍歷所有的子類,調用其dispatchSaveInstanceState方法存儲它們的狀態信息在View的saveHierarchyState方法裡面有如下代碼:
//調用那個onSaveInstanceState()方法獲取自身狀態信息
Parcelable state = onSaveInstanceState();
這句代碼的意思是獲取自身狀態信息,我們點進去查看源代碼如下:
protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (mStartActivityRequestWho != null) {
BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
return state;
}
return BaseSavedState.EMPTY_STATE;
}
上面的代碼意思是:返回一個存儲了當前View狀態信息的Parcelable對象,如果沒有任何信息,返回null,默認返回null。我們可以得出以下信息
View視圖的狀態信息的存儲在Parcelable對象裡面 如果我們要保存View的狀態信息,需要覆寫onSaveInstanceState()方法,將需要保存的信息存放在Parcelable裡面,然後返回。到這裡我們畫張圖小結一下View和ViewGroup存儲的流程:
上面分析了存儲了Window的視圖樹狀態信息。
存儲了Window的視圖樹狀態信息後,便會執行存儲Fragment中的狀態信息、回退棧等 。
下面我們來分析存儲了狀態信息的Bundle數據存儲在哪裡?
我們知道onSveInstanceState是在Activity被銷毀之前,onStop調用之前。onStop方法在ActivityThread的performStopActivity函數中,這裡就不列出代碼了,主要的步驟大致如下:
(1)、判斷是否需要存儲Activity (2)、如果需要存儲Activity狀態,調用onSaveInstanceState函數獲取狀態信息。 (3)、將狀態信息存儲到ActivityClientRecord對象的state字段 (3)、系統維護了一個Acitivity信息表mActivities,將AcitivityClientRecord對象存儲到Acitivity信息表中。 (4)、調用Activity的onStop()函數
當Activity重新啟時:
從mActivities查詢對應的ActivityClientRecord,如果這個記錄對象中包含有狀態信息,那麼調用Activity的onRestoreInstanceState函數,然後將這些狀態信息傳遞給onCreat方法總結一下onSaveInstanceState的調用時機:
當系統未經我們允許時銷毀了Acitivity,onSaveInstanceState()方法會被調用。常見的幾種場景:
當用戶按下Home鍵時 按下電源鍵時 啟動一個新的Activity時 來電話時 屏幕發生旋轉時如下場景:簡單實現上面的onSaveInstanceState()的使用,當我我們輸入用戶名和密碼後,通過旋轉屏幕Activity會被銷毀,如果不存儲,相關信息可能會丟失,所以在onSaveInstanceState保存相關信息,在onCreate方法裡面獲取信息,重新填充即可。
簡單演示:
代碼簡單實現如下:
public class MainActivity extends AppCompatActivity {
private EditText et_name;
private EditText et_psw;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_name = (EditText) findViewById(R.id.et_name);
et_psw = (EditText) findViewById(R.id.et_psw);
if (savedInstanceState!=null){
String username = savedInstanceState.getString("username");
String psw = savedInstanceState.getString("psw");
System.out.println("username:" + username + ",psw:" + psw);
et_name.setText(username);
et_psw.setText(psw);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("username", et_name.getText().toString());
outState.putString("psw", et_psw.getText().toString());
super.onSaveInstanceState(outState);
}
}
關於activity的動畫,相信大家再熟悉不過了,我們開發中經常用到,網上資料也很多,但是也有一些小細節需要我們注意,今天這篇文章我總結了幾種常用的動畫實現方式,通過這篇
在了解ViewPager的工作原理之前,先回顧ListView的工作原理:ListView只有在需要顯示某些列表項時,它才會去申請可用的視圖對象;如果為所有的列表項數據創
開發中很常見的一個問題,項目中的listview不僅僅是簡單的文字,常常需要自己定義listview,自己的Adapter去繼承BaseAdapter,在adapter
ViewStub可以在運行時動態的添加布局。幫助文檔給定的定義是:A ViewStub is an invisible, zero-sized View that can