问题原因的分析这段文字没有经过润色,纯粹是初稿,不一定易于阅读,这段看不下去的同学可以直接看文章最后的解决方案。
ReportFragment
是android.arch.lifecycle
组件中的类,lifecycle
组件中的ProcessLifecycleOwnerInitializer
继承自ContentProvider
,利用ContentProvider#onCreate()
方法初始化。
在初始化时,通过Application#registerActivityLifecycleCallbacks()
注册Activity
的生命周期监听,在onActivityCreated()
回调中为每一个Activity
注入一个ReportFragment
public static void injectIfNeededIn(Activity activity) {
// ProcessLifecycleOwner should always correctly work and some activities may not extend
// FragmentActivity from support lib, so we use framework fragments for activities
android.app.FragmentManager manager = activity.getFragmentManager();
if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
// Hopefully, we are the first to make a transaction.
manager.executePendingTransactions();
}
}
注入ReportFragment
之后,ProcessLifecycleOwner
中也会监听Activity
的生命周期,并且在onActivityCreated()
回调中通过ReportFragment.get()
方法获取ReportFragment
。
static ReportFragment get(Activity activity) {
return (ReportFragment) activity.getFragmentManager().findFragmentByTag(
REPORT_FRAGMENT_TAG);
}
出错的代码就是get()
方法中的转型。
ReportFragment
为什么不能被转型成ReportFragment
呢?因为它们的ClassLoader
不同,但是从ReportFragment
的注入方法:injectIfNeededIn()
和使用方法:get()
来看,这两处代码都是ProcessLifecycleOwnerInitializer
在初始化时注册的Activity
的生命周期监听中回调的。又因为在类A中通过new
关键字创建类B,类B的ClassLoader就是类A的ClassLoader,那么这两处代码中的ReportFragment
的Classloader应该是同一个,为什么会出现ClassLoader不一致的情况呢?
这就要从FragmentActivity
的状态保存说起,来看代码,FragmentActivity#onSaveInstanceState()
中,this.mFragments.saveAllState
会保存Fragment
。
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
this.markFragmentsCreated();
Parcelable p = this.mFragments.saveAllState();
if (p != null) {
outState.putParcelable("android:support:fragments", p);
}
...
}
}
最终通过new FragmentState()
将Fragment
序列化。
Parcelable saveAllState() {
...
if (this.mActive != null && this.mActive.size() > 0) {
int N = this.mActive.size();
FragmentState[] active = new FragmentState[N];
boolean haveFragments = false;
for(int i = 0; i < N; ++i) {
Fragment f = (Fragment)this.mActive.valueAt(i);
if (f != null) {
if (f.mIndex < 0) {
this.throwException(new IllegalStateException("Failure saving state: active " + f + " has cleared index: " + f.mIndex));
}
haveFragments = true;
FragmentState fs = new FragmentState(f);
active[i] = fs;
...
而序列化时,只保存了Fragment
的类名,ClassLoader
信息就丢失了。
FragmentState(Fragment frag) {
this.mClassName = frag.getClass().getName();
this.mIndex = frag.mIndex;
this.mFromLayout = frag.mFromLayout;
this.mFragmentId = frag.mFragmentId;
this.mContainerId = frag.mContainerId;
this.mTag = frag.mTag;
this.mRetainInstance = frag.mRetainInstance;
this.mDetached = frag.mDetached;
this.mArguments = frag.mArguments;
this.mHidden = frag.mHidden;
}
当后台App由于内存不足被kill后,从最近使用的App列表中返回App,系统会恢复切后台时所处的Activity
,在Activity#onCreate()
中通过this.mFragments.restoreAllState
恢复Fragment
。
protected void onCreate(@Nullable Bundle savedInstanceState) {
this.mFragments.attachHost((Fragment)null);
super.onCreate(savedInstanceState);
FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
if (nc != null && nc.viewModelStore != null && this.mViewModelStore == null) {
this.mViewModelStore = nc.viewModelStore;
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable("android:support:fragments");
this.mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
if (savedInstanceState.containsKey("android:support:next_request_index")) {
...
之前序列化了Fragment
信息的FragmentState
类,通过FragmentState#instantiate()
方法,将Context
, ClassName
传递给Fragment#instantiate()
方法,最终通过Context
的ClassLoader
加载Fragment
的Class
。
public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
try {
Class> clazz = sClassMap.get(fname);
if (clazz == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = context.getClassLoader().loadClass(fname);
if (!Fragment.class.isAssignableFrom(clazz)) {
throw new InstantiationException("Trying to instantiate a class " + fname
+ " that is not a Fragment", new ClassCastException());
}
sClassMap.put(fname, clazz);
}
...
这个Context
是Activity#mFragments
中的HostCallbacks
通过onGetHost
获取的,就是Activity
自身。
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
class HostCallbacks extends FragmentHostCallback {
@Override
public Activity onGetHost() {
return Activity.this;
}
}
下面了解一下Replugin的ClassLoader机制,Replugin中有宿主和插件之分,通过RePluginClassLoader
加载宿主的类,通过PluginDexClassLoader
加载插件的类,插件中的Activity
就是PluginDexClassLoader
加载的。如上所述,在内存不足的情况下,Activity
中的ReportFragment
是在状态恢复时,由PluginDexClassLoader
加载的。
再回到出问题的代码,此时findfragmentByTag
得到的ReportFragment
就是由PluginDexClassLoader
加载的,而转型后的ReportFragment
是由系统的ClassLoader加载的,ClassLoader不同,自然就不能转型。
static ReportFragment get(Activity activity) {
return (ReportFragment) activity.getFragmentManager().findFragmentByTag(
REPORT_FRAGMENT_TAG);
}
知道了问题的来龙去脉,就好找解决方案了,只要保证LifeCycle使用的ReportFragment都是由LifeCycle组件加载,就不会有转型问题,但是Activity的状态保存步骤又不能去除,综上得到的方案就是:在Activity创建了ReportFragment之后,LifeCycle插入ReportFragment之前,移除Activity创建的ReportFragment,这样LifeCycle就会自行创建ReportFragment,就能保证LifeCycle使用的ReportFragment都是自己加载的。
在所有Activity的公共父类的onCreate
中执行removeReportTag
方法移除ReportFragment。
private static void removeReportTag(Activity activity) {
try {
XLLog.d(TAG, "removeReportTag");
FragmentManager manager = activity.getFragmentManager();
if (manager != null) {
Fragment fragment = manager.findFragmentByTag(REPORT_FRAGMENT_TAG);
XLLog.d(TAG, "removeReportTag--fragment=" + fragment);
if (fragment != null) {
XLLog.d(TAG, "removeReportTag--fragment.classLoader=" + fragment.getClass().getClassLoader());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
manager.beginTransaction().remove(fragment).commitNow();
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
这种方法的缺点是,第三方的Activity不会继承我们的公共父类,无法执行这段代码,那就看方案二。
在Application#onCreate
中执行HookActivityLifecycle#hook
方法,通过反射在Activity生命周期的监听器list的第一位中插入一个监听器,在监听器中的onActivityCreated
回调中移除ReportFragment。这个方案的缺点是,Activity
中的FragmentManager
中的commitNow
方法在api 24及以上版本才实现,如果不用commitNow
方法,在LifeCycle判断是否插入ReportFragment时,ReportFragment还没来得及被移除。
public class HookActivityLifeCycle {
private static final String TAG = "HookActivityLifeCycle";
private static final String REPORT_FRAGMENT_TAG = "android.arch.lifecycle"
+ ".LifecycleDispatcher.report_fragment_tag";
public static void hook(Application application) {
try {
Field[] fields = Application.class.getDeclaredFields();
for (Field field : fields) {
XLLog.d(TAG, "field=" + field.getName());
}
Field mActivityLifecycleCallbacksField = Application.class.getDeclaredField("mActivityLifecycleCallbacks");
mActivityLifecycleCallbacksField.setAccessible(true);
List list = (List) mActivityLifecycleCallbacksField.get(application);
if (list != null) {
list.add(0, new hook());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
static class hook implements Application.ActivityLifecycleCallbacks {
@Override
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
removeReportTag(activity);
}
@Override
public void onActivityStarted(@NonNull Activity activity) {
}
@Override
public void onActivityResumed(@NonNull Activity activity) {
}
@Override
public void onActivityPaused(@NonNull Activity activity) {
}
@Override
public void onActivityStopped(@NonNull Activity activity) {
}
@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
}
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
}
}
private static void removeReportTag(Activity activity) {
if (activity instanceof FragmentActivity) {
removeReportTagV4((FragmentActivity) activity);
} else {
try {
XLLog.d(TAG, "removeReportTag");
FragmentManager manager = activity.getFragmentManager();
if (manager != null) {
Fragment fragment = manager.findFragmentByTag(REPORT_FRAGMENT_TAG);
XLLog.d(TAG, "removeReportTag--fragment=" + fragment);
if (fragment != null) {
XLLog.d(TAG, "removeReportTag--fragment.classLoader=" + fragment.getClass().getClassLoader());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
manager.beginTransaction().remove(fragment).commitNow();
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
/**
* FragmentActivity中的FragmentManager的commitNow方法没有版本限制。
*/
private static void removeReportTagV4(FragmentActivity activity) {
try {
XLLog.d(TAG, "removeReportTagV4");
android.support.v4.app.FragmentManager manager = activity.getSupportFragmentManager();
if (manager != null) {
android.support.v4.app.Fragment fragment = manager.findFragmentByTag(REPORT_FRAGMENT_TAG);
XLLog.d(TAG, "removeReportTagV4--fragment=" + fragment);
if (fragment != null) {
XLLog.d(TAG, "removeReportTagV4--fragment.classLoader=" + fragment.getClass().getClassLoader());
manager.beginTransaction().remove(fragment).commitNow();
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
这个问题的复现场景就是内存不足,那么如果模拟内存不足的情况呢,参考模拟内存不足