业务场景:在插件A 里面,我们想嵌入插件B 的视图。
1,先把插件B 的 Fragment 改造成 View,这样拿到 View 可以直接放到插件A 里面。
2,宿主里面依赖 support 包,其他插件全部不依赖 support 包(provided)。这样各个插件的 Fragment 可以通过反射相互填充了。
3,在插件A 反射调用插件B 的 Fragment 生命周期。
第一种方案,是最直接的,也是问题最少的。
第二种方案,也比较简单,不过需要注意 support 包的版本要统一,还有注意主题 Theme。
第三种方案,需要适配。
很多时候,我们碰到的业务场景是把一个插件的 Activity 内嵌到另一个插件的 Tab。正常我们把 Activity 改造成 View ,这样问题最少。但是有时候我们 Activity 用的是 MVVM,使用了 ViewModel,这样改造了,肯定会报错的。因为宿主和插件的 FragmentActivity 不是一个类的。这时候要么大改代码。要么考虑2,3两种方案了。
而如果这时候,我们不想发主版本,宿主和插件 support 包版本又不一致。
如果宿主的版本是比较大的,还可以直接升级下插件的 support 包就行了。
而如果宿主的比较小,或者不全,这样可以考虑第三种方案了。
本文重点讲第三种方案:
单纯的 Fragment 代理,挺简单的。主要就是把插件 Fragment onCreateView 创建的 View 作为返回值就好了,其他生命周期再代理一下。
以下 v4 包源码,都是基于 28.0.0 版本。
1,先解决 getActivity() 为空的问题
public class Fragment {
FragmentHostCallback mHost;
public final FragmentActivity getActivity() {
// 看到 getActivity(),注意 mHost 不能为空。
return this.mHost == null ? null : (FragmentActivity)this.mHost.getActivity();
}
}
public class FragmentActivity extends SupportActivity {
// 注意到 FragmentActivity 初始化就会创建 FragmentController,里面就有一个 mHost
final FragmentController mFragments = FragmentController.createController(new FragmentActivity.HostCallbacks());
}
public class FragmentController {
private final FragmentHostCallback<?> mHost;
}
从源码,我们可以考虑把 FragmentActivity 的 mFragments.mHost 赋值给 Fragment 就行了。(最后会附带实现源码)
2,ViewModel 的生命周期怎么办?
本身要用到 ViewModel 就得适配 getActivity(),不能返回空。
// 我们正常利用 ViewModelProviders 创建 VM
mViewModel = ViewModelProviders.of(this).get(UserInfoBarViewModel.class);
public class ViewModelProviders {
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
// 注意 checkActivity checkApplication,会检查
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(fragment), (Factory)factory);
}
private static Activity checkActivity(Fragment fragment) {
// 这里会判断 getActivity() 不能为空,否则报错。
Activity activity = fragment.getActivity();
if (activity == null) {
throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
} else {
return activity;
}
}
}
VM 生命周期的实现就是注册监听,相关类主要就是 LiveData,Lifecycle,LifecycleRegistry。
// 正常我们监听某个属性变化,都是 LiveData.observe。
public abstract class LiveData<T> {
// LifecycleOwner 就是 FragmentActivity 或者 Fragment。
public void observe(LifecycleOwner owner, Observer<T> observer) {
if (owner.getLifecycle().getCurrentState() != State.DESTROYED) {
LiveData<T>.LifecycleBoundObserver wrapper = new LiveData.LifecycleBoundObserver(owner, observer);
LiveData<T>.ObserverWrapper existing = (LiveData.ObserverWrapper)this.mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer with different lifecycles");
} else if (existing == null) {
owner.getLifecycle().addObserver(wrapper);
}
}
}
}
public class Fragment implements LifecycleOwner {
LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
public Lifecycle getLifecycle() {
return this.mLifecycleRegistry;
}
}
public class FragmentActivity extends SupportActivity implements LifecycleOwner {
private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
public Lifecycle getLifecycle() {
return this.mLifecycleRegistry;
}
}
不管是 Fragment 还是 FragmentActivity,getLifecycle() 都是 LifecycleRegistry。
// 接着来看怎么调用 LifecycleRegistry 的。
public class Fragment {
void performCreate(Bundle savedInstanceState) {
this.onCreate(savedInstanceState);
......
// 都是通过调用 LifecycleRegistry.handleLifecycleEvent 方法来通知生命周期变化的
this.mLifecycleRegistry.handleLifecycleEvent(Event.ON_CREATE);
}
void performResume() {
this.onResume();
......
this.mLifecycleRegistry.handleLifecycleEvent(Event.ON_RESUME);
}
}
从源码,可以看出我们自己代理调用 mLifecycleRegistry.handleLifecycleEvent(Event) 就可以切换生命周期了。
3,总结:
1,自己创建一个 FragmentActivity。(因为2个插件的 ClassLoader 不一样,导致插件之间 FragmentActivity 就不一样了。)
2,把 FragmentActivity 的 mFragments.mHost 赋值给 Fragment.mHost。这样使得 getActivity() 不为空了。
3,把 Fragment 的 mLifecycleRegistry 赋值给 FragmentActivity,mLi