Android 一個輕量級的自動恢復記憶體資料框架
引入
android 記憶體被回收是一個開發者的常見問題。當我們跳轉到一個二級介面,或者切換到後臺的時候,如果時間過長或者手機的記憶體不足,當我們再返回這個介面的時候,activity或fragment就會被記憶體回收。這時候雖然介面被重新執行了onCreate,但是很多變數的值卻已經被置空,這樣就導致了很多潛在的bug,已經很多空指標的問題。
其實這種問題需要解決的話也很簡單。大家知道,當Activity或者Fragment被記憶體回收後,我們再進入這個介面,它會自動重新進行onCreate操作,並且系統會幫助我們儲存一些值。但是系統只會儲存介面上的一些元素,比如textview中的文字,但是很多全域性變數仍然會被置空。
對於儲存這些變數,我們可以重寫onSaveInstanceState
|
public int a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); //記憶體回收,介面重新onCreate後,恢復資料 if(savedInstanceState != null){ a = savedInstanceState.getInt("A"); } } private void initData() { ... } @Override protected void onSaveInstanceState(Bundle outState) { //儲存資料 outState.putInt("A", a); super.onSaveInstanceState(outState); }
通過這樣的操作,便可以解決記憶體回收後變數a的值變為初始值0的問題。
問題到這裡,似乎已經可以解決記憶體被回收的問題了。但是隨著專案的開發,一個Activity中的變數以及程式碼會變得非常多,這時候我們需要去儲存某個值就會使程式碼變得越來越凌亂,同時不斷重複的去寫outState.putXX已經savedInstanceState.getXX這樣的程式碼都是很重複的,一不小心還會去寫錯中間的key值。
於是我寫了這個很輕量級的框架,來解決這個問題。先給出引入這個框架後的程式碼寫法:
@NeedSave public String test; @NeedSave private boolean b; @NeedSave public Boolean c; @NeedSave public ArrayList<String> t; @NeedSave public Integer i; @NeedSave(isParcelable = true) public ParcelableObject example; @NeedSave public SerializableObject example; @NeedSave public Float f1; @NeedSave public float f2; @NeedSave public char achar; @NeedSave public char achars[]; @NeedSave public int sssss[]; @NeedSave public Bundle bundle; @NeedSave public int a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); SaveHelper.bind(this,savedInstanceState); } private void initData() { ... } @Override protected void onSaveInstanceState(Bundle outState) { SaveHelper.save(this,outState); super.onSaveInstanceState(outState); }
這裡我特地寫了很多的變數,但是無論這個Activity中有多少變數,我在onCreate和onSaveInstanceState程式碼中都只要去各寫一行程式碼,同時給變數加一個標籤標記一個即可:
@NeedSave
SaveHelper.bind(this,savedInstanceState);
SaveHelper.save(this,outState);
這樣就不會因為這種太多的重複的操作去導致程式碼邏輯的混亂,同時也避免了敲程式碼時因為key寫錯導致的錯誤。
效果展示
我們來看一下測試程式碼:
不進行資料儲存操作
很簡單,就是通過點選事情,去給變數“testString”賦值,然後再去模擬記憶體被回收的情況,看一下顯示的值是否是記憶體被回收前的。
呼叫框架程式碼後的記憶體恢復
加入框架程式碼:
加入程式碼之後的效果:
原理介紹
@NeedSave
這是一個註解,這個註解只能使用在全域性變數中,特別注意,被加上這個註解的變數必須是public,否則會不生效。
當前支援儲存的型別有:
String
boolean Boolean
ArrayList
int int[] Integer
Parcelable
Serializable
float Float
char[] char
Bundle
注意,如果是Parcelable型別,需要特別在註解中加入 @NeedSave(isParcelable = true) 這樣標記
SaveHelper.bind(this,savedInstanceState);
這個方法其實是恢復資料的時候去呼叫的。
public static <T> void bind(T recover, Bundle savedInstanceState){
if(savedInstanceState != null){
ISaveInstanceStateHelper<T> saveInstanceStateHelper = findSaveHelper(recover);
if(saveInstanceStateHelper != null){
saveInstanceStateHelper.recover(savedInstanceState, recover);
}
}
}
savedInstanceState不會null的時候,說明就是需要記憶體恢復的時候,這時候就會去通過findSaveHelper方法找到一個實現類,然後去呼叫recover方法恢復資料。
SaveHelper.save(this,outState);
這是一個儲存資料的方法,注意的是,這個方法必須在super.onSaveInstanceState(outState);之前呼叫。
public static <T> void save(T save, Bundle outState){
ISaveInstanceStateHelper<T> saveInstanceStateHelper = findSaveHelper(save);
if(saveInstanceStateHelper != null){
saveInstanceStateHelper.save(outState, save);
}
}
它最終呼叫的是ISaveInstanceStateHelper實現類的save方法。
ISaveInstanceStateHelper實現類
這個類是一個介面,專門用來儲存和恢復資料用。這個類是不要我們自己寫的,在程式碼編譯的時候會自動生成模板程式碼。整個呼叫過程中也只有尋找ISaveInstanceStateHelper實現類的findSaveHelper這個方法呼叫了反射,其他時候不會去用到反射,而影響效率。
自動生成程式碼所在位置:
自動生成的程式碼如下:
public class MainActivity_SaveStateHelper implements ISaveInstanceStateHelper<MainActivity> {
@Override
public void save(Bundle outState, MainActivity save) {
outState.putString("TEST",save.test);
outState.putBoolean("C",save.c);
outState.putSerializable("T",save.t);
outState.putInt("I",save.i);
outState.putParcelable("EXAMPLE",save.example);
outState.putFloat("F1",save.f1);
outState.putFloat("F2",save.f2);
outState.putChar("ACHAR",save.achar);
outState.putCharArray("ACHARS",save.achars);
outState.putIntArray("SSSSS",save.sssss);
outState.putIntArray("SASA",save.sasa);
outState.putBundle("BUNDLE",save.bundle);
outState.putInt("A",save.a);
}
@Override
public void recover(Bundle savedInstanceState, MainActivity recover) {
if(savedInstanceState != null) {
recover.test = savedInstanceState.getString("TEST");
recover.c = savedInstanceState.getBoolean("C");
recover.t = (ArrayList<String>)savedInstanceState.getSerializable("T");
recover.i = savedInstanceState.getInt("I");
recover.example = savedInstanceState.getParcelable("EXAMPLE");
recover.f1 = savedInstanceState.getFloat("F1");
recover.f2 = savedInstanceState.getFloat("F2");
recover.achar = savedInstanceState.getChar("ACHAR");
recover.achars = savedInstanceState.getCharArray("ACHARS");
recover.sssss = savedInstanceState.getIntArray("SSSSS");
recover.sasa = savedInstanceState.getIntArray("SASA");
recover.bundle = savedInstanceState.getBundle("BUNDLE");
recover.a = savedInstanceState.getInt("A");
}
}
}
總結
看到這裡大家已經猜到其實這個框架的實現原理和BufferKnife是相同的。而bufferknife的原理很多文章都有,這裡就不過多介紹了。
github地址:https://github.com/JavaNoober/AutoSave
引入方式,在app的gradle中加入下面依賴即可:
compile 'com.noober:savehelper:1.0.0'
compile 'com.noober:savehelper-api:1.0.0'
annotationProcessor 'com.noober:processor:1.0.0'