当前位置: 首页 > 工具软件 > RePlugin > 使用案例 >

Replugin 宿主嵌入插件 Fragment

羊慈
2023-12-01

业务场景:在插件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,mLifecycleRegistry。
4,手动代理 Fragment 的生命周期,并手动调用 mLifecycleRegistry.handleLifecycleEvent(Event) ,使得 VM 正常。

4,最终实现源码:

插件 A 要使用 插件 B 的 XXXFragment,来展示。
再次强调 插件B v4 包源码,都是 28.0.0 版本。
插件 B 里面的代码:

// 要展示的 XXXFragment 继承 ExportFragment
public class ExportFragment extends Fragment {
    protected FragmentActivityEx mActivityEx;

    public FragmentActivityEx getActivityEx(Activity activity) {
        if (mActivityEx == null) {
            mActivityEx = FragmentActivityEx.buildActivityEx(activity, this);
        }
        return mActivityEx;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mActivityEx != null) {
            mActivityEx.release();
        }
    }
}
// ContextUtils.getPluginAppContext() 就是当前插件(插件B)的 AppContext。
public class FragmentActivityEx extends FragmentActivity {
    public Fragment mTargetFrag;

    public static FragmentActivity buildActivityEx(Activity activity, Fragment fragment) {
        FragmentActivityEx obj = new FragmentActivityEx();//通过反射创建对象
        try {
            Class clazz = Class.forName("android.content.ContextWrapper");
            Field f = clazz.getDeclaredField("mBase");
            f.setAccessible(true);
            f.set(obj, activity);
        } catch (Exception e) {
        }
        try {
            // 4.4.4 空指针
            Class clazz = Class.forName("android.view.ContextThemeWrapper");
            Field f = clazz.getDeclaredField("mBase");
            f.setAccessible(true);
            f.set(obj, activity);
        } catch (Exception e) {
        }
        try {
            Class clazz = Class.forName("android.app.Activity");
            Field f = clazz.getDeclaredField("mApplication");
            f.setAccessible(true);
            f.set(obj, ContextUtils.getPluginAppContext());
        } catch (Exception e) {
        }
        try {
            Field field = null;
            Class csl = null;
            // 步骤2,设置 Fragment 的 mHost。
            csl = Class.forName("android.support.v4.app.FragmentActivity");
            field = csl.getDeclaredField("mFragments");
            field.setAccessible(true);
            Object fControl = field.get(obj);
            csl = Class.forName("android.support.v4.app.FragmentController");
            field = csl.getDeclaredField("mHost");
            field.setAccessible(true);
            Object host = field.get(fControl);
            csl = Class.forName("android.support.v4.app.Fragment");
            field = csl.getDeclaredField("mHost");
            field.setAccessible(true);
            field.set(fragment, host);
            // 步骤3,设置 ActivityEx 的 mLifecycleRegistry。
            csl = Class.forName("android.support.v4.app.SupportActivity");
            field = csl.getDeclaredField("mLifecycleRegistry");
            field.setAccessible(true);
            field.set(obj, fragment.getLifecycle());
        } catch (Exception e) {
            if (LogUtils.isDebug()) {
                throw new RuntimeException(e);
            }
        }
        return obj;
    }

    private Activity getSrcActivity() {
        if (getBaseContext() instanceof Activity) {
            return (Activity) getBaseContext();
        }
        if ((getBaseContext() instanceof ContextWrapper)
                && ((ContextWrapper) getBaseContext()).getBaseContext() instanceof Activity) {
            return ((Activity) ((ContextWrapper) getBaseContext()).getBaseContext());
        }
        return null;
    }

    @Override
    public boolean equals(Object obj) {
        Activity activity = getSrcActivity();
        if (activity != null) {
            return activity.equals(obj);
        }
        return super.equals(obj);
    }
    
    @Override
    public FragmentManager getSupportFragmentManager() {
    	// 为解决 glide 内存泄漏
        return mTargetFrag.getChildFragmentManager();
    }

    public void release() {
        try {
            Field field = null;
            Class csl = null;
            // 设置 ActivityEx 的 LifecycleOwner
            csl = Class.forName("android.support.v4.app.SupportActivity");
            field = csl.getDeclaredField("mLifecycleRegistry");
            field.setAccessible(true);
            field.set(this, null);
        } catch (Exception e) {
        }
        mTargetFrag = null;
    }

    @Override
    public Intent getIntent() {
        Activity activity = getSrcActivity();
        if (activity != null) {
            return activity.getIntent();
        }
        return super.getIntent();
    }

    @Override
    public void finish() {
        Activity activity = getSrcActivity();
        if (activity != null) {
            activity.finish();
        }
        super.finish();
    }

    @Override
    public Window getWindow() {
        Activity activity = getSrcActivity();
        if (activity != null) {
            return activity.getWindow();
        }
        return super.getWindow();
    }

    @Override
    public WindowManager getWindowManager() {
        Activity activity = getSrcActivity();
        if (activity != null) {
            return activity.getWindowManager();
        }
        return super.getWindowManager();
    }

    @Override
    public Context getApplicationContext() {
        return ContextUtils.getPluginAppContext();
    }

    @Override
    public Resources getResources() {
        return ContextUtils.getPluginAppContext().getResources();
    }

    @Override
    public AssetManager getAssets() {
        return ContextUtils.getPluginAppContext().getAssets();
    }

    @Override
    public LayoutInflater getLayoutInflater() {
        return LayoutInflater.from(ContextUtils.getPluginAppContext()).cloneInContext(ContextUtils.getPluginAppContext());
    }

    @Override
    public Object getSystemService(String name) {
        // Dialog WindowManager$BadTokenException
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            return getLayoutInflater();
        }
        Activity activity = getSrcActivity();
        if (activity != null) {
            return activity.getSystemService(name);
        }
        return super.getSystemService(name);
    }

    @Override
    public Resources.Theme getTheme() {
        return ContextUtils.getPluginAppContext().getTheme();
    }

    @Override
    public ComponentName getComponentName() {
        // 4.4.4闪退
        // NullPointerException
        // ApplicationPackageManager.getActivityInfo
        // ContentViewCore.hasHardwareAcceleration android.webkit.WebView.init
        // com.baidu.mobads.container.ae.
        Activity activity = getSrcActivity();
        if (activity != null) {
            return activity.getComponentName();
        }
        return super.getComponentName();
    }

    @Override
    public void startActivity(Intent intent) {
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        ContextUtils.getPluginAppContext().startActivity(intent);
    }

    @Override
    public void startActivity(Intent intent, Bundle options) {
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        ContextUtils.getPluginAppContext().startActivity(intent, options);
    }
}

插件 A 里面的代码:

// 步骤4,手动调用生命周期。
// 直接调用 FragmentProxy.create 方法,就创建了一个代理 Fragment。
public class FragmentProxy extends Fragment {
    private static final String TAG = "FragmentProxy";

    private static final String ARGS_KEY_CLS = "cls";
    private static final String PLUGIN_NAME = "插件B包名";
    public static final String FRAGMENT_CLASS_NAME = "插件B.XXXFragment";

    public static Fragment create(String className) {
        AnswerFragmentProxy proxy = new AnswerFragmentProxy();
        Bundle args = new Bundle();
        args.putString(ARGS_KEY_CLS, className);
        proxy.setArguments(args);
        return proxy;
    }

    private Object target;
    private Object targetAtyEx;

    public void createTarget(String className) {
        try {
            Field field = null;
            Class csl = null;
            Context tContext = RePlugin.fetchContext(PLUGIN_NAME);
            csl = Class.forName(className, false, tContext.getClassLoader());
            target = csl.newInstance();

            Object atyEx = ReflectUtils.invokeMethod(target, "getActivityEx", new Class[]{Activity.class}, new Object[]{getActivity()});

        } catch (Exception e) {
        }
    }

    public View getTargetView() {
        try {
            Field field = null;
            Class csl = null;
            Context tContext = RePlugin.fetchContext(PLUGIN_NAME);
            csl = Class.forName("android.support.v4.app.Fragment", false, tContext.getClassLoader());
            field = csl.getDeclaredField("mView");
            field.setAccessible(true);
            return (View) field.get(target);
        } catch (Exception e) {
        }
        return null;
    }

    public void release() {
        try {
            Field field = null;
            Class csl = null;
            Context tContext = RePlugin.fetchContext(PLUGIN_NAME);
            csl = Class.forName("android.support.v4.app.Fragment", false, tContext.getClassLoader());
            field = csl.getDeclaredField("mHost");
            field.setAccessible(true);
            field.set(target, null);
        } catch (Exception e) {
        }
        target = null;
        targetAtyEx = null;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        createTarget(getArguments().getString(ARGS_KEY_CLS));
        if (target != null) {
            ReflectUtils.invokeMethod(target, "performCreate", new Class[]{Bundle.class}, new Object[]{savedInstanceState});
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (target != null) {
        	// 拿到 插件B 的 inflate。
            Object inflaterObj = ReflectUtils.invokeMethod(targetAtyEx, "getLayoutInflater", null);
            ReflectUtils.invokeMethod(target, "performCreateView", new Class[]{LayoutInflater.class, ViewGroup.class, Bundle.class}, new Object[]{inflaterObj, container, savedInstanceState});
            return getTargetView();
        }
        return null;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        if (target != null) {
            ReflectUtils.invokeMethod(target, "onViewCreated", new Class[]{View.class, Bundle.class}, new Object[]{view, savedInstanceState});
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (target != null) {
            ReflectUtils.invokeMethod(target, "performActivityCreated", new Class[]{Bundle.class}, new Object[]{savedInstanceState});
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (target != null) {
            ReflectUtils.invokeMethod(target, "onAttach", new Class[]{Context.class}, new Object[]{context});
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (target != null) {
            ReflectUtils.invokeMethod(target, "performResume", null);
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        if (target != null) {
            ReflectUtils.invokeMethod(target, "performStart", null);
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        if (target != null) {
            ReflectUtils.invokeMethod(target, "performPause", null);
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (target != null) {
            ReflectUtils.invokeMethod(target, "performStop", null);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (target != null) {
            ReflectUtils.invokeMethod(target, "performDestroy", null);
            release();
        }
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        if (target != null) {
            ReflectUtils.invokeMethod(target, "performDestroyView", null);
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        if (target != null) {
            ReflectUtils.invokeMethod(target, "performDetach", null);
        }
    }
}
最后附上混淆
-keep class android.support.v4.app.FragmentActivity { android.support.v4.app.FragmentController mFragments;}
-keep class android.support.v4.app.Fragment {*;}
-keep class android.support.v4.app.FragmentController {android.support.v4.app.FragmentHostCallback mHost;}
-keep class android.support.v4.app.SupportActivity {android.arch.lifecycle.LifecycleRegistry mLifecycleRegistry;}
-keep class android.arch.lifecycle.LifecycleRegistry {<methods>;}
-keep class com.xxx.ExportFragment {*;}
-keep class * extends com.xxx.ExportFragment {<methods>;}
 类似资料: