RePlugin GitHub 主页
RePlugin Wiki 主页
RePlugin 原理剖析
全面插件化:RePlugin 的使命
* RePlugin 介绍
* 主要优势
* RePlugin 架构
* 快速上手
* 针对主程序开发者
* 将 RePlugin 接入到你的主程序
* 安装一个插件
* 打开一个插件的 Activity
* 针对插件开发者
* 将现有工程 → RePlugin 插件
* 声明一个插件的 Activity
* 打开一个插件的 Activity
* 调试与运行
* 调试宿主
* 环境配置
* 仓库配置
* 插件使用配置
* 插件的 Gradle 任务
* 调试插件
* 环境配置
* 仓库配置
* 插件使用配置
* 插件的 Gradle 任务
一、RePlugin 介绍
RePlugin
是一套完整的、稳定的、适合全面使用的,占坑类插件化方案。我们“逐词”拆开来解释这个定义:
完整的:让插件运行起来“像单品那样”,支持大部分特性
稳定的:如此灵活完整的情况下,其框架崩溃率仅为业内很低的“万分之一”
适合全面使用的:其目的是让应用内的“所有功能皆为插件”
占坑类:以稳定为前提的
Manifest
占坑思路插件化方案:基于 Android 原生 API 和语言来开发,充分利用原生特性
1.1 主要优势
极其灵活:主程序无需升级(无需在
Manifest
中预埋组件),即可支持新增的四大组件,甚至全新的插件非常稳定:
Hook
点仅有一处(ClassLoader)
,无任何Binder Hook
!如此可做到其崩溃率仅为“万分之一”,并完美兼容市面上近乎所有的 Android ROM特性丰富:支持近乎所有在“单品”开发时的特性。包括静态
Receiver
、Task-Affinity
坑位、自定义Theme
、进程坑位、AppCompat
、DataBinding
等易于集成:无论插件还是主程序,只需“数行”就能完成接入
管理成熟:拥有成熟稳定的“插件管理方案”,支持插件安装、升级、卸载、版本管理,甚至包括进程通讯、协议版本、安全校验等
1.2 RePlugin 架构
以 360 手机卫士为例:
系统层 —— Android:为 Android Framework 层。只有
ClassLoader
是Hook
的,而AMS
、Resources
等都没有做Hook
,确保了其稳定性。框架层 —— RePlugin 框架:
RePlugin
框架层,只有RePlugin
是对“上层完全公开”的,其余均为Internal
,或“动态编译方案”生效后的调用,对开发者而言是“无需关心”的。插件层 —— 各插件:“标蓝部分”是各插件,包括大部分的业务插件(如体检、清理、桌面插件等)。而其中“标黄部分”是支撑一个应用的各种基础插件,如
WebView
、Download
、Share
,甚至Protobuf
都能成为基础插件。
二、快速上手
RePlugin
的使用方法非常简单易懂,大部分情况下和“单品”开发无异。以下分别针对:主程序(接入 RePlugin
)和插件(研发 RePlugin
插件)来进行导引,以便适用于不同的团队成员。
2.1 针对主程序开发者
2.1.1 将 RePlugin 接入到你的主程序
只需三步,就能让你的 “主程序” 接入 RePlugin
:
注意:目前有开发同学反馈,开启
Instant Run
时可能会出现运行时异常情况,请临时关掉此功能后再试。
有关“混淆”:
RePlugin
的 AAR 自带Proguard
文件,你无需关心,直接引入 AAR 即可生效。此外,其内部仅 Keep 了关键的接口类,大部分都是允许被混淆的,故对应用来说也没有影响。
第 1 步:添加 RePlugin Host Gradle 依赖
在项目根目录的 build.gradle
(注意:不是 app/build.gradle
) 中添加 replugin-host-gradle
依赖:
buildscript {
dependencies {
classpath 'com.qihoo360.replugin:replugin-host-gradle:2.3.1'
...
}
}
第 2 步:添加 RePlugin Host Library 依赖
在 app/build.gradle
中应用 replugin-host-gradle
插件,并添加 replugin-host-lib
依赖:
android {
// ATTENTION!!! Must CONFIG this to accord with Gradle's standard, and avoid some error
defaultConfig {
// 如果已经有一个 applicationId 则无需再添加
applicationId "com.qihoo360.replugin.sample.host"
...
}
...
}
// ATTENTION!!! Must be PLACED AFTER "android{}" to read the applicationId
// 注意!必须放在 “android{ }” 块后面,因为要先读取 applicationId
apply plugin: 'replugin-host-gradle'
/**
* 配置项均为可选配置,默认无需添加
* 更多可选配置项参见replugin-host-gradle的RepluginConfig类
* 可更改配置项参见 自动生成RePluginHostConfig.java
*/
repluginHostConfig {
/**
* 是否使用 AppCompat 库
* 不需要个性化配置时,无需添加
*/
useAppCompat = true
/**
* 背景不透明的坑的数量
* 不需要个性化配置时,无需添加
*/
countNotTranslucentStandard = 6
countNotTranslucentSingleTop = 2
countNotTranslucentSingleTask = 3
countNotTranslucentSingleInstance = 2
}
dependencies {
implementation 'com.qihoo360.replugin:replugin-host-lib:2.3.1'
...
}
以下内容请务必注意:
请一定要确保符合 Gradle 开发规范,即“必须将包名写在applicatonId”,而非
AndroidManifest.xml
中(通常从 Eclipse 迁移过来的项目可能出现此问题)。如果不这么写,则有可能导致运行时出现“Failed to find provider info for com.ss.android.auto.loader.p.main”
的问题。具体可参见 #87 Issue 的问答。请将
apply plugin: 'replugin-host-gradle'
放在android{ }
块之后,防止出现无法读取applicationId
,导致生成的坑位出现异常如果你的应用需要支持
AppComat
,则除了在主程序中引入AppComat-v7
包以外,还需要在宿主的build.gradle
中添加下面的代码,若不支持AppComat
则请不要设置此项:
repluginHostConfig {
useAppCompat = true
}
开启
useAppCompat
后,RePlugin
会在编译期生成AppCompat
专用坑位,这样插件若使用AppCompat
的Theme
时就能生效了。若不设置,则可能会出现“IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.”
的异常。
- 如果你的应用需要个性化配置坑位数量,则需要在宿主的
build.gradle
中添加下面的代码:
repluginHostConfig {
/**
* 背景不透明的坑的数量
*/
countNotTranslucentStandard = 6
countNotTranslucentSingleTop = 2
countNotTranslucentSingleTask = 3
countNotTranslucentSingleInstance = 2
}
- 笔者在导入了上述依赖库之后,进行同步项目时,遇到了如下问题:
Gradle sync failed: No signature of method: com.android.build.gradle.internal.scope.VariantScopeImpl.getMergeAssetsTask() is applicable for argument types: () values: []
。这是因为 Google 对 3.2.0 版本之后的 Gradle 构建工具做了一些修改(笔者用的是 3.2.1),RePlugin
官方还没有对此用兼容性处理。 解决方法也很简单,把项目根目录的build.gradle
(注意:不是app/build.gradle
)中的
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
改为
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
}
即可,具体参见 #Issue 640。
更多可选配置项请参见 replugin-host-gradle
的 RepluginConfig
类。
第 3 步:配置 Application 类
让工程的 Application
直接继承自 RePluginApplication
。笔者发现,如果应用导入了 RePlugin
,但是不配置 Application
类的话,那么应用程序将无法运行。详情请看这里 #Issue 46 sample 运行报错。
如果你的工程已有 Application
类,则可以将基类切换到 RePluginApplication
即可。或者你也可以用“非继承式”接入。
public class MainApplication extends RePluginApplication {
}
既然声明了 Application
,自然还需要在 AndroidManifest
中配置这个 Application
。
备选:“非继承式”配置 Application
若你的应用对 Application
类继承关系的修改有限制,或想自定义 RePlugin
加载过程(慎用!),则可以直接调用相关方法来使用 RePlugin
。
public class MainApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
RePlugin.App.attachBaseContext(this);
....
}
@Override
public void onCreate() {
super.onCreate();
RePlugin.App.onCreate();
....
}
@Override
public void onLowMemory() {
super.onLowMemory();
/* Not need to be called if your application's minSdkVersion > = 14 */
RePlugin.App.onLowMemory();
....
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
/* Not need to be called if your application's minSdkVersion > = 14 */
RePlugin.App.onTrimMemory(level);
....
}
@Override
public void onConfigurationChanged(Configuration config) {
super.onConfigurationChanged(config);
/* Not need to be called if your application's minSdkVersion > = 14 */
RePlugin.App.onConfigurationChanged(config);
....
}
}
针对“非继承式”的注意点:
所有方法必须在 UI 线程来“同步”调用。切勿放到工作线程,或者通过
post
方法来执行所有方法必须一一对应,例如
RePlugin.App.attachBaseContext()
方法只在Application.attachBaseContext()
中调用请将
RePlugin.App
的调用方法,放在“仅次于super.xxx()
”方法的后面
2.1.2 安装一个插件
这部分内容请看 RePlugin 关于插件管理。
2.1.3 打开一个插件的 Activity
这部分内容请看 RePlugin 关于插件的组件。
2.2 针对插件开发者
2.2.1 将现有工程 → RePlugin 插件
只需两步,就能让你的 App 变成 “RePlugin
插件”:
有关“混淆”:
RePlugin
的 AAR 自带Proguard
文件,你无需关心,直接引入 AAR 即可生效。此外,其内部仅 Keep 了关键的接口类,大部分都是允许被混淆的,故对应用来说也没有影响。
第 1 步:添加 RePlugin Plugin Gradle 依赖
在项目根目录的 build.gradle
(注意:不是 app/build.gradle
) 中添加 replugin-plugin-gradle
依赖:
buildscript {
dependencies {
classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.3.1'
...
}
}
第 2 步:添加 RePlugin Plugin Library 依赖
在 app/build.gradle
中应用 replugin-plugin-gradle
插件,并添加 replugin-plugin-lib
依赖:
apply plugin: 'replugin-plugin-gradle'
dependencies {
implementation 'com.qihoo360.replugin:replugin-plugin-lib:2.3.1'
...
}
接下来你就可以像单品那样,开你的插件。生成出来的是 APK,既可以“安装到设备”,又可以“作为插件”使用。
2.2.2 声明一个插件的 Activity
这部分内容请看 RePlugin 关于插件的组件。
2.2.3 打开一个插件的 Activity
这部分内容请看 RePlugin 关于插件的组件。
三、调试与运行
3.1 调试宿主
3.1.1 环境配置
3.1.1.1 仓库配置
buildscript {
repositories {
maven { url 'https://dl.google.com/dl/android/maven2/' }
google()
jcenter()
}
dependencies {
...
classpath 'com.qihoo360.replugin:replugin-host-gradle:2.3.1'
}
}
3.1.1.2 插件使用配置
// ATTENTION!!! Must be PLACED AFTER "android{}" to read the applicationId
apply plugin: 'replugin-host-gradle'
repluginHostConfig {
/**
* 是否使用 AppCompat 库
* 不需要个性化配置时,无需添加
*/
useAppCompat = true
}
apply plugin
尽量放在 android 配置之后,因为可以自动读取 android中 的配置项,方便以后升级。简单的说,就是放在你 build.gradle
文件末尾即可。
3.1.2 插件的 Gradle 任务
-
rpGenerateDebugBuiltinJson
或rpGenerateReleaseBuiltinJson
等
生成内置插件的配置文件(一般很少使用,编译时会自动处理)
-
rpGenerateDebugHostConfig
或rpGenerateReleaseHostConfig
等
生成插件们的坑位配置文件(一般很少使用,编译时会自动处理)
-
rpShowPluginsDebug
和rpShowPluginsRelease
等
查看所有内置插件的信息
3.2 调试插件
3.2.1 环境配置
3.2.1.1 仓库配置
buildscript {
repositories {
maven { url 'https://dl.google.com/dl/android/maven2/' }
google()
jcenter()
}
dependencies {
...
classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.3.1'
}
}
3.2.1.2 插件使用配置
apply plugin: 'replugin-plugin-gradle'
repluginPluginConfig {
//插件名
pluginName = "demo1"
//宿主app的包名
hostApplicationId = "com.qihoo360.replugin.sample.host"
//宿主app的启动activity
hostAppLauncherActivity = "com.qihoo360.replugin.sample.host.MainActivity"
}
apply plugin
需要放在 android 配置之后,因为需要读取 android 中的配置项。简单的说,就是放在你 build.gradle
文件末尾即可。
3.2.2 插件的 Gradle 任务
一些 Gradle 任务依赖宿主,需要在宿主中添加
RePlugin.enableDebugger(base, BuildConfig.DEBUG);
这行代码
rpForceStopHostApp
强制停止宿主程序
-
rpInstallAndRunPluginDebug
或rpInstallAndRunPluginRelease
等
安装插件到宿主并运行(常用任务)
-
rpInstallPluginDebug
或rpInstallPluginRelease
等
仅仅安装插件到宿主
rpRestartHostApp
重启宿主程序
-
rpRunPluginDebug
或rpRunPluginRelease
等
仅仅运行插件,如果插件前面没安装,则执行不成功
rpStartHostApp
启动宿主程序
-
rpUninstallPluginDebug
或rpUninstallPluginRelease
仅仅卸载插件,如果完全卸载,还需要执行 rpRestartHostAp
任务