RePlugin 坑位使用原理与指南
RePlugin 坑位使用原理与指南
一、坑位机制概述
RePlugin 框架中的"坑位"(Pit)是一种预先注册在宿主 AndroidManifest.xml 中的 Activity/Service/Provider 组件,主要用于承载插件中的组件运行。这种机制使得插件无需修改宿主应用的 manifest 文件,就能动态加载并运行插件组件,有效地解决了 Android 系统对组件注册的严格要求。
坑位机制是 RePlugin 框架最核心的设计之一,它确保了插件框架的稳定性和灵活性,同时使得插件能够像正常安装的应用一样使用四大组件。
二、坑位类型与数量
RePlugin 框架中有以下几种类型的坑位:
1. Activity 坑位
Activity 坑位按照以下维度进行分类:
1.1 透明度分类
- 透明坑位(Transparent,TS): 用于加载需要透明背景的 Activity
- 不透明坑位(Non-Transparent,NTS): 用于加载普通 Activity
1.2 启动模式分类
每种透明度类型下,又按照 Android 的四种启动模式细分:
- standard: 标准模式
- singleTop: 栈顶复用模式
- singleTask: 栈内复用模式
- singleInstance: 单实例模式
1.3 TaskAffinity 分类
为了支持插件中的 TaskAffinity 功能,RePlugin 还提供了专门的 TaskAffinity 坑位组。
2. 进程坑位
RePlugin 支持多进程运行插件,主要有:
- UI 进程:宿主主进程
- 常驻进程:插件管理进程,负责插件的加载、通信等
- 自定义插件进程:可以为插件分配独立进程运行
3. 默认坑位数量
根据 RePluginHostConfig 的默认配置:
// 透明坑位数量
ACTIVITY_PIT_COUNT_TS_STANDARD = 2;
ACTIVITY_PIT_COUNT_TS_SINGLE_TOP = 2;
ACTIVITY_PIT_COUNT_TS_SINGLE_TASK = 2;
ACTIVITY_PIT_COUNT_TS_SINGLE_INSTANCE = 3;// 不透明坑位数量
ACTIVITY_PIT_COUNT_NTS_STANDARD = 6;
ACTIVITY_PIT_COUNT_NTS_SINGLE_TOP = 2;
ACTIVITY_PIT_COUNT_NTS_SINGLE_TASK = 3;
ACTIVITY_PIT_COUNT_NTS_SINGLE_INSTANCE = 2;// TaskAffinity 组数
ACTIVITY_PIT_COUNT_TASK = 2;
三、坑位分配原理
RePlugin 的坑位分配遵循以下原则:
1. Activity 坑位分配流程
- 坑位匹配:根据插件 Activity 的启动模式、透明度和 TaskAffinity,找到对应类型的坑位集合
- 优先复用:优先查找已分配给相同插件、相同 Activity 的坑位
- 空白坑位:如果没有可复用的坑位,则寻找状态为
STATE_NONE
的空白坑位 - 回收坑位:如果没有空白坑位,则查找没有引用(没有运行中实例)的最老坑位进行回收
- 强制分配:如果所有坑位都在使用中,则选择时间戳最老的坑位,结束其中的 Activity 后重新分配
2. 进程坑位分配流程
StubProcessManager
负责管理进程坑位,主要流程如下:
- 进程匹配:根据插件名称找到最合适的进程坑位
- 优先级计算:根据以下顺序确定优先级
- 已分配给相同插件的进程 (优先级最高)
- 空闲进程
- 已停止的进程
- 分配时间超过10秒的进程
- 没有任何组件的进程
- 强制分配:如需要,会终止已有进程并重新分配
3. 坑位分配流程图
也可以简化为以下基本流程:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ │ │ │ │ │
│ 插件 Activity │────▶│ 坑位匹配 │────▶│ 坑位分配 │
│ │ │ │ │ │
└─────────────────┘ └─────────────────┘ └────────┬────────┘│▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ │ │ │ │ │
│ Activity 创建 │◀────│ 启动坑位 │◀────│ 记录映射 │
│ │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
四、TaskAffinity 支持机制
TaskAffinity 是 Android 中控制 Activity 分组的机制,RePlugin 通过特殊设计支持插件中的 TaskAffinity:
- TaskAffinity 坑位组:提供多组 TaskAffinity 坑位,每组包含不同启动模式的坑位
- TaskAffinity 索引计算:通过对插件 Activity 的 taskAffinity 值计算索引,确定使用哪组坑位
- 同组复用:相同 TaskAffinity 的 Activity 尽量使用同一组坑位,确保它们在同一个任务栈中
五、坑位状态管理
坑位状态由 ActivityState
类管理,主要有三种状态:
- STATE_NONE:未使用状态
- STATE_OCCUPIED:已被分配状态
- STATE_RESTORED:系统恢复状态(系统恢复 Activity 时使用)
坑位状态变化流程:
- 分配时:调用
occupy()
方法,状态变为STATE_OCCUPIED
- 创建 Activity 时:调用
create()
方法,添加 Activity 引用 - 销毁 Activity 时:调用
removeRef()
方法,移除 Activity 引用 - 回收时:调用
recycle()
方法,状态变为STATE_NONE
六、坑位使用最佳实践
1. 坑位数量配置
在项目中使用 RePlugin 时,可根据实际需求调整坑位数量:
repluginHostConfig {// 常规 Activity 的坑位数countNormalStandard = 8countNormalSingleTop = 4countNormalSingleTask = 6countNormalSingleInstance = 3// 透明 Activity 的坑位数countTranslucentStandard = 3countTranslucentSingleTop = 3countTranslucentSingleTask = 3countTranslucentSingleInstance = 3// TaskAffinity 的坑位组数countTaskAffinity = 4// 是否使用 AppCompat 库useAppCompat = true
}
2. 避免坑位耗尽
合理使用坑位,避免同时打开过多相同类型的 Activity,建议:
- 及时关闭不需要的 Activity
- 优先使用 standard 启动模式
- 使用 Fragment 代替多个功能相似的 Activity
3. TaskAffinity 的合理使用
- 避免在插件中定义过多不同的 TaskAffinity
- 相似功能的 Activity 尽量使用相同的 TaskAffinity
- 注意 TaskAffinity 与启动模式的组合使用
4. 多进程注意事项
- 合理规划插件进程分配,避免创建过多进程
- 考虑进程间通信成本,非必要时使用同一进程
- 注意进程被杀时的状态恢复问题
5. 动态坑位注册
通过使用 registerHookingClass
方法,可以动态注册坑位,使得更灵活地处理插件中的组件:
// 注册 Activity 动态坑位
RePlugin.registerHookingClass("com.example.host.TargetActivity", // 宿主中定义的目标类名RePlugin.createComponentName("demo1", "com.example.plugin.PluginActivity"), // 插件中的实际实现类DummyActivity.class // 默认实现类,通常使用 Dummy 系列类
);
这种方式有以下优势:
- 无需预先在宿主 Manifest 中注册大量坑位
- 可以按需动态添加插件组件的映射
- 适合处理外部调用或 Schema 跳转等场景
6. Schema 支持配置
对于需要支持 Schema 跳转的插件,可通过以下方式配置:
// 1. 在插件初始化时注册 Schema 映射
public static void registerSchemaHandler(Context context) {// 注册外部跳转的入口 ActivityRePlugin.registerHookingClass("com.example.host.SchemaActivity", // 宿主中的 Schema 处理 ActivityRePlugin.createComponentName("demo1", "com.example.plugin.SchemaActivity"), // 插件中的接收 ActivityDummyActivity.class);// 注册更多需要支持的 Schema 入口...
}
然后在宿主的 AndroidManifest.xml 中配置 Schema 处理 Activity:
<activity android:name=".SchemaActivity" android:exported="true"><intent-filter><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><data android:scheme="replugin" android:host="demo1" /></intent-filter>
</activity>
这样,外部应用或网页可通过 replugin://demo1/xxxx
的方式直接跳转到插件中的页面。
七、常见问题与解决方案
1. 坑位不足问题
现象:启动插件 Activity 失败,日志提示坑位分配失败。
解决方案:
- 增加对应类型的坑位数量
- 检查是否有 Activity 没有正确释放
- 调整 Activity 的启动模式,减少 singleTask/singleInstance 模式的使用
- 使用 registerHookingClass 方法动态注册坑位
2. TaskAffinity 异常
现象:插件中设置了 TaskAffinity 的 Activity 没有按预期分组。
解决方案:
- 检查 TaskAffinity 坑位组数量是否足够
- 确保 TaskAffinity 字符串一致
- 排查启动模式与 TaskAffinity 组合是否正确
3. 进程分配问题
现象:插件运行在非预期的进程中。
解决方案:
- 检查进程指定是否正确
- 排查是否有其他插件占用了指定进程
- 增加自定义进程数量
4. Schema 跳转失败
现象:外部通过 Schema URL 无法正确跳转到插件页面。
解决方案:
- 检查宿主 AndroidManifest.xml 中的 Schema 配置是否正确
- 确认已通过 registerHookingClass 正确注册了 Schema 处理组件
- 确保插件已正确安装并加载
- 检查 Schema URL 格式是否符合预期
八、总结
RePlugin 的坑位机制是其核心设计,通过预先注册坑位和动态分配,实现了插件组件的灵活加载和稳定运行。合理配置和使用坑位,对于提高插件框架的稳定性和性能至关重要。
开发者在使用 RePlugin 时,应该根据实际需求合理配置坑位数量,遵循最佳实践,避免坑位资源浪费和耗尽问题,以充分发挥 RePlugin 框架的优势。动态坑位注册和 Schema 支持等高级功能,可以进一步提升插件框架的灵活性和用户体验。