版本更新說明:
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
咱們來看一下測試代碼:
加入框架代碼:
加入代碼以後的效果:
這是一個註解,這個註解只能使用在全局變量中,特別注意,被加上這個註解的變量必須是public,不然會不生效。 1.0.1更新爲只要非private便可。
當前支持保存的類型有:
String
boolean Boolean
ArrayList
int int[] Integer
Parcelable
Serializable
float Float
char[] char
Bundle
複製代碼
注意,若是是Parcelable類型,須要特別在註解中加入 @NeedSave(isParcelable = true) 這樣標記 目前已經自動支持全部的類型,isParcelable已經棄用。
這個方法實際上是恢復數據的時候去調用的。
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方法恢復數據。
這是一個保存數據的方法,注意的是,這個方法必須在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實現類的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使用,與在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的原理不少文章都有,這裏就不過多介紹了。
支持Bundle全部支持的的類型
增長對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/…