app 被系統意外殺死(包括橫豎屏切換) Activity/Fragment中的字段通過自定義註解+反射實現自動恢復
源碼
當App意外被殺死,如長時間滯留後臺,橫豎屏切換,這時再進入app,並不是正常啓動app(不會走入口流程)。
這時候Activity/Fragment 中字段就需要臨時保存和恢復。
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
//通過Bundle 保存臨時數據
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if(savedInstanceState != null) {
//Bundle 重新恢復數據
}
}
上面代碼比較簡單,唯一的問題就是需要自己反覆的保存和讀取,重複代碼比較多。
下面通過自定義註解和反射的方式實現字段自動恢復(可以通過開發者選項,打開不保留後臺選項,模擬後臺殺死app,或者橫豎屏切換)
創建一個基類 BaseActivity 或者 BaseFragment
abstract class BaseActivity : Fragment(), IDisplay {
//通過 ObjectInstanceManager實現保存和恢復字段,包括恢復字段類型爲fragment
private var mOIM: ObjectInstanceManager = ObjectInstanceManager()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//....
if(savedInstanceState != null) {
//重新恢復fragment
mOIM.againFragmentInstance(supportFragmentManager, this)
//重新恢復字段
mOIM.againFieldInstance(savedInstanceState, this)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
//保存字段
mOIM.saveField(outState, this)
}
}
使用
class MainActivity : BaseAct() {
//添加AgainFragmentInstance註解,可以重新實例化fragment
@AgainFragmentInstance
private var mainFragment: MainFragment? = null
//未指定key, 字段名作爲key
@AgainInstance
private val age = 10
/**
* 可以手動指定key
*/
@AgainInstance(key = "price")
private val mPrice = 2000
//第一次正常初始化字段
override fun onInitData() {
//fragment 只要初始化一次
mainFragment = MainFragment.newInstance("this is main_fragment")
supportFragmentManager.beginTransaction().replace(R.id.contentPanel, mainFragment!!)
.commitAllowingStateLoss()
}
override fun onResume() {
super.onResume()
Timber.d("${mainFragment == null}")
Timber.d("age $age")
Timber.d("user $mUser")
Timber.d("price $mPrice")
Timber.d("userList $mUserLis")
Timber.d("userMap $mUserMap")
Timber.d("map $mMap")
}
}
實現邏輯
主要通過自定義註解來標識需要重新實例化的字段,再通過反射把字段存儲到Bundle,和從Bundle重新獲取重新賦值。
- @AgainInstance 標識字段
@Keep
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AgainInstance {
/**
* 通過onSaveInstanceState保存
* 不指定key的情況使用字段名作爲key
* @return key作爲 Bundle的key
*/
String key() default "";
}
- 找到有@AgainInstance標識的字段,判斷類型根據不同類型,存放Bundle中
public class ObjectInstanceManager {
/**
* 保存 @AgainInstance註解綁定的field
*
* @param outState 通過Bundle
* @param activityOrFragment 只能是activity或者fragment
*/
public void saveField(Bundle outState, Object activityOrFragment) {
//獲取對象中的所有字段
Field[] declaredFields = activityOrFragment.getClass().getDeclaredFields();
if (declaredFields.length == 0) return;
for (Field field : declaredFields) {
//標記 AgainInstance註解的才處理
if (field.isAnnotationPresent(AgainInstance.class)) {
field.setAccessible(true);
AgainInstance annotation = field.getAnnotation(AgainInstance.class);
String key = annotation.key();
//如果沒有自定義key,那麼直接使用屬性名
if(TextUtils.isEmpty(key)) {
key = field.getName();
}
try {
//如果字段 值爲null,不處理
if (field.get(activityOrFragment) == null) continue;
//以下根據字段的不同類型,保存到Bundle中
if (field.getType() == short.class) {
outState.putShort(key, field.getShort(activityOrFragment));
} else if (field.getType() == int.class) {
outState.putInt(key, field.getInt(activityOrFragment));
} else if (field.getType() == long.class) {
outState.putLong(key, field.getLong(activityOrFragment));
} else if (field.getType() == float.class) {
outState.putFloat(key, field.getFloat(activityOrFragment));
} else if (field.getType() == double.class) {
outState.putDouble(key, field.getDouble(activityOrFragment));
} else if (field.getType() == String.class) {
outState.putString(key, (String) field.get(activityOrFragment));
} else if (!isParcelableArray(field.getType()) && isSerializable(field.getType())) {
outState.putSerializable(key, (Serializable) field.get(activityOrFragment));
} else if (isParcelable(field.getType())) {
outState.putParcelable(key, (Parcelable) field.get(activityOrFragment));
} else if (field.getType() == short[].class || field.getType() == Short[].class) {
outState.putShortArray(key, (short[]) field.get(activityOrFragment));
} else if (field.getType() == int[].class || field.getType() == Integer[].class) {
outState.putIntArray(key, (int[]) field.get(activityOrFragment));
} else if (field.getType() == long[].class || field.getType() == Long[].class) {
outState.putLongArray(key, (long[]) field.get(activityOrFragment));
} else if (field.getType() == float[].class || field.getType() == Float[].class) {
outState.putFloatArray(key, (float[]) field.get(activityOrFragment));
} else if (field.getType() == double[].class || field.getType() == Double[].class) {
outState.putDoubleArray(key, (double[]) field.get(activityOrFragment));
} else if (isParcelableArray(field.getType())) { //不支持
// Parcelable[] parcelables = (Parcelable[]) field.get(activityOrFragment);
// ArrayList<Parcelable> parcelables1 = (ArrayList<Parcelable>) Arrays.asList(parcelables);
// outState.putParcelableArrayList(key, parcelables1);
throw new RuntimeException(activityOrFragment.getClass().getName() + "--> 屬性字段:" + field.getName() + " 不支持Parcelable[]類型 ");
} else if (field.getType() == List.class || field.getType() == ArrayList.class) {
saveListType(outState, activityOrFragment, field, key);
} else {
throw new RuntimeException(activityOrFragment.getClass().getName() + ": 該類型不支持" +
field.getType().getName() +
", 支持的類型 包括基本類型,基本類型包裝類,實現了Serializable,Parcelable接口對象,數組,一級List集合");
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
- 重新還原字段值
public class ObjectInstanceManager {
/**
* 重新實例化 帶有@AgainInstance註解 的field
*
* @param savedInstanceState
* @param activityOrFragment
*/
public void againFieldInstance(@NonNull Bundle savedInstanceState, @NonNull Object activityOrFragment) {
Field[] declaredFields = activityOrFragment.getClass().getDeclaredFields();
if (declaredFields.length == 0) return;
for (Field field : declaredFields) {
try {
if (field.isAnnotationPresent(AgainInstance.class)) {
field.setAccessible(true);
AgainInstance annotation = field.getAnnotation(AgainInstance.class);
String key = annotation.key();
//如果沒有指定key,就直接使用屬性名稱
if(TextUtils.isEmpty(key)) {
key = field.getName();
}
Object val = null;
if (field.getType() == short.class) {
val = savedInstanceState.getShort(key);
} else if (field.getType() == int.class) {
val = savedInstanceState.getInt(key);
} else if (field.getType() == long.class) {
val = savedInstanceState.getLong(key);
} else if (field.getType() == float.class) {
val = savedInstanceState.getFloat(key);
} else if (field.getType() == double.class) {
val = savedInstanceState.getDouble(key);
} else if (field.getType() == String.class) {
val = savedInstanceState.getString(key);
} else if (!isParcelableArray(field.getType()) && isSerializable(field.getType())) {
val = savedInstanceState.getSerializable(key);
} else if (isParcelable(field.getType())) {
val = savedInstanceState.getParcelable(key);
} else if (field.getType() == short[].class || field.getType() == Short[].class) {
val = savedInstanceState.getShortArray(key);
} else if (field.getType() == int[].class || field.getType() == Integer[].class) {
val = savedInstanceState.getIntArray(key);
} else if (field.getType() == long[].class || field.getType() == Long[].class) {
val = savedInstanceState.getLongArray(key);
} else if (field.getType() == float[].class || field.getType() == Float[].class) {
val = savedInstanceState.getFloatArray(key);
} else if (field.getType() == double[].class || field.getType() == Double[].class) {
val = savedInstanceState.getDoubleArray(key);
} else if (isParcelableArray(field.getType())) { //不支持Parcelable[]
// ArrayList<Parcelable> parcelableArrayList = savedInstanceState.getParcelableArrayList(key);
// Parcelable[] parcelables = parcelableArrayList.toArray(new Parcelable[]{});
// val = parcelables;
} else if (field.getType() == List.class || field.getType() == ArrayList.class) {
val = againListType(savedInstanceState, field, key);
} else {
throw new RuntimeException(activityOrFragment.getClass().getName() + ": 該類型不支持" +
field.getType().getName() +
", 支持的類型 包括基本類型,基本類型包裝類,實現了Serializable,Parcelable接口對象,數組,一級List集合");
}
field.set(activityOrFragment, val);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
- 字段類型爲 Fragment 類型 重新還原
public class ObjectInstanceManager {
/**
* 當app意外被殺死,重啓後,重新實例化fragment
*
* @param fragmentManager activity 的 SupportFragmentManager, fragment 的 childFragmentManager
* @param activityOrFragment 必須是fragment或者activity
*/
public void againFragmentInstance(FragmentManager fragmentManager, Object activityOrFragment) {
if (fragmentManager.getFragments().size() <= 0) return;
//找出fragment 列表
List<Fragment> fragments = fragmentManager.getFragments();
//獲取字段列表
Field[] declaredFields = activityOrFragment.getClass().getDeclaredFields();
if (declaredFields.length == 0) return;
for (Field field : declaredFields) {
if (field.isAnnotationPresent(AgainFragmentInstance.class)) {
AgainFragmentInstance annotation = field.getAnnotation(AgainFragmentInstance.class);
for (Fragment fragment : fragments) {
//註解中不帶key,並且key不等於當前的tag 跳過
if (!TextUtils.isEmpty(annotation.tag())
&& !annotation.tag().equals(fragment.getTag())) {
continue;
}
//兩種情況, 註解沒有設置tag,那麼直接判斷字段類型的全類名和fragment列表中的fragment的全類名比較是否同一個。
//註解設置了tag,並且fragment的tag === 自定義註解的tag,那麼再做上面的全類名比較
if (fragment.getClass().getName().equals(field.getType().getName())) {
field.setAccessible(true);
try {
field.set(activityOrFragment, fragment);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
}