• Android 组件提供的状态保存(saveInstanceState)与恢复(restoreInstanceState)


    Android的组件Activity中,有这样一对方法: onSaveInstanceeStateonRestoreInstanceState

    这两对方法,可以让我在Activiy异常销毁时,保存状态;以及在Activity重建时,恢复状态。

    比如:当我们在输入框中输入了内容,此时因为种种原因,将App退至了后台。这个处于后台的App很有可能因为内存不足、其他配置,被系统杀死。

    当我们恢复这个页的时候,希望它能够保存住我们原来输入的内容。

    除了,我们自己手动保存,也可以利用系统的onSaveInstanceStateonRestoreInstanceState

    那么,在Android已有的系统中,是如何做的呢?

    我们查阅EditText,发现它的父类TextView做了保存状态与恢复状态的处理,但是根据条件(freezesText || hasSelection)做了保存与恢复,如果只用TextView用于展示,并不会触发保存与恢复。

    TextView的状态保存与恢复。

    // 保存状态
    @Override  
    public Parcelable onSaveInstanceState() {  
    	Parcelable superState = super.onSaveInstanceState();  
      
    	// Save state if we are forced to  
    	final boolean freezesText = getFreezesText();  
    	boolean hasSelection = false;  
    	int start = -1;  
    	int end = -1;  
      
    	if (mText != null) {  
    		start = getSelectionStart();  
    		end = getSelectionEnd();  
    		if (start >= 0 || end >= 0) {  
    			// Or save state if there is a selection  
    			hasSelection = true;  
    		}  
    	}  
    	// 满足此条件时,才进行保存数据
    	if (freezesText || hasSelection) {  
    		SavedState ss = new SavedState(superState);  
      
    		if (freezesText) {  
    			if (mText instanceof Spanned) {  
    				final Spannable sp = new SpannableStringBuilder(mText);  
    		  
    				if (mEditor != null) {  
    					removeMisspelledSpans(sp);  
    					sp.removeSpan(mEditor.mSuggestionRangeSpan);  
    				}  
    				ss.text = sp;  
    			} else {  
    				ss.text = mText.toString();  
    			}  
    		}  
      
    		if (hasSelection) {  
    			// XXX Should also save the current scroll position!  
    			ss.selStart = start;  
    			ss.selEnd = end;  
    		}  
    	  
    		if (isFocused() && start >= 0 && end >= 0) {  
    			ss.frozenWithFocus = true;  
    		}  
    	  
    		ss.error = getError();  
    	  
    		if (mEditor != null) {  
    			ss.editorState = mEditor.saveInstanceState();  
    		}  
    		return ss;  
    	}  
      
    	return superState;  
    }
    
    // 恢复状态
    @Override  
    public void onRestoreInstanceState(Parcelable state) {  
    	if (!(state instanceof SavedState)) {  
    		super.onRestoreInstanceState(state);  
    		return;  
    	}  
      
    	SavedState ss = (SavedState) state;  
    	super.onRestoreInstanceState(ss.getSuperState());  
      
    	// XXX restore buffer type too, as well as lots of other stuff  
    	if (ss.text != null) {  
    		setText(ss.text);  
    	}  
      
    	if (ss.selStart >= 0 && ss.selEnd >= 0) {  
    		if (mSpannable != null) {  
    			int len = mText.length();  
      
    			if (ss.selStart > len || ss.selEnd > len) {  
    				String restored = "";  
      
    				if (ss.text != null) {  
    					restored = "(restored) ";  
    				}  
      
    				Log.e(LOG_TAG, "Saved cursor position " + ss.selStart + "/" + ss.selEnd  
    				+ " out of range for " + restored + "text " + mText);  
    			} else {  
    				Selection.setSelection(mSpannable, ss.selStart, ss.selEnd);  
    				if (ss.frozenWithFocus) {  
    					createEditorIfNeeded();  
    					mEditor.mFrozenWithFocus = true;  
    				}  
    			}  
    		}  
    	}  
      
    	if (ss.error != null) {  
    		final CharSequence error = ss.error;  
    		// Display the error later, after the first layout pass  
    		post(new Runnable() {  
    				public void run() {  
    					if (mEditor == null || !mEditor.mErrorWasChanged) {  
    						setError(error);  
    					}  
    				}  
    			});  
    	}  
      
    	if (ss.editorState != null) {  
    		createEditorIfNeeded();  
    		mEditor.restoreInstanceState(ss.editorState);  
    	}  
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115

    onSaveInstanceState&onRestoreInstanceState的执行时机

    这两个函数在什么情况下使用?比如开发者模式中开启了不保留活动、屏幕方向发生改变等原因,导致Activity(视图)被销毁或重建时,会执行。

    被销毁时,执行onSaveInstanceState
    重建时,执行onRestoreInstanceState

    当然,这两个函数的执行也是有一些条件的,比如,View必须指定了Id,Id在整个视图(PhoneWindow)内必须唯一,如果不唯一则会在恢复状态时报错(保存时不会报错)。

    在View的默认实现中,如果发现id一样,则会在恢复状态时报错。

    @CallSuper  
    protected void onRestoreInstanceState(Parcelable state) {  
    	mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;  
    	if (state != null && !(state instanceof AbsSavedState)) {  
    		throw new IllegalArgumentException("Wrong state class, expecting View State but "  
    		+ "received " + state.getClass().toString() + " instead. This usually happens "  
    		+ "when two views of different type have the same id in the same hierarchy. "  
    		+ "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure "  
    		+ "other views do not use the same id.");  
    }
    // …… 省略剩余代码
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我们在自定义View和使用第三方控件等情况下,需要合理处理这两个函数,否则会导致崩溃。

    这里附一张InstanceState的执行时机图:

    在这里插入图片描述

    save的状态保存在哪里?restore的数据怎么取?

    在View中,执行了onSaveInstanceState()后,View会将获取到的结果,保存在一个SparseArray中,这个SparseArray是从最根部的PhoneWindow中传递进来的,整个PhoneWindow中只有一份。

    view中执行,container.put(mID, state);就会把自己要保存的数据放置到SparseArray中。

    如果视图中存在id相同的View,那么后面保存的替换掉之前保存的。

    在恢复数据时,也是从SparseArray中以当前View的Id为可以,获取保存的数据。获取到就是上一次保存时最后存储的数据。

    Parcelable state = container.get(mID);  
    if (state != null) {  
    	// ……
    	onRestoreInstanceState(state);
    	// ……
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    【第1期赠书活动】〖Python 数据库开发实战 - Python与MySQL交互篇④〗- 数据库连接池技术
    JavaScript基础
    3D生成式AI模型与工具
    ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
    自定义类型:结构体、枚举、联合
    树和二叉树练习题
    http1.0到http3.0的介绍以及新特性
    戏说领域驱动设计(十四)——补遗
    浅析Java中的LinkedList和ArrayList特点和底层
    展开运算符 ...
  • 原文地址:https://blog.csdn.net/yztbydh/article/details/138152172