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/…

相关文章
相关标签/搜索