Android系統(tǒng)在5.0時(shí),對進(jìn)程內(nèi)的內(nèi)存管理做了一個(gè)優(yōu)化,但并沒有明確的文檔說明這個(gè)優(yōu)化。
這個(gè)優(yōu)化為解決Android應(yīng)用的內(nèi)存問題,提供了一個(gè)新的思路。但如果開發(fā)者習(xí)慣于單Task的應(yīng)用開發(fā),或者從來不考慮SaveState,那開發(fā)者可能根本無法體會這個(gè)新機(jī)制的好處。
本文首先從SaveState講起,對于了解SaveState的同學(xué),可以直接跳過
什么是SaveState
要了解什么是SaveState必須要先知道Activity
的兩個(gè)關(guān)鍵方法
onSaveInstanceState
onRestoreInstanceState
onSaveInstanceState時(shí)系統(tǒng)做了些什么
在Activity
被回收之前,系統(tǒng)會調(diào)用onSaveInstanceState(Bundle outState)
來保存View
的狀態(tài),并到傳入的outState
對象中。
- 保存Window
- 保存Fragment
- 調(diào)用外部注冊的回調(diào)方法
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
onRestoreInstanceState時(shí)系統(tǒng)做了些什么
在Activity
被重新創(chuàng)建時(shí),會通過onCreate(Bundle savedInstanceState)
和onRestoreInstanceState(Bundle savedInstanceState)
傳入保存的狀態(tài)信息并恢復(fù)View
的狀態(tài)。
- onCreate重建Fragment
- onRestoreInstanceState恢復(fù)Window狀態(tài)
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
if (mLastNonConfigurationInstances != null) {
mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
}
if (mActivityInfo.parentActivityName != null) {
if (mActionBar == null) {
mEnableDefaultActionBarUp = true;
} else {
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
}
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);
if (mVoiceInteractor != null) {
mVoiceInteractor.attachActivity(this);
}
mCalled = true;
}
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
Window在save和restore時(shí)對View的處理
- Save時(shí),遍歷View的樹狀結(jié)構(gòu)調(diào)用
Parcelable onSaveInstanceState()
- 以View的id為key在Window的
SparseArray<Parcelable>
中保存這些Parcelable
- Restore時(shí),Window從
savedInstanceState
獲取View的savedStates
- 遍歷View的樹狀結(jié)構(gòu)調(diào)用
onRestoreInstanceState(Parcelable state)
- View根據(jù)id獲取自己的state并恢復(fù)
小結(jié)
- Save和Restore的機(jī)制主要是用于保存和恢復(fù)View的
- 沒有id的View是不會被保存狀態(tài)的
- 如果id重復(fù),則View的狀態(tài)會被覆蓋
- 被保存的Fragment會在onCreate中被自動(dòng)創(chuàng)建和添加到FragmentActivity中
- 被保存的View不會被自動(dòng)創(chuàng)建,只是通過id獲取savedInstance用于更新View
關(guān)于SaveState的詳細(xì)介紹可以參考文章Android中SaveState原理分析
為什么開始使用SaveState
為什么很多人不重視SaveState
我們先了解下會用到Restore機(jī)制的地方
-
FragmentStatePagerAdapter
用于在ViewPager中使用可回收和重建的Fragment
- 應(yīng)用Crash時(shí),當(dāng)前頁面被銷毀,前一個(gè)頁面被Restore
- 在4.0之前,系統(tǒng)不會自動(dòng)重啟應(yīng)用
- 在4.0之后,系統(tǒng)會自動(dòng)重啟,并通過Restore機(jī)制恢復(fù)Crash的頁面。
FragmentStatePagerAdapter
中考慮SaveState是必須的,所以大家都會被迫處理SaveState的問題。
大多數(shù)開發(fā)者不會考慮Crash重建的問題,所以SaveState很少被開發(fā)者重視。而認(rèn)真考慮過Crash重建的開發(fā)者一定不會對SaveState陌生。
5.0的新機(jī)制
在5.0中,SaveState有了新的作用,稍加利用,它會幫你解決OutOfMemory
。而根據(jù)Google的統(tǒng)計(jì),到今年下半年,Android5.0及以上的系統(tǒng)占比將超過50%。
要觸發(fā)這個(gè)新機(jī)制,你的應(yīng)用必須是多Task結(jié)構(gòu)的。關(guān)于Task,那又是一個(gè)很大的話題,下面我只用一個(gè)簡單的例子看看這個(gè)新機(jī)制。
演示代碼可以通過git倉庫下載
這里看看關(guān)鍵的ActivivtyOne.java
- ActivityOne是standard
- ActivityTwo是singleInstance,所以他會在單獨(dú)的新的Task中
- AcitivyOne可以啟動(dòng)ActivityTwo
- ActivityOne可以不斷消耗內(nèi)存
public class ActivityOne extends BaseActivity {
boolean forceOom = false;
List<Bitmap> memory = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_one);
}
public void launchTwo(View view) {
ActivityTwo.launch(this);
}
/**
* 第一次點(diǎn)擊使內(nèi)存接近進(jìn)程能獲取的內(nèi)存上限,再次點(diǎn)擊觸發(fā)OOM
* @param view
*/
public void consumeMem(View view) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (!forceOom && isLowMemory()) {
forceOom = true;
break;
}
memory.add(Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
}).start();
}
/**
* 判斷已使用的內(nèi)存是否接近了單進(jìn)程的內(nèi)存上限
*
* @return
*/
public boolean isLowMemory() {
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
long total = Runtime.getRuntime().totalMemory() / (1024l * 1024l);
int max = activityManager.getMemoryClass();
Log.w(getClass().getSimpleName(), total + "/" + max);
if (total > activityManager.getMemoryClass() * 0.85) return true;
return false;
}
public static void launch(Activity activity) {
activity.startActivity(new Intent(activity, ActivityOne.class));
}
}
操作步驟:
- ActivityOne啟動(dòng)ActivityTow
- ActivityTwo啟動(dòng)ActivityOne,從而切換到老的Task中,ActivityTwo不會被銷毀
- ActivityOne不斷消耗內(nèi)存,直到接近進(jìn)程使用內(nèi)存的上限(Android系統(tǒng)對每個(gè)進(jìn)程使用的最大內(nèi)存有一個(gè)限制)
這時(shí)通過logcat你會看到5.0的不同:
- 5.0之前:不會有什么事情發(fā)生。再次點(diǎn)擊消耗內(nèi)存,會OOM,整個(gè)進(jìn)程被殺。
- 5.0及之后:
ActivityTwo#OnDestroy
會被調(diào)用,這時(shí)再啟動(dòng)ActivityTwo,可以看到ActivityTwo#onRestoreInstanceState
的調(diào)用。
W/ActivityOne: 83571877-onCreate: TaskId-7551
W/ActivityTwo: 128141518-onCreate: TaskId-7552
W/ActivityOne: 83571877-onSaveInstanceState
W/ActivityOne: 219591424-onCreate: TaskId-7551
W/ActivityTwo: 128141518-onSaveInstanceState
W/ActivityOne: 23/192
W/ActivityOne: 42/192
W/ActivityOne: 88/192
W/ActivityOne: 111/192
W/ActivityOne: 157/192
以下是5.0系統(tǒng)上才會出現(xiàn)的
W/ActivityTwo: 128141518-onDestroy
W/ActivityOne: 176/192
W/ActivityTwo: 80252517-onCreate: TaskId-7552
W/ActivityTwo: 80252517-onRestoreInstanceState
W/ActivityTwo: 80252517-onNewIntent
W/ActivityOne: 219591424-onSaveInstanceState
因此我們可以得出結(jié)論:
5.0之后,Android進(jìn)程在遇到內(nèi)存瓶頸時(shí),會通過主動(dòng)銷毀進(jìn)程中的Acitivty來釋放內(nèi)存。這些被銷毀的Activity都屬于后臺Task,當(dāng)被銷毀的Activity需要重新出現(xiàn)時(shí),會觸發(fā)Restore機(jī)制
當(dāng)然,這個(gè)結(jié)論又會引起很多疑問。
- 為什么不銷毀當(dāng)前Task中的后臺Activity?
- 如果后臺Task中有多個(gè)Activity是一起銷毀嗎?如果后臺Task中的多個(gè)Activity是屬于不同的進(jìn)程呢?
- ......
關(guān)于這些問題,需要分析源碼才能找到答案,但我不希望這篇文章變成對Android源碼的一次分析。我將在下篇文章,繼續(xù)介紹怎么處理SaveState。
當(dāng)然,即使沒有SaveState在5.0上帶來的好處,正確處理頁面的SaveState也是保證Android應(yīng)用程序健壯性的一個(gè)重要部分。
怎么處理SaveState,請看下篇文章
- 原文鏈接
- 我的主頁:www.huangyifei.info
- 微信訂閱號:tech_galaxy