Android 一個輕量級的自動恢復內存數據框架

license
JCenter

版本更新說明:

1.0.0 完成基本功能;
1.0.1 全局變量的做用域從以前強制public改爲只要非private便可;
1.0.2 修改 SaveHelper.bind(this, savedInstanceState)方法爲SaveHelper.recover(this, savedInstanceState),只是重命名,
      以便於理解;
      去掉當內存被回收去調用recover方法時,卻沒有對應helper類會主動拋異常的狀況,方便在BaseAcitviy 和 BaseFragment的
      onSaveInstanceState 和 onRestoreInstanceState 統一添加SaveHelper.save和SaveHelper.recover方法。
1.0.3 優化代碼生成,若是一個activity或者fragment中沒有有效的@NeedSave註解,可是添加了SaveHelper.recover和SaveHelper.save
      方法,如今就不會自動生成這個類的SaveStateHelper類,減小了無用SaveStateHelper類,便於在Base類中統一集成。
      
2.0.0 去掉NeedSave註解中的isParcelable字段,自動能夠支持不一樣類型;
      若是字段被標記爲private在編譯的時候會拋異常;
      支持基本全部bundle能夠傳入的類型,包括SparseParcelableArray等, 若是傳入的類型bundle不支持會拋異常(若是有遺漏的類型,請在github 提出issue);
2.0.2 修復經過繼承去實現Serializable的對象不能識別的bug;
2.0.3 優化異常提示
2.0.4 修復枚舉類型保存的時候不能識別的問題

2.1.0 增長對PersistableBundle的支持,NeedSave註解中設置isPersistable = true則說明該參數保存到PersistableBundle
複製代碼

引入方式,在app的gradle中加入下面依賴便可:java

implementation 'com.noober:savehelper:2.1.0'
implementation 'com.noober:savehelper-api:2.1.0'
annotationProcessor 'com.noober:processor:2.1.0'
複製代碼

kotlin的依賴方式android

apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'

implementation 'com.noober:savehelper:2.1.0'
kapt 'com.noober:processor:2.1.0'
implementation 'com.noober:savehelper-api:2.1.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
複製代碼

混淆配置:git

-dontwarn  com.noober.**
 -keep class com.noober.api.**{*;}
 -keep class com.noober.savehelper.**{*;}
 -keep class * implements com.noober.savehelper.ISaveInstanceStateHelper {*;}
複製代碼

引入

android 內存被回收是一個開發者的常見問題。當咱們跳轉到一個二級界面,或者切換到後臺的時候,若是時間過長或者手機的內存不足,當咱們再返回這個界面的時候,activity或fragment就會被內存回收。這時候雖然界面被從新執行了onCreate,可是不少變量的值卻已經被置空,這樣就致使了不少潛在的bug,已經不少空指針的問題。github

其實這種問題須要解決的話也很簡單。你們知道,當Activity或者Fragment被內存回收後,咱們再進入這個界面,它會自動從新進行onCreate操做,而且系統會幫助咱們保存一些值。可是系統只會保存界面上的一些元素,好比textview中的文字,可是不少全局變量仍然會被置空。 對於保存這些變量,咱們能夠重寫onSaveInstanceState這個方法,在onCreate中便可恢復數據。代碼以下: |api

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的問題。app

問題到這裏,彷佛已經能夠解決內存被回收的問題了。可是隨着項目的開發,一個Activity中的變量以及代碼會變得很是多,這時候咱們須要去保存某個值就會使代碼變得愈來愈凌亂,同時不斷重複的去寫outState.putXX已經savedInstanceState.getXX這樣的代碼都是很重複的,一不當心還會去寫錯中間的key值。框架

因而我寫了這個很輕量級的框架,來解決這個問題。先給出引入這個框架後的代碼寫法:maven

@NeedSave
String test;
@NeedSave
protected boolean b;
@NeedSave
public Boolean c;
@NeedSave
public ArrayList<String> t;
@NeedSave
public Integer i;
@NeedSave
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.recover(this,savedInstanceState);
}

private void initData() {
	//TODO
}

@Override
protected void onSaveInstanceState(Bundle outState) {
	SaveHelper.save(this,outState);
	super.onSaveInstanceState(outState);
}
複製代碼

這裏我特意寫了不少的變量,可是不管這個Activity中有多少變量,我在onCreate和onSaveInstanceState代碼中都只要去各寫一行代碼,同時給變量加一個標籤標記一個便可:ide

@NeedSave
    SaveHelper.recover(this,savedInstanceState);
    SaveHelper.save(this,outState);
複製代碼

這樣就不會由於這種太多的重複的操做去致使代碼邏輯的混亂,同時也避免了敲代碼時由於key寫錯致使的錯誤。svg

效果展現

咱們來看一下測試代碼:

不進行數據保存操做

很簡單,就是經過點擊事情,去給變量「testString」賦值,而後再去模擬內存被回收的狀況,看一下顯示的值是不是內存被回收前的。

調用框架代碼後的內存恢復

加入框架代碼:

加入代碼以後的效果:

原理介紹

@NeedSave

這是一個註解,這個註解只能使用在全局變量中,特別注意,被加上這個註解的變量必須是public,不然會不生效。 1.0.1更新爲只要非private便可。

當前支持保存的類型有:

String
    boolean Boolean
    ArrayList
    int int[] Integer
    Parcelable
    Serializable
    float Float
    char[] char
    Bundle
複製代碼

注意,若是是Parcelable類型,須要特別在註解中加入 @NeedSave(isParcelable = true) 這樣標記 目前已經自動支持全部的類型,isParcelable已經棄用。

SaveHelper.recover(this,savedInstanceState);

這個方法實際上是恢復數據的時候去調用的。

public static <T> void recover(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");
    }
  }
}
複製代碼

kotlin使用方法

若是要在kotlin使用,與在java中使用相同,直接加註解便可,可是不一樣之出在於:

1:若是是基本數據類型,須要多添加一個註解@JvmField

2:若是是其餘數據類型,須要增長lateinit關鍵字或者添加一個註解@JvmField 不然會報錯"the modifier of the field must not be private, otherwise it won't work"。

示例:

class KotlinActivity : AppCompatActivity() {

      @NeedSave
      @JvmField
      var a :Int=3

      @NeedSave
      lateinit var bundle: Bundle

      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
          setContentView(R.layout.activity_kotlin)
          SaveHelper.recover(this, savedInstanceState)
          Log.e("KotlinActivity",  a.toString())

      }


      override fun onSaveInstanceState(outState: Bundle?) {
          Log.e("KotlinActivity",  "onSaveInstanceState")
          a = 2
          SaveHelper.save(this,  outState)
          super.onSaveInstanceState(outState)
      }
  }
複製代碼

總結

看到這裏你們已經猜到其實這個框架的實現原理和ButterKnife是相同的。而bufferknife的原理不少文章都有,這裏就不過多介紹了。

更新

2.0.0

支持Bundle全部支持的的類型

2.1.0

增長對PersistableBundle持久化數據的保存,用於手機關機重啓後的數據恢復,使用方法以下:

@NeedSave(isPersistable = true)
PersistableBundle persistableBundle;  

@NeedSave(isPersistable = true)
int i;

@Override
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
    super.onCreate(savedInstanceState, persistentState);
    SaveHelper.recover(this, savedInstanceState, persistentState);
}

@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
    SaveHelper.save(this, outState, outPersistentState);
    super.onSaveInstanceState(outState, outPersistentState);
}
複製代碼

github地址:github.com/JavaNoober/…

相關文章
相關標籤/搜索