Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

ClarkRep/Learn_Replugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

61 Commits

Repository files navigation

Learn_Replugin

360的Replugin框架研究案例

由于官方Replugin是没有适配AndroidX项目的,所以此Demo对Replugin2.3.3的Release版本进行了AndroidX的改造,具体改造点参考:Replugin适配AndroidX

Demo结构:

  • host:宿主project,用来加载插件、调试插件;
  • library:用来生成test.jar的module,生成的test.jar作为宿主和插件的中间桥梁,实现宿主和插件的通信;
  • plugindemo1:插件project,用来生成给宿主使用的插件apk;
  • plugindemo2:插件project,用来生成给宿主使用的插件apk;
  • replugin-host-gradle:host插件库
  • replugin-host-library:host依赖库
  • replugin-plugin-gradle:plugin插件库
  • replugin-plugin-library:plugin依赖库
  • RepluginHostLocalRepo:replugin-host-gradle编译后生成的本地host插件库,host就依赖该本地库进行编译的;
  • RepluginPluginLocalRepo:replugin-plugin-gradle编译后生成的本地plugin插件库,plugindemo1就依赖该本地插件库进行编译的。

参考资料:

一、原理分析

二、Demo讲解

前面的原理分析已经讲述了 replugin-host-gradle、replugin-host-library、replugin-plugin-gradle、replugin-plugin-library的一些原理,下面会结合本demo去实践一下Replugin的具体使用方式。

  1. host

host 是本项目的宿主project,集成了Replugin的 replugin-host-gradle插件库、replugin-host-library依赖库,是用来调试各个插件的功能。
plugindemo1plugindemo2打包生成的apk作为插件放置在hostassets文件夹中,作为host的插件进行使用。

  1. library

library 是用来生成 test.jar 的库,本身和其他几个库没有任何依赖关系,只是提供 test.jar 给host和plugindemo1作为中间桥梁。

  1. plugindemo1

编译生成的APK作为host的插件进行使用,集成了Replugin的replugin-plugin-gradle插件库、replugin-plugin-library依赖库。
complieOnly 依赖了 test.jar,为了骗过编译期,从而让宿主可以使用 test.jar 里面的接口去调用 plugindemo1 里面的实现类,从而实现宿主和插件的交互工作。

  1. plugindemo2

编译生成的APK作为host的插件进行使用,集成了Replugin的replugin-plugin-gradle插件库、replugin-plugin-library依赖库。
complieOnly 依赖了 supportV4 库,为了骗过编译期,从而加载宿主的 Fragment.class ,这样就保证了宿主和插件的 Fragment.class 是同一个,从而让宿主可以加载插件的Fragment。

三、Replugin的缺陷

问题1:宿主和插件的类隔离

从上面原理分析可以看到,Replugin加载插件和宿主的使用的ClassLoader,是不同的ClassLoader,这就导致了使用的是相同的拓展库,并不能在宿主和插件中进行类型转换,会报 ClassCastException。除非插件使用compileOnly进行依赖,欺骗了编译期,从而会从宿主中去寻找该类。所以宿主初始化的一些对象或者属性,如果插件需要使用的话,还需要在插件再初始化一份,也就是将插件也作为一个可以独立运行的APP去打包。
将插件作为一个独立APP进行打包还是有个问题,就是臃肿,因为作为独立apk打包,依赖的库就比较多了,这样打包出来的插件甚至会和宿主差不多大小,所以需要尽可能的去减小插件的依赖,缩小插件的体积。

问题2:宿主加载插件Fragment存在的坑

上面我们说了,插件使用 compileOnly 可以骗过编译期,从而去加载宿主的类。我们想让宿主加载插件的Fragment,则必须保证宿主和插件的Fragment.class是同一个对象,则需要插件使用 compileOnly fragment.jar 包去骗过编译期。这时候又存在一个严重的问题,使用 compileOnly fragment.jar之后,会导致 replugin-plugin-gradle 无法将继承 FragmentActivity 的子类的继承关系修改为继承 PluginFragmentActivity。因为 FragmentActivity.class 和 Fragment.class 是在同一个包里面,使用 compileOnly 编译的时候, 在插件APK里 FragmentActivity.class 就不存在了,所以 replugin-plugin-gradle 没有办法修改继承关系。这就导致了在插件里凡是继承了 FragmentActivity 的子类,都没有办法使用 startActivity() 等一些 PluginFragmentActivity hook住的方法。
解决办法:让插件的所有要继承FragmentActivity的都继承 PluginFragmentActivity,这样就符合了Replugin所需要修改的默认继承关系,但是耦合性太高。

问题3:一些特殊的启动Activity方式在插件中无法使用

我们在插件中,使用 startActivity() 是没问题的,因为 replugin-plugin-gradle 修改了继承关系,这样保证我们可以和宿主一样去启动Activity。但是有一些特殊的Activity启动方式,我在插件里做了实验,是无法正常启动Activity的(宿主中可以)。比如广点通广告SDK启动他们的广告页面,是使用下面这种方式启动的:

 String className = "com.clark.learn.replugin.plugindemo1.TestClassNameActivity";
 Class<?> aClass = Class.forName(className);
 Intent intent = new Intent();
 intent.setClassName(TestPendingIntentActivity.this, className);
 PendingIntent activity = PendingIntent.getActivity(TestPendingIntentActivity.this, 0, intent, 134217728);
 activity.send();

这种启动方式在插件里去启动插件自身的Activity是行不通的,估计是由于该启动方式越过了Replugin所Hook住的逻辑,默认去开启宿主的Activity了,从而异常崩溃了,暂时没有解决办法。

问题4:一些第三方SDK的兼容性问题

如果我们使用的插件里集成里宿主没有的第三方库,这就需要小心一点,因为第三方库的逻辑有可能和Replugin不太兼容,建议这类第三方库都交给宿主去集成,插件compileOnly去使用。
还有 ARouter 库的问题,插件的路由表和宿主的路由表没有办法进行通信的问题,都需要解决。

About

360的Replugin框架研究案例

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

Contributors

AltStyle によって変換されたページ (->オリジナル) /