上篇博客已经分析了详细的源码和原理! 现在分析后面的这个部分!
Q1: 问题:“爱机密” 加密APk是否会影响插件化。
Shadow主要是在Transform期间对系统组件的子类进行字节码修改。如果“爱机密”的原理和这个由用途,就会受到影响。
7.0 Shadow Transform核心功能总览
整体架构流程图
Shadow Transform处理的核心功能
Transform处理的功能(字节码结构改变):
| 序号 | 功能 | 处理内容 | 实现效果 |
|---|---|---|---|
| 1 | 组件继承关系转换 | 将Activity/Service等父类替换为Shadow组件 | 插件组件被Shadow容器托管 |
| 2 | 组件信息收集 | 自动扫描并收集四大组件信息 | 生成组件注册表,无需手动配置 |
| 3 | 系统API重定向 | 将系统API调用替换为Shadow方法 | 插件代码透明访问插件资源 |
字节码插桩实现的功能(代码逻辑增强):
| 序号 | 功能 | 插桩方式 | 实现效果 |
|---|---|---|---|
| 1 | 生命周期增强 | 方法前后插入代码 | 自动管理组件生命周期状态 |
| 2 | 异常处理增强 | 添加try-catch块 | 统一异常捕获和处理 |
| 3 | 字段注入 | 动态添加成员变量 | 注入Shadow运行时委托对象 |
| 4 | 构造方法增强 | 插入初始化代码 | 初始化注入的字段 |
| 5 | 资源ID转换 | 替换常量引用为方法调用 | 解决资源ID冲突 |
| 6 | 性能监控 | 插入耗时统计代码 | 自动监控方法执行时间 |
| 7 | 日志注入 | 插入Log调用 | 自动记录方法调用轨迹 |
7.1 Transform机制的工作原理与顺序控制
7.1.1 工作流程时序图
7.1.2 Transform顺序问题与解决方案
核心问题:多个Transform的执行顺序不可控
// 问题场景:Transform顺序导致的不确定性
// 假设有两个Transform:
// Transform A: 修改类的父类
// Transform B: 根据类名做特定处理
// 场景1:Transform A 先执行
// 输入: class MainActivity extends Activity
// Transform A: 变为 class MainActivity extends ShadowActivity
// Transform B: 查找MainActivity,正常处理 ✅
// 场景2:Transform B 先执行
// 输入: class MainActivity extends Activity
// Transform B: 查找MainActivity,正常处理
// Transform A: 变为 class MainActivity extends ShadowActivity
// 结果:Transform B没有看到父类变化,可能做出错误判断 ❌
Shadow的解决方案:将多个SpecificTransform整合到一个Transform中
// ShadowPlugin.java - 注册单个Transform
public class ShadowPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
// 只注册一个ShadowTransform,内部包含所有转换逻辑
plugin.extension.registerTransform(new ShadowTransform(...));
}
}
// ShadowTransform.java - 内部顺序控制
public class ShadowTransform extends ClassTransform {
@Override
protected void setUp() {
// 按固定顺序添加SpecificTransform,确保执行顺序
addSpecificTransform(new ActivityTransform()); // 1. 先处理Activity
addSpecificTransform(new ServiceTransform()); // 2. 再处理Service
addSpecificTransform(new BroadcastReceiverTransform()); // 3. 处理BroadcastReceiver
addSpecificTransform(new ApplicationTransform()); // 4. 处理Application
addSpecificTransform(new ContextTransform()); // 5. 处理Context
addSpecificTransform(new ResourcesTransform()); // 6. 处理Resources
}
}
7.1.3 增量编译问题与解决方案
核心问题:修改Transform源码后,由于输入没变,Gradle不会重新执行Transform任务
// 问题复现流程:
// 1. 原始Transform逻辑:将Activity改为ShadowActivity
// 2. 修改Transform逻辑:将Activity改为NewShadowActivity
// 3. 重新构建:由于输入(插件字节码)没变,输出(修改后字节码)存在
// 4. Gradle认为任务是最新的,不执行Transform
// 5. 结果:修改没有生效 ❌
解决方案:将Transform程序本身也作为输入
// ClassTransform.java - Shadow的Transform基类
public abstract class ClassTransform extends Transform {
@Override
public Set<File> getSecondaryFiles() {
Set<File> secondaryFiles = new HashSet<>();
// 关键:将Transform程序本身也作为输入
// 这样修改Transform源码后,文件变化会触发任务重新执行
// 获取当前Transform的class文件路径
URL location = getClass().getProtectionDomain()
.getCodeSource().getLocation();
File transformJar = new File(location.getPath());
if (transformJar.exists()) {
secondaryFiles.add(transformJar);
}
// 添加所有依赖的class文件
secondaryFiles.addAll(getTransformDependencies());
return secondaryFiles;
}
private Set<File> getTransformDependencies() {
Set<File> dependencies = new HashSet<>();
// 添加transform-kit的jar包
dependencies.add(getJarFile(TransformKit.class));
// 添加所有SpecificTransform的实现类
for (SpecificTransform specific : getSpecificTransforms()) {
dependencies.add(getClassFile(specific.getClass()));
}
return dependencies;
}
}
7.1.4 Transform核心源码实现
// ShadowTransform.java - Transform入口完整实现
public class ShadowTransform extends Transform {
@Override
public String getName() {
return "shadowTransform";
}
@Override
public Set<QualifiedContent.ContentType> getInputTypes() {
return Collections.singleton(QualifiedContent.DefaultContentType.CLASSES);
}
@Override
public Set<? super QualifiedContent.Scope> getScopes() {
// 处理项目代码和依赖库
return Sets.immutableEnumSet(
QualifiedContent.Scope.PROJECT,
QualifiedContent.Scope.SUB_PROJECTS,
QualifiedContent.Scope.EXTERNAL_LIBRARIES
);
}
@Override
public boolean isIncremental() {
return true; // 支持增量编译
}
@Override
public void transform(TransformInvocation invocation)
throws TransformException, InterruptedException, IOException {
// 遍历所有输入文件(包括主工程和依赖库)
for (TransformInput input : invocation.getInputs()) {
// 处理目录输入(主工程代码)
for (DirectoryInput dirInput : input.getDirectoryInputs()) {
processDirectory(dirInput.getFile(),
invocation.getOutputProvider());
}
// 处理JAR输入(依赖库)
for (JarInput jarInput : input.getJarInputs()) {
processJar(jarInput.getFile(),
invocation.getOutputProvider());
}
}
}
private void processDirectory(File inputDir, TransformOutputProvider provider) {
// 遍历所有.class文件
Files.walkFileTree(inputDir.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (file.toString().endsWith(".class")) {
// 关键转换:修改类的继承关系
byte[] transformed = transformClass(Files.readAllBytes(file));
// 写入输出目录
writeTransformedClass(transformed, file, provider);
}
return FileVisitResult.CONTINUE;
}
});
}
}
7.2 字节码插桩详细实现
7.2.1 组件类替换实现
实现效果:将插件Activity的父类从Activity改为ShadowActivity
// ActivityTransform.java - 组件类替换核心实现
public class ActivityTransform implements SpecificTransform {
@Override
public boolean isNeedTransform(CtClass ctClass) {
try {
// 递归检查继承链
CtClass superClass = ctClass.getSuperclass();
while (superClass != null) {
if ("android.app.Activity".equals(superClass.getName())) {
return true;
}
superClass = superClass.getSuperclass();
}
} catch (NotFoundException e) {
// 忽略
}
return false;
}
@Override
public void transformClass(CtClass ctClass) throws Exception {
// 1. 修改父类
CtClass shadowActivity = ctClass.getClassPool()
.get("com.tencent.shadow.core.runtime.ShadowActivity");
ctClass.setSuperclass(shadowActivity);
// 2. 添加委托字段
CtField delegateField = new CtField(
ctClass.getClassPool()
.get("com.tencent.shadow.core.runtime.ShadowActivityDelegate"),
"mShadowDelegate",
ctClass
);
delegateField.setModifiers(Modifier.PRIVATE);
ctClass.addField(delegateField);
// 3. 修改构造方法
modifyConstructors(ctClass);
// 4. 修改生命周期方法
modifyLifecycleMethods(ctClass);
}
private void modifyConstructors(CtClass ctClass) throws Exception {
CtConstructor[] constructors = ctClass.getConstructors();
for (CtConstructor constructor : constructors) {
// 在构造方法中初始化委托字段
constructor.insertBeforeBody(
"if (mShadowDelegate == null) {" +
" mShadowDelegate = new com.tencent.shadow.core.runtime.ShadowActivityDelegate(this);" +
"}"
);
}
}
}
字节码变化对比:
// 转换前
.class public Lcom/example/PluginActivity;
.super Landroid/app/Activity; // ← 父类是系统Activity
// 转换后
.class public Lcom/example/PluginActivity;
.super Lcom/tencent/shadow/core/runtime/ShadowActivity; // ← 父类改为ShadowActivity
.field private mShadowDelegate Lcom/tencent/shadow/core/runtime/ShadowActivityDelegate;
7.2.2 系统API调用重定向实现
实现效果:插件调用getResources()时,实际执行Shadow.getPluginResources()
// ApiRedirector.java - API调用重定向
public class ApiRedirector extends MethodVisitor {
private static final Map<String, RedirectInfo> REDIRECT_MAP = new HashMap<>();
static {
// 资源相关方法重定向
REDIRECT_MAP.put("android/content/Context/getResources",
new RedirectInfo("com/tencent/shadow/core/runtime/Shadow",
"getPluginResources", true));
REDIRECT_MAP.put("android/content/Context/getAssets",
new RedirectInfo("com/tencent/shadow/core/runtime/Shadow",
"getPluginAssets", true));
REDIRECT_MAP.put("android/content/Context/getPackageManager",
new RedirectInfo("com/tencent/shadow/core/runtime/Shadow",
"getPluginPackageManager", true));
// 组件启动方法重定向
REDIRECT_MAP.put("android/content/Context/startActivity",
new RedirectInfo("com/tencent/shadow/core/runtime/Shadow",
"startActivityInPlugin", true));
REDIRECT_MAP.put("android/content/Context/startService",
new RedirectInfo("com/tencent/shadow/core/runtime/Shadow",
"startServiceInPlugin", true));
}
@Override
public void visitMethodInsn(int opcode, String owner, String name,
String desc, boolean itf) {
String key = owner + "/" + name;
RedirectInfo redirect = REDIRECT_MAP.get(key);
if (redirect != null) {
// 执行方法重定向
if (redirect.needsContext) {
injectContextParameter(); // 插入Context参数
}
super.visitMethodInsn(opcode, redirect.targetOwner,
redirect.targetName, desc, itf);
} else {
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
}
字节码变化对比:
// 转换前
invoke-virtual {p0}, Landroid/content/Context;->getResources()Landroid/content/res/Resources;
// 转换后
invoke-static {p0}, Lcom/tencent/shadow/core/runtime/Shadow;->getPluginResources(Landroid/content/Context;)Landroid/content/res/Resources;
7.2.3 生命周期增强实现
实现效果:在生命周期方法前后自动插入跟踪代码
// LifecycleEnhancer.java - 生命周期增强
public class LifecycleEnhancer {
public void enhanceLifecycleMethod(CtMethod method) throws Exception {
// 保存原始方法
CtMethod originalMethod = CtNewMethod.copy(method,
method.getDeclaringClass(), null);
originalMethod.setName(method.getName() + "$$Shadow");
method.getDeclaringClass().addMethod(originalMethod);
// 创建包装方法体
String newBody = buildWrappedMethodBody(method.getName());
method.setBody(newBody);
}
private String buildWrappedMethodBody(String methodName) {
return "{\n" +
" long startTime = System.currentTimeMillis();\n" +
" try {\n" +
" if (mShadowDelegate != null) {\n" +
" mShadowDelegate.before" + capitalize(methodName) + "();\n" +
" }\n" +
" " + methodName + "$$Shadow($$);\n" +
" if (mShadowDelegate != null) {\n" +
" mShadowDelegate.after" + capitalize(methodName) + "();\n" +
" }\n" +
" } catch (Throwable t) {\n" +
" ShadowCrashHandler.handle(t);\n" +
" throw t;\n" +
" } finally {\n" +
" long cost = System.currentTimeMillis() - startTime;\n" +
" if (cost > 100) {\n" +
" Log.w("Shadow", "Method " + "" + methodName + "" + " cost: " + cost);\n" +
" }\n" +
" }\n" +
"}";
}
}
字节码变化对比:
// 转换前
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// 转换后
protected void onCreate(Bundle savedInstanceState) {
long startTime = System.currentTimeMillis();
try {
if (mShadowDelegate != null) {
mShadowDelegate.beforeOnCreate();
}
onCreate$$Shadow(savedInstanceState); // 原始逻辑
if (mShadowDelegate != null) {
mShadowDelegate.afterOnCreate();
}
} catch (Throwable t) {
ShadowCrashHandler.handle(t);
throw t;
} finally {
long cost = System.currentTimeMillis() - startTime;
if (cost > 100) {
Log.w("Shadow", "Method onCreate cost: " + cost);
}
}
}
7.2.4 资源ID转换实现
实现效果:将资源ID常量替换为转换方法调用
// ResourceIdTransformer.java - 资源ID转换
public class ResourceIdTransformer extends MethodVisitor {
@Override
public void visitLdcInsn(Object value) {
// 检测资源ID常量
if (value instanceof Integer) {
int intValue = (Integer) value;
// 检查是否是资源ID (通常以0x7F开头)
if ((intValue & 0xFF000000) == 0x7F000000) {
// 替换为转换方法调用
mv.visitLdcInsn(intValue);
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"com/tencent/shadow/core/runtime/ShadowResourceConverter",
"convertResourceId",
"(I)I", false);
return;
}
}
super.visitLdcInsn(value);
}
}
字节码变化对比:
// 转换前
const v0, 0x7f09001c // R.layout.activity_main
// 转换后
const v0, 0x7f09001c
invoke-static {v0}, Lcom/tencent/shadow/core/runtime/ShadowResourceConverter;->convertResourceId(I)I
move-result v0
7.3 Javassist字节码编辑工具
7.3.1 Javassist vs ASM对比
| 特性 | Javassist | ASM |
|---|---|---|
| API层次 | 源码级别(字符串操作) | 字节码级别(指令操作) |
| 学习曲线 | 平缓,易于上手 | 陡峭,需要理解字节码指令 |
| 开发效率 | 高,代码简洁 | 低,需要处理细节 |
| 灵活性 | 中等 | 极高 |
| 性能 | 稍低 | 极高 |
| 适用场景 | 简单转换、AOP | 复杂优化、编译器 |
7.3.2 Javassist核心应用示例
// BroadcastReceiver转换示例
public class ReceiverSupportTransform {
public void transformBroadcastReceivers(CtClass receiver) {
try {
// 获取原始的onReceive方法
CtMethod originalOnReceive = receiver.getDeclaredMethod(
"onReceive",
new CtClass[] {
classPool.get("android.content.Context"),
classPool.get("android.content.Intent")
}
);
// 重命名原始方法
originalOnReceive.setName("onReceive$$Shadow");
// 创建新的onReceive方法
CtMethod newOnReceive = CtNewMethod.make(
generateNewOnReceiveCode(), receiver);
receiver.addMethod(newOnReceive);
} catch (NotFoundException e) {
// 没有重写onReceive方法,无需转换
}
}
private String generateNewOnReceiveCode() {
return "public void onReceive(android.content.Context context, " +
"android.content.Intent intent) {\n" +
" // 获取插件ClassLoader\n" +
" java.lang.ClassLoader cl = getClass().getClassLoader();\n" +
" // 获取插件Context\n" +
" android.content.Context pluginContext = \n" +
" com.tencent.shadow.core.runtime.PluginPartInfoManager\n" +
" .getPluginInfo(cl).getApplication();\n" +
" // 修正Intent的ClassLoader\n" +
" android.content.Intent newIntent = new android.content.Intent(intent);\n" +
" newIntent.setExtrasClassLoader(cl);\n" +
" // 调用原始方法\n" +
" onReceive$$Shadow(pluginContext, newIntent);\n" +
"}";
}
}
7.3.3 Javassist性能优化
// 优化的Javassist处理器
public class OptimizedJavassistProcessor {
private ClassPool sharedClassPool; // 共享ClassPool
private final Map<String, CtClass> classCache; // 类缓存
private final ExecutorService executor; // 并行处理
public OptimizedJavassistProcessor() {
// 使用共享的ClassPool,避免重复创建
this.sharedClassPool = new ClassPool(true);
this.classCache = new ConcurrentHashMap<>();
this.executor = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors());
// 预热ClassPool
warmupClassPool();
}
private void warmupClassPool() {
// 预加载常用类
String[] commonClasses = {
"android.app.Activity",
"android.app.Service",
"android.content.BroadcastReceiver",
"android.os.Bundle",
"android.content.Intent"
};
for (String className : commonClasses) {
try {
sharedClassPool.get(className);
} catch (NotFoundException e) {
// 忽略
}
}
}
public byte[] processClassOptimized(byte[] classBytes, String className) {
try {
// 使用缓存的CtClass
CtClass ctClass = classCache.computeIfAbsent(className, k -> {
try {
return sharedClassPool.makeClass(
new ByteArrayInputStream(classBytes));
} catch (IOException e) {
throw new RuntimeException(e);
}
});
// 并行处理
CompletableFuture<byte[]> future = CompletableFuture.supplyAsync(() -> {
try {
processClass(ctClass);
return ctClass.toBytecode();
} catch (Exception e) {
throw new RuntimeException(e);
}
}, executor);
return future.get(5, TimeUnit.SECONDS);
} catch (Exception e) {
throw new RuntimeException("Failed to process: " + className, e);
}
}
}
7.4 AOP在Shadow中的应用
7.4.1 AOP实现架构图
7.4.2 AOP实现模式示例
Before/After通知:
// 在Activity.onCreate()方法前后插入代码
public class ActivityLifecycleAspect {
public void weaveActivityLifecycle(CtClass activityClass) {
try {
CtMethod onCreate = activityClass.getDeclaredMethod(
"onCreate",
new CtClass[] { classPool.get("android.os.Bundle") }
);
// 前置通知 - 插入方法开始处
onCreate.insertBefore(
"android.util.Log.d("Shadow", "Activity onCreate开始: " + getClass().getName());"
);
// 后置通知 - 插入方法返回前
onCreate.insertAfter(
"android.util.Log.d("Shadow", "Activity onCreate结束: " + getClass().getName());"
);
} catch (Exception e) {
// 方法不存在或其他异常
}
}
}
环绕通知:
// 替换整个方法的实现,添加性能监控
public class MethodReplacementAspect {
public void replaceMethod(CtClass targetClass, String methodName) {
try {
CtMethod original = targetClass.getDeclaredMethod(methodName);
// 保存原始方法体
String originalBody = original.getMethodInfo().toString();
// 创建新方法体,包含性能监控
String newBody = "{\n" +
" long startTime = System.currentTimeMillis();\n" +
" try {\n" +
" // 调用原始方法逻辑\n" +
" $proceed($$);\n" +
" } finally {\n" +
" long cost = System.currentTimeMillis() - startTime;\n" +
" if (cost > 100) {\n" +
" android.util.Log.w("Shadow", \n" +
" "方法执行超时: " + "" + methodName + "" + ", 耗时: " + cost);\n" +
" }\n" +
" }\n" +
"}";
original.setBody(newBody);
} catch (Exception e) {
throw new TransformException("方法替换失败", e);
}
}
}
异常处理织入:
// 为所有方法添加统一的异常处理
public class ExceptionHandlingAspect {
public void addExceptionHandling(CtClass targetClass) {
CtMethod[] methods = targetClass.getDeclaredMethods();
for (CtMethod method : methods) {
if (!Modifier.isAbstract(method.getModifiers())) {
weaveExceptionHandler(method);
}
}
}
private void weaveExceptionHandler(CtMethod method) {
try {
method.addCatch(
"{ \n" +
" com.tencent.shadow.core.runtime.ShadowCrashHandler.handle(e); \n" +
" throw e; \n" +
"}",
classPool.get("java.lang.Throwable")
);
} catch (CannotCompileException e) {
// 某些方法无法添加catch块(如构造方法)
}
}
}
7.5 "零Hook"实现原理
7.5.1 "零Hook"架构图
7.5.2 类继承链重构实现
// 通过修改字节码改变类的继承关系
public class InheritanceTransformer {
public byte[] transformInheritance(byte[] classBytes, String className) {
ClassReader cr = new ClassReader(classBytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new ClassVisitor(Opcodes.ASM7, cw) {
@Override
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
// 检测并替换父类
String newSuperName = mapSuperClass(superName);
// 修改父类引用
super.visit(version, access, name, signature, newSuperName, interfaces);
}
};
cr.accept(cv, 0);
return cw.toByteArray();
}
private String mapSuperClass(String originalSuper) {
// 映射系统组件到Shadow组件
Map<String, String> superClassMap = new HashMap<>();
superClassMap.put("android/app/Activity",
"com/tencent/shadow/core/runtime/ShadowActivity");
superClassMap.put("android/app/Service",
"com/tencent/shadow/core/runtime/ShadowService");
superClassMap.put("android/content/BroadcastReceiver",
"com/tencent/shadow/core/runtime/ShadowBroadcastReceiver");
return superClassMap.getOrDefault(originalSuper, originalSuper);
}
}
7.5.3 "零Hook"优势与挑战
优势:
| 优势 | 说明 |
|---|---|
| 系统兼容性好 | 无需适配不同Android版本,不依赖系统私有API |
| 安全性高 | 不修改系统框架,避免安全风险和系统检测 |
| 稳定性强 | 不会因系统升级导致功能失效 |
| 维护成本低 | 只需维护Shadow框架,无需关注系统变化 |
关键技术挑战与解决方案:
| 挑战 | 解决方案 |
|---|---|
| 类的初始化顺序 | 通过静态代码块注入确保Shadow框架先初始化 |
| 资源ID冲突 | 动态分配packageId,编译期资源ID转换 |
| 系统API变更 | 使用适配层,针对不同API Level提供不同实现 |
| 性能开销 | 缓存转换结果,优化热路径代码 |
7.6 调试与研究实践指南
7.6.1 查看字节码差异的正确姿势
步骤详解:
# 1. 构建两种APK
./gradlew :sample-normal-app:assembleDebug # 正常安装的APK
./gradlew :sample-plugin-app:assembleDebug # 插件APK(会经过Transform)
# 2. 在Android Studio中打开APK文件
# - 将APK文件拖入Android Studio
# - 或者使用 File -> Open 选择APK
# 3. 分析字节码
# - 双击dex文件
# - 选择目标类
# - 右键 -> Show Bytecode
7.6.2 调试Transform的断点设置
// 在Transform代码中设置断点
public class ShadowTransform extends ClassTransform {
public void transform(TransformInvocation invocation) {
// 在这里设置断点,检查invocation内容
System.out.println("Transform started");
// 设置条件断点:只处理特定类
if (className.contains("TestActivity")) {
System.out.println("Processing: " + className);
}
// 打印调试信息
for (TransformInput input : invocation.getInputs()) {
for (DirectoryInput dirInput : input.getDirectoryInputs()) {
File dir = dirInput.getFile();
System.out.println("Processing dir: " + dir.getAbsolutePath());
}
}
}
}
7.6.3 Javassist调试辅助工具
// 调试辅助类
public class DebugHelper {
public static void debugClass(CtClass ctClass) {
System.out.println("Class: " + ctClass.getName());
System.out.println("Superclass: " + ctClass.getSuperclass().getName());
// 输出所有方法
CtMethod[] methods = ctClass.getDeclaredMethods();
for (CtMethod method : methods) {
System.out.println(" Method: " + method.getName());
System.out.println(" Signature: " + method.getSignature());
}
// 输出所有字段
CtField[] fields = ctClass.getDeclaredFields();
for (CtField field : fields) {
System.out.println(" Field: " + field.getName());
}
}
// 保存转换后的字节码到文件
public static void saveBytecode(CtClass ctClass, String path) {
try {
byte[] bytecode = ctClass.toBytecode();
FileOutputStream fos = new FileOutputStream(path);
fos.write(bytecode);
fos.close();
System.out.println("Saved to: " + path);
} catch (Exception e) {
e.printStackTrace();
}
}
}
7.6.4 性能优化建议
// 1. 缓存ClassPool
public class ClassPoolCache {
private static ClassPool sharedPool;
public static ClassPool getClassPool() {
if (sharedPool == null) {
sharedPool = new ClassPool(true);
AndroidClassPoolBuilder.build(sharedPool);
}
return sharedPool;
}
}
// 2. 并行处理
public class ParallelTransformer {
private ExecutorService executor =
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public void transformParallel(List<CtClass> classes) {
List<Future<Void>> futures = new ArrayList<>();
for (CtClass ctClass : classes) {
futures.add(executor.submit(() -> {
transformClass(ctClass);
return null;
}));
}
// 等待所有任务完成
for (Future<Void> future : futures) {
future.get();
}
}
}
7.7 总结
Shadow通过Transform机制和字节码插桩实现了完整的插件化能力:
Transform处理的核心功能:
- 组件继承关系转换:将Activity/Service等替换为Shadow组件
- 组件信息收集:自动收集插件中的四大组件
- 系统API重定向:将系统API调用重定向到Shadow实现
字节码插桩实现的功能:
- 生命周期增强:在生命周期方法中添加跟踪和初始化代码
- 异常处理增强:为所有方法添加统一的异常处理
- 字段注入:注入Shadow运行时所需的委托对象
- 构造方法增强:初始化注入的字段
- 资源ID转换:动态转换资源ID
- 性能监控:自动注入性能统计代码
- 日志注入:自动添加方法入口/出口日志
八、Shadow安全与底层实现
8.1 安全机制的实现
8.1.1 白名单机制的设计与作用
设计原理: Shadow采用多层白名单机制,从宿主、插件、API、权限四个维度进行安全控制。这种设计基于最小权限原则,确保只有经过验证的实体才能进行相关操作。
多层白名单架构:
// WhiteListManager.java - 白名单管理器
public class WhiteListManager {
// 1. 宿主白名单 - 控制哪些宿主可以加载插件
private final Set<HostInfo> hostWhiteList = new ConcurrentHashSet<>();
// 2. 插件白名单 - 控制哪些插件可以运行
private final Map<String, PluginWhiteInfo> pluginWhiteList = new ConcurrentHashMap<>();
// 3. API白名单 - 控制插件可以调用哪些系统API
private final Map<String, ApiPermission> apiWhiteList = new ConcurrentHashMap<>();
// 4. 权限白名单 - 控制插件可以申请哪些权限
private final Set<String> permissionWhiteList = new ConcurrentHashSet<>();
// 宿主验证
public boolean verifyHost(String hostPackageName, String hostSignature) {
return hostWhiteList.stream()
.anyMatch(host -> host.matches(hostPackageName, hostSignature));
}
// 插件验证
public boolean verifyPlugin(String pluginId, String pluginVersion,
String pluginSignature) {
PluginWhiteInfo info = pluginWhiteList.get(pluginId);
return info != null && info.isAllowed(pluginVersion, pluginSignature);
}
// API调用检查
public boolean checkApiPermission(String apiName, String pluginId) {
ApiPermission permission = apiWhiteList.get(apiName);
return permission != null && permission.isAllowed(pluginId);
}
// 权限申请检查
public boolean checkPermission(String permission, String pluginId) {
if (!permissionWhiteList.contains(permission)) {
return false;
}
// 进一步检查插件的权限申请记录
return checkPermissionHistory(pluginId, permission);
}
// 动态更新白名单
public void updateWhiteList(WhiteListConfig config) {
// 支持热更新白名单配置
hostWhiteList.clear();
hostWhiteList.addAll(config.getHosts());
pluginWhiteList.clear();
config.getPlugins().forEach(p ->
pluginWhiteList.put(p.getId(), p));
// 应用更新
applyWhiteListChanges();
}
}
白名单配置格式:
{
"version": "1.0",
"hosts": [
{
"packageName": "com.tencent.shadow.host",
"signature": "308202...",
"validUntil": "2024-12-31"
}
],
"plugins": [
{
"id": "finance-module",
"allowedVersions": ["1.0.0", "1.1.0", "2.0.0"],
"signature": "308203...",
"apis": ["getResources", "startActivity", "getSystemService"]
}
],
"apis": [
{
"name": "android.app.ActivityManager/getRunningTasks",
"minSdkVersion": 21,
"maxSdkVersion": 30,
"allowedPlugins": ["monitor-plugin"]
}
],
"permissions": [
"android.permission.INTERNET",
"android.permission.ACCESS_NETWORK_STATE"
]
}
白名单更新机制:
// 支持动态更新白名单
public class DynamicWhiteListUpdater {
public void fetchAndUpdateWhiteList() {
// 从安全服务器获取最新白名单
WhiteListConfig config = fetchWhiteListFromServer();
// 验证配置的签名
if (!verifyConfigSignature(config)) {
return;
}
// 应用白名单更新
whiteListManager.updateWhiteList(config);
// 重新验证已加载的插件
revalidateLoadedPlugins();
}
private void revalidateLoadedPlugins() {
pluginManager.getLoadedPlugins().forEach(plugin -> {
if (!whiteListManager.verifyPlugin(
plugin.getId(), plugin.getVersion(), plugin.getSignature())) {
// 从白名单中移除,卸载插件
pluginManager.unloadPlugin(plugin.getId());
}
});
}
}
8.1.2 为何要求插件与宿主包名一致
之前的实战中报了错,现在我们看看原理
设计原理的核心: 要求插件与宿主包名一致是Shadow设计的核心约束,其根本目的是让插件代码成为宿主代码的"逻辑一部分",从而避免使用私有API。这种设计基于Android系统的安全模型和类加载机制。
技术深层次原因:
- 类加载器委派模型的一致性:
// 包名一致确保类加载器委派模型正常工作
public class PluginClassLoader extends PathClassLoader {
@Override
protected Class<?> loadClass(String name, boolean resolve) {
synchronized (getClassLoadingLock(name)) {
// 优先从已加载类中查找
Class<?> c = findLoadedClass(name);
if (c != null) {
return c;
}
// 相同包名的类优先委派给父加载器(宿主)
if (name.startsWith(hostPackageName + ".")) {
try {
return parent.loadClass(name);
} catch (ClassNotFoundException e) {
// 父加载器找不到,继续向下查找
}
}
// 自己加载
return findClass(name);
}
}
}
- 资源命名空间的统一:
// 资源ID分配机制
public class ResourceIdAllocator {
// Android资源ID结构:0xPPTTEEEE
// PP: package ID (0x01-0x7F)
// TT: type ID (0x01-0x1F)
// EEEE: entry ID
public int allocateResourceId(int pluginResId, String pluginPackage) {
// 如果插件与宿主包名相同,可以共享package ID
if (pluginPackage.equals(hostPackageName)) {
// 使用宿主的package ID (通常是0x7F)
int hostPackageId = 0x7F;
int typeId = (pluginResId >> 16) & 0xFF;
int entryId = pluginResId & 0xFFFF;
return (hostPackageId << 24) | (typeId << 16) | entryId;
} else {
// 包名不同,需要分配新的package ID
int newPackageId = allocateNewPackageId();
return remapResourceId(pluginResId, newPackageId);
}
}
}
- 权限继承与安全边界:
// 权限检查机制
public class PermissionChecker {
public boolean checkPermission(String permission, Context context) {
// Android系统检查权限时基于包名
int result = context.checkPermission(permission,
android.os.Process.myPid(),
android.os.Process.myUid());
// 如果插件包名与宿主不同,权限检查会失败
// 因为插件的UID与宿主不同,且没有单独声明权限
return result == PackageManager.PERMISSION_GRANTED;
}
}
包名一致的实际优势:
- 避免使用私有API:
// 插件中的代码
public class PluginActivity extends Activity {
// 由于包名相同,这个Activity被系统视为宿主的一部分
// 因此可以正常使用所有public API
}
- 简化四大组件注册:
<!-- 插件AndroidManifest.xml -->
<activity android:name=".PluginActivity"
android:exported="false">
<!-- 由于包名相同,组件自动注册到宿主进程 -->
</activity>
- 无缝的数据共享:
// 插件可以访问宿主的数据
public class PluginDataAccess {
public void accessHostData() {
// 使用宿主包名访问SharedPreferences
SharedPreferences sp = context.getSharedPreferences(
"host_data", Context.MODE_PRIVATE);
// 可以直接访问,因为包名相同
}
}
- ContentProvider的透明使用:
// 插件可以直接使用宿主的ContentProvider
public class PluginContentResolver {
public Cursor queryHostData() {
// URI基于包名构建
Uri uri = Uri.parse("content://" + hostPackageName + "/data");
return context.getContentResolver().query(uri, null, null, null, null);
}
}
例外情况处理: 虽然要求包名一致,但Shadow也提供了特殊情况下的解决方案:
// 对于必须使用不同包名的场景
public class PackageNameAdapter {
public Context createAdaptedContext(Context original, String targetPackageName) {
// 创建包装Context,模拟目标包名
return new ContextWrapper(original) {
@Override
public String getPackageName() {
return targetPackageName;
}
@Override
public PackageManager getPackageManager() {
return new PackageManagerWrapper(super.getPackageManager()) {
@Override
public PackageInfo getPackageInfo(String packageName, int flags) {
if (packageName.equals(targetPackageName)) {
// 返回模拟的PackageInfo
return createVirtualPackageInfo(targetPackageName);
}
return super.getPackageInfo(packageName, flags);
}
};
}
};
}
}
总结: 包名一致的要求是Shadow设计中的关键决策,它简化了插件化实现的复杂度,提高了系统兼容性和安全性。虽然这带来了一定的限制,但通过Shadow提供的完整工具链和运行时支持,开发者可以在这个约束下构建强大的插件化应用。
8.5 内存占用对比:原生加载 vs 插件加载
设计原理: Shadow通过精细化内存管理和资源共享机制,在保证功能完整性的同时,尽可能减少插件加载带来的内存开销。
内存分析工具:
// MemoryAnalyzer.java - 内存使用分析
public class MemoryAnalyzer {
public MemoryUsage analyzeMemoryUsage(String pluginId) {
Runtime runtime = Runtime.getRuntime();
// 收集内存信息
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
long maxMemory = runtime.maxMemory();
// Native内存信息
long nativeHeapSize = Debug.getNativeHeapSize();
long nativeHeapAllocated = Debug.getNativeHeapAllocatedSize();
long nativeHeapFree = Debug.getNativeHeapFreeSize();
// 类加载统计
int loadedClassCount = countLoadedClasses();
int instanceCount = countInstances();
return new MemoryUsage(
totalMemory, freeMemory, usedMemory, maxMemory,
nativeHeapSize, nativeHeapAllocated, nativeHeapFree,
loadedClassCount, instanceCount
);
}
// 对比原生加载和插件加载
public ComparisonResult compareLoadingMethods(File apkFile) {
// 原生加载基准测试
MemoryUsage nativeUsage = measureNativeLoading(apkFile);
// 插件加载测试
MemoryUsage pluginUsage = measurePluginLoading(apkFile);
// 计算差异
long memoryOverhead = pluginUsage.getUsedMemory() - nativeUsage.getUsedMemory();
float overheadPercentage = (float) memoryOverhead / nativeUsage.getUsedMemory() * 100;
return new ComparisonResult(
nativeUsage, pluginUsage,
memoryOverhead, overheadPercentage
);
}
}
内存优化策略:
- ClassLoader共享:
// 共享公共库的ClassLoader
public class SharedClassLoaderManager {
private final Map<String, ClassLoader> sharedClassLoaders = new ConcurrentHashMap<>();
public ClassLoader getSharedClassLoader(String libraryName) {
return sharedClassLoaders.computeIfAbsent(libraryName, name -> {
// 创建共享ClassLoader
return createSharedClassLoader(name);
});
}
private ClassLoader createSharedClassLoader(String libraryName) {
// 加载公共库,如support-v4, okhttp等
List<File> libraryFiles = findLibraryFiles(libraryName);
String dexPath = buildDexPath(libraryFiles);
return new DexClassLoader(
dexPath,
getOdexDir(libraryName),
getLibraryDir(libraryName),
getSystemClassLoader()
);
}
}
- 资源去重与共享:
// 资源去重管理器
public class ResourceDeduplicator {
public Resources deduplicateResources(Resources hostRes, Resources pluginRes) {
// 合并资源,去除重复项
Map<String, Integer> mergedResources = new HashMap<>();
// 收集宿主资源
collectResources(hostRes, mergedResources);
// 收集插件资源,跳过重复项
collectResources(pluginRes, mergedResources, true);
// 创建去重后的Resources
return createDeduplicatedResources(mergedResources);
}
private void collectResources(Resources res, Map<String, Integer> target,
boolean skipDuplicates) {
// 遍历所有资源
for (int i = 0; i < res.getResources().getTableLength(); i++) {
String resName = res.getResourceName(i);
int resId = res.getIdentifier(resName, null, null);
if (skipDuplicates && target.containsKey(resName)) {
// 跳过重复资源
continue;
}
target.put(resName, resId);
}
}
}
- 内存缓存优化:
// 智能内存缓存
public class SmartMemoryCache {
private final LruCache<String, CacheEntry> cache;
private final Map<String, AccessPattern> accessPatterns = new ConcurrentHashMap<>();
public SmartMemoryCache(int maxSize) {
this.cache = new LruCache<String, CacheEntry>(maxSize) {
@Override
protected int sizeOf(String key, CacheEntry value) {
return calculateSize(value);
}
@Override
protected void entryRemoved(boolean evicted,
String key,
CacheEntry oldValue,
CacheEntry newValue) {
// 根据访问模式决定是否写入磁盘缓存
if (shouldPersist(key)) {
persistToDisk(key, oldValue);
}
}
};
}
public void recordAccess(String key) {
AccessPattern pattern = accessPatterns.get(key);
if (pattern == null) {
pattern = new AccessPattern();
accessPatterns.put(key, pattern);
}
pattern.recordAccess(System.currentTimeMillis());
}
private boolean shouldPersist(String key) {
AccessPattern pattern = accessPatterns.get(key);
if (pattern == null) {
return false;
}
// 基于访问频率和最近访问时间决定是否持久化
return pattern.getAccessFrequency() > 0.1 &&
pattern.getLastAccessTime() > System.currentTimeMillis() - 24 * 60 * 60 * 1000;
}
}
实际内存对比数据:
| 指标 | 原生加载 | Shadow加载 | 开销 |
|---|---|---|---|
| Dex内存 | 5.2MB | 5.8MB | +12% |
| 资源内存 | 3.1MB | 3.5MB | +13% |
| Native内存 | 2.4MB | 2.7MB | +12% |
| 类实例数 | 1,245 | 1,310 | +5% |
| 总内存 | 10.7MB | 12.0MB | +12% |
优化建议:
- 延迟加载:非必要组件延迟到使用时加载
- 代码压缩:使用ProGuard/R8优化代码
- 资源精简:移除未使用资源
- Native库优化:按需加载Native库
8.6 核心类及其职责划分
整体架构:
Shadow框架核心类
├── 运行时层 (Runtime)
│ ├── ShadowRuntime # 运行时入口,管理插件环境
│ ├── PluginContext # 插件上下文,提供插件资源访问
│ └── ShadowApplication # 插件Application代理
├── 加载层 (Loader)
│ ├── PluginLoader # 插件加载器接口
│ ├── PluginClassLoader # 插件类加载器实现
│ └── ComponentManager # 组件管理器
├── 管理层 (Manager)
│ ├── PluginManager # 插件管理器接口
│ ├── PluginInfo # 插件信息封装
│ └── PluginLifecycle # 插件生命周期管理
└── 支撑层 (Support)
├── SecurityManager # 安全管理器
├── CommunicationManager # 通信管理器
└── ResourceManager # 资源管理器
核心类详解:
- ShadowRuntime - 运行时核心:
public class ShadowRuntime {
// 单例实例
private static ShadowRuntime sInstance;
// 初始化Shadow运行时
public static void init(Context hostContext) {
if (sInstance == null) {
sInstance = new ShadowRuntime(hostContext);
sInstance.setup();
}
}
// 创建插件Context
public Context createPluginContext(String pluginId) {
PluginInfo pluginInfo = pluginManager.getPluginInfo(pluginId);
return new PluginContextImpl(hostContext, pluginInfo);
}
// 获取当前插件Context
public Context getCurrentPluginContext() {
return ThreadLocalStorage.getPluginContext();
}
}
- PluginClassLoader - 类加载隔离:
public class PluginClassLoader extends DexClassLoader {
// 打破双亲委派,优先自己加载
@Override
protected Class<?> loadClass(String name, boolean resolve) {
// 已加载类
Class<?> clazz = findLoadedClass(name);
if (clazz != null) {
return clazz;
}
// 系统类委派给父加载器
if (name.startsWith("android.") || name.startsWith("java.")) {
return super.loadClass(name, resolve);
}
// 尝试自己加载
try {
clazz = findClass(name);
if (resolve) {
resolveClass(clazz);
}
return clazz;
} catch (ClassNotFoundException e) {
// 自己加载失败,委派给父加载器
return super.loadClass(name, resolve);
}
}
}
- ComponentManager - 组件管理:
public class ComponentManager {
// 组件注册表
private final Map<String, ComponentInfo> componentRegistry = new ConcurrentHashMap<>();
// 注册组件
public void registerComponent(ComponentInfo info) {
componentRegistry.put(info.getName(), info);
// 动态注册广播接收器
if (info instanceof BroadcastReceiverInfo) {
registerDynamicReceiver((BroadcastReceiverInfo) info);
}
}
// 查找组件
public ComponentInfo findComponent(String name) {
return componentRegistry.get(name);
}
// 启动Activity
public void startActivity(Context context, Intent intent) {
ComponentInfo target = findComponent(intent.getComponent().getClassName());
if (target != null) {
// 路由到正确的插件和组件
routeToPluginActivity(context, intent, target);
}
}
}
- PluginManager - 插件生命周期:
public interface PluginManager {
// 安装插件
PluginInfo installPlugin(File pluginFile);
// 加载插件
Plugin loadPlugin(String pluginId);
// 卸载插件
boolean unloadPlugin(String pluginId);
// 获取插件信息
PluginInfo getPluginInfo(String pluginId);
// 获取已加载插件
List<Plugin> getLoadedPlugins();
}
- ResourceManager - 资源管理:
public class ResourceManager {
// 资源ID映射表
private final Map<Integer, Integer> resourceIdMap = new ConcurrentHashMap<>();
// 合并资源
public Resources mergeResources(Resources hostRes, Resources pluginRes) {
// 收集所有资源
Map<String, Integer> allResources = collectAllResources(hostRes, pluginRes);
// 分配新的资源ID
Map<String, Integer> allocatedResources = allocateResourceIds(allResources);
// 创建映射表
buildResourceIdMap(pluginRes, allocatedResources);
// 创建合并后的Resources
return createMergedResources(hostRes, allocatedResources);
}
// 转换资源ID
public int convertResourceId(int pluginResId) {
Integer hostResId = resourceIdMap.get(pluginResId);
return hostResId != null ? hostResId : pluginResId;
}
}
类协作流程:
// 典型的使用流程
public class PluginLauncher {
public void launchPluginActivity(Context hostContext, String pluginId,
String activityName) {
// 1. 获取PluginManager
PluginManager pluginManager = PluginManager.getInstance(hostContext);
// 2. 加载插件(如果未加载)
Plugin plugin = pluginManager.getLoadedPlugin(pluginId);
if (plugin == null) {
plugin = pluginManager.loadPlugin(pluginId);
}
// 3. 创建插件Context
Context pluginContext = ShadowRuntime.createPluginContext(pluginId);
// 4. 创建Intent
Intent intent = new Intent(pluginContext, Class.forName(activityName));
// 5. 通过ComponentManager路由
ComponentManager.getInstance().startActivity(hostContext, intent);
}
}
设计模式应用:
- 工厂模式 - 创建各种组件实例
- 代理模式 - 代理系统组件调用
- 装饰器模式 - 包装Context和Resources
- 观察者模式 - 监听插件生命周期
- 策略模式 - 不同的加载和优化策略
通过这样清晰的核心类划分,Shadow框架实现了高内聚、低耦合的架构设计,每个类都有明确的职责边界,便于维护和扩展。
九、宿主与插件通信过程
9.1 宿主向插件通信机制
设计原理: 宿主向插件通信主要通过接口调用、广播、Binder和消息总线四种方式实现。Shadow采用统一的服务发现和调用机制,使宿主能够透明地调用插件功能。
核心实现:
- 服务接口调用(推荐方式):
// 定义通信接口(放在共享模块中)
public interface IPluginService {
String getPluginVersion();
void executeTask(String taskName, Bundle params);
Bundle queryData(String query);
}
// 宿主端调用
public class HostServiceInvoker {
public void callPluginService(String pluginId, String serviceName) {
// 获取插件管理器
PluginManager pluginManager = PluginManager.getInstance(context);
// 获取插件
Plugin plugin = pluginManager.getLoadedPlugin(pluginId);
if (plugin == null) {
throw new PluginNotLoadedException(pluginId);
}
// 通过插件的ClassLoader加载服务类
Class<?> serviceClass = plugin.getClassLoader()
.loadClass(serviceName);
// 获取服务实例(约定单例模式)
Method getInstance = serviceClass.getMethod("getInstance");
Object serviceInstance = getInstance.invoke(null);
// 转换为接口
IPluginService pluginService = (IPluginService) serviceInstance;
// 调用接口方法
String version = pluginService.getPluginVersion();
pluginService.executeTask("syncData", new Bundle());
}
}
- 广播通信:
// 宿主发送广播到插件
public class HostBroadcastSender {
public void sendBroadcastToPlugin(String pluginId, String action, Bundle extras) {
Intent intent = new Intent(action);
if (extras != null) {
intent.putExtras(extras);
}
// 设置目标插件标识
intent.putExtra(Constants.EXTRA_TARGET_PLUGIN, pluginId);
// 通过Shadow的广播代理发送
ShadowBroadcastProxy.sendBroadcast(context, intent);
}
}
// Shadow广播代理
public class ShadowBroadcastProxy {
public static void sendBroadcast(Context context, Intent intent) {
// 检查是否发送给特定插件
String targetPlugin = intent.getStringExtra(Constants.EXTRA_TARGET_PLUGIN);
if (targetPlugin != null) {
// 定向发送到指定插件
sendToSpecificPlugin(context, intent, targetPlugin);
} else {
// 广播到所有已加载插件
broadcastToAllPlugins(context, intent);
}
}
private static void sendToSpecificPlugin(Context context, Intent intent,
String pluginId) {
// 获取插件的PluginContext
Context pluginContext = ShadowRuntime.getPluginContext(pluginId);
if (pluginContext != null) {
// 使用插件的Context发送广播
pluginContext.sendBroadcast(intent);
}
}
}
- Binder跨进程通信:
// 宿主端的Binder服务
public class HostBinderService extends Service {
private final Map<String, IBinder> pluginBinders = new ConcurrentHashMap<>();
@Override
public IBinder onBind(Intent intent) {
return new HostBinderStub();
}
private class HostBinderStub extends IHostService.Stub {
@Override
public void registerPluginBinder(String pluginId, IBinder binder) {
pluginBinders.put(pluginId, binder);
}
@Override
public Bundle callPluginMethod(String pluginId, String method,
Bundle params) {
IBinder pluginBinder = pluginBinders.get(pluginId);
if (pluginBinder != null) {
IPluginService pluginService =
IPluginService.Stub.asInterface(pluginBinder);
return pluginService.invoke(method, params);
}
return null;
}
}
}
- 消息总线通信:
// 统一的消息总线
public class ShadowMessageBus {
private static final Map<String, List<MessageHandler>> handlers =
new ConcurrentHashMap<>();
// 注册消息处理器
public static void register(String messageType, MessageHandler handler) {
handlers.computeIfAbsent(messageType, k -> new CopyOnWriteArrayList<>())
.add(handler);
}
// 发送消息
public static void post(Message message) {
List<MessageHandler> typeHandlers = handlers.get(message.getType());
if (typeHandlers != null) {
for (MessageHandler handler : typeHandlers) {
// 在正确的线程执行
executeOnTargetThread(handler, message);
}
}
}
// 定向发送到插件
public static void postToPlugin(String pluginId, Message message) {
// 添加插件标识
message.setTargetPlugin(pluginId);
// 路由到对应插件
MessageRouter.routeToPlugin(pluginId, message);
}
}
9.2 插件向宿主通信机制
设计原理: 插件向宿主通信主要通过宿主提供的服务接口、回调机制、广播和共享存储实现。Shadow确保插件在安全边界内与宿主交互。
核心实现:
- 宿主服务接口调用:
// 宿主提供给插件的服务接口
public interface IHostService {
String getHostVersion();
Bundle getHostConfig(String key);
void executeOnHost(String task, Bundle params, HostCallback callback);
boolean requestPermission(String permission);
}
// 宿主服务实现
public class HostServiceImpl implements IHostService {
@Override
public String getHostVersion() {
return BuildConfig.VERSION_NAME;
}
@Override
public Bundle getHostConfig(String key) {
// 从宿主配置中读取
return ConfigManager.getInstance().getConfig(key);
}
@Override
public void executeOnHost(String task, Bundle params, HostCallback callback) {
// 在宿主主线程执行
new Handler(Looper.getMainLooper()).post(() -> {
try {
Bundle result = executeTask(task, params);
if (callback != null) {
callback.onSuccess(result);
}
} catch (Exception e) {
if (callback != null) {
callback.onError(e);
}
}
});
}
}
// 插件中获取宿主服务
public class PluginHostServiceAccessor {
public static IHostService getHostService(Context pluginContext) {
// 通过ShadowRuntime获取宿主服务
return ShadowRuntime.getService(IHostService.class);
}
public void callHostServiceFromPlugin() {
IHostService hostService = getHostService(context);
// 调用宿主服务
String version = hostService.getHostVersion();
Bundle params = new Bundle();
params.putString("key", "value");
hostService.executeOnHost("sync", params, new HostCallback() {
@Override
public void onSuccess(Bundle result) {
// 处理成功结果
}
@Override
public void onError(Throwable error) {
// 处理错误
}
});
}
}
- 回调机制:
// 异步回调接口
public interface PluginCallback {
void onSuccess(Bundle result);
void onError(int code, String message);
void onProgress(int progress);
}
// 宿主执行插件请求
public class HostRequestHandler {
public void handlePluginRequest(String pluginId, Bundle request,
PluginCallback callback) {
// 验证插件权限
if (!checkPluginPermission(pluginId, request)) {
callback.onError(403, "Permission denied");
return;
}
// 异步处理请求
executorService.submit(() -> {
try {
Bundle result = processRequest(request);
callback.onSuccess(result);
} catch (Exception e) {
callback.onError(500, e.getMessage());
}
});
}
}
- 共享存储通信:
// 通过SharedPreferences共享数据
public class SharedStorageCommunicator {
// 宿主向插件写数据
public void hostWriteToPlugin(String pluginId, String key, String value) {
// 使用插件包名作为存储标识
String prefName = "shadow_shared_" + pluginId;
SharedPreferences sp = hostContext.getSharedPreferences(prefName,
Context.MODE_PRIVATE);
sp.edit().putString(key, value).apply();
// 通知插件数据更新
notifyPluginDataChanged(pluginId, key);
}
// 插件读取宿主数据
public String pluginReadFromHost(String key) {
// 插件通过宿主Context访问共享存储
Context hostContext = ShadowRuntime.getHostContext();
SharedPreferences sp = hostContext.getSharedPreferences(
"host_shared", Context.MODE_PRIVATE);
return sp.getString(key, null);
}
}
9.3 插件之间的通信机制
设计原理: 插件间通信采用基于消息总线的发布-订阅模式,结合服务发现机制。Shadow确保插件间通信的安全性和隔离性。
核心实现:
- 消息总线通信:
// 插件间消息总线
public class InterPluginMessageBus {
private final MessageRouter messageRouter;
private final Map<String, PluginMessageHandler> handlers;
// 发布消息到其他插件
public void publish(String pluginId, Message message) {
// 设置发送方
message.setSender(pluginId);
// 路由消息
if (message.getTargetPlugin() != null) {
// 定向发送
messageRouter.routeToPlugin(message.getTargetPlugin(), message);
} else {
// 广播到所有插件
messageRouter.broadcastToPlugins(message);
}
}
// 订阅消息
public void subscribe(String pluginId, String messageType,
PluginMessageHandler handler) {
String key = pluginId + ":" + messageType;
handlers.put(key, handler);
// 注册到路由表
messageRouter.registerHandler(pluginId, messageType, handler);
}
}
// 消息路由器
public class MessageRouter {
private final Map<String, Map<String, PluginMessageHandler>> routingTable;
public void routeToPlugin(String targetPluginId, Message message) {
Map<String, PluginMessageHandler> pluginHandlers =
routingTable.get(targetPluginId);
if (pluginHandlers != null) {
PluginMessageHandler handler = pluginHandlers.get(message.getType());
if (handler != null) {
// 在目标插件的ClassLoader环境下执行
executeInPluginContext(targetPluginId, () -> {
handler.handleMessage(message);
});
}
}
}
}
- 服务发现与调用:
// 插件服务注册中心
public class PluginServiceRegistry {
private final Map<String, Map<String, ServiceInfo>> serviceRegistry;
// 注册服务
public void registerService(String pluginId, String serviceName,
ServiceInfo serviceInfo) {
serviceRegistry
.computeIfAbsent(pluginId, k -> new ConcurrentHashMap<>())
.put(serviceName, serviceInfo);
}
// 发现服务
public ServiceInfo discoverService(String pluginId, String serviceName) {
Map<String, ServiceInfo> pluginServices = serviceRegistry.get(pluginId);
return pluginServices != null ? pluginServices.get(serviceName) : null;
}
// 调用服务
public Bundle callService(String callerPluginId, String targetPluginId,
String serviceName, Bundle params) {
// 检查调用权限
if (!checkCallPermission(callerPluginId, targetPluginId, serviceName)) {
throw new SecurityException("Call permission denied");
}
ServiceInfo serviceInfo = discoverService(targetPluginId, serviceName);
if (serviceInfo == null) {
throw new ServiceNotFoundException(serviceName);
}
// 在目标插件环境中执行调用
return executeInPluginContext(targetPluginId, () -> {
return serviceInfo.invoke(params);
});
}
}
- 共享数据通信:
// 插件间共享数据管理器
public class InterPluginDataManager {
// 创建共享数据空间
public SharedDataSpace createSharedSpace(String spaceName,
List<String> pluginIds) {
// 验证插件权限
for (String pluginId : pluginIds) {
if (!checkPluginPermission(pluginId, "create_shared_space")) {
throw new SecurityException("Plugin " + pluginId +
" has no permission");
}
}
// 创建共享内存区域
SharedMemory sharedMemory = createSharedMemory(spaceName);
// 为每个插件创建访问接口
Map<String, SharedDataAccessor> accessors = new HashMap<>();
for (String pluginId : pluginIds) {
accessors.put(pluginId, createAccessor(pluginId, sharedMemory));
}
return new SharedDataSpace(spaceName, sharedMemory, accessors);
}
// 数据同步机制
public void syncData(String sourcePluginId, String targetPluginId,
String dataKey, Object data) {
// 检查同步权限
if (!checkSyncPermission(sourcePluginId, targetPluginId, dataKey)) {
return;
}
// 序列化数据
byte[] serialized = serializeData(data);
// 通过消息总线发送
Message message = new Message(Message.TYPE_DATA_SYNC);
message.setData(serialized);
message.setExtra("key", dataKey);
interPluginMessageBus.publish(sourcePluginId, message, targetPluginId);
}
}
- 事件驱动通信:
// 插件间事件系统
public class PluginEventSystem {
private final EventBus eventBus;
private final Map<String, EventChannel> channels;
// 创建事件通道
public EventChannel createChannel(String channelName,
EventDeliveryPolicy policy) {
EventChannel channel = new EventChannel(channelName, policy);
channels.put(channelName, channel);
return channel;
}
// 订阅事件
public Subscription subscribe(String pluginId, String channelName,
EventHandler handler) {
EventChannel channel = channels.get(channelName);
if (channel == null) {
throw new ChannelNotFoundException(channelName);
}
// 验证订阅权限
if (!channel.canSubscribe(pluginId)) {
throw new SecurityException("Subscribe permission denied");
}
return channel.subscribe(pluginId, handler);
}
// 发布事件
public void publish(String pluginId, String channelName, Event event) {
EventChannel channel = channels.get(channelName);
if (channel == null) {
throw new ChannelNotFoundException(channelName);
}
// 验证发布权限
if (!channel.canPublish(pluginId)) {
throw new SecurityException("Publish permission denied");
}
// 设置事件源
event.setSource(pluginId);
// 发布事件
channel.publish(event);
}
}
通信安全机制:
// 通信安全验证器
public class CommunicationSecurityVerifier {
public boolean verifyCommunication(String source, String target,
String action, Bundle data) {
// 1. 黑白名单检查
if (!checkWhiteList(source, target)) {
return false;
}
// 2. 权限验证
if (!checkPermission(source, target, action)) {
return false;
}
// 3. 数据签名验证
if (!verifyDataSignature(source, data)) {
return false;
}
// 4. 频率限制检查
if (!checkRateLimit(source, action)) {
return false;
}
// 5. 数据大小限制
if (!checkDataSize(data)) {
return false;
}
return true;
}
private boolean checkPermission(String source, String target,
String action) {
// 查询权限配置
PermissionConfig config = permissionManager.getConfig(source, target);
// 检查action是否在允许列表中
return config.getAllowedActions().contains(action);
}
}
性能优化策略:
- 通信连接池:
// 复用通信连接
public class ConnectionPool {
private final Map<String, List<Connection>> pool;
public Connection getConnection(String pluginId) {
List<Connection> connections = pool.get(pluginId);
if (connections == null || connections.isEmpty()) {
return createNewConnection(pluginId);
}
// 获取空闲连接
return connections.remove(0);
}
public void returnConnection(String pluginId, Connection connection) {
List<Connection> connections = pool.computeIfAbsent(pluginId,
k -> new ArrayList<>());
connections.add(connection);
}
}
- 消息批处理:
// 批量发送消息
public class BatchMessageSender {
private final Map<String, List<Message>> batchBuffer;
public void sendBatch(String pluginId, List<Message> messages) {
// 合并消息
Message batchMessage = mergeMessages(messages);
// 批量发送
messageSender.send(pluginId, batchMessage);
}
private Message mergeMessages(List<Message> messages) {
// 合并消息体,减少通信次数
Bundle batchData = new Bundle();
for (int i = 0; i < messages.size(); i++) {
batchData.putBundle("msg_" + i, messages.get(i).getData());
}
return new Message(Message.TYPE_BATCH, batchData);
}
}
通过以上通信机制,Shadow实现了宿主与插件、插件与插件之间安全、高效、灵活的通信,支持复杂的业务场景和架构需求。
参考博客: juejin.cn/post/684490…