7. Android Shadow插件化原理深挖(下):Transform字节码插桩与“零Hook”的底层实现与宿主通信全流程

1 阅读27分钟

上篇博客已经分析了详细的源码和原理! 现在分析后面的这个部分!

Q1: 问题:“爱机密” 加密APk是否会影响插件化。

Shadow主要是在Transform期间对系统组件的子类进行字节码修改。如果“爱机密”的原理和这个由用途,就会受到影响。

7.0 Shadow Transform核心功能总览

整体架构流程图

1.png

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 工作流程时序图

2.png

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对比

特性JavassistASM
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实现架构图

3.png

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"架构图

4.png

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处理的核心功能:

  1. 组件继承关系转换:将Activity/Service等替换为Shadow组件
  2. 组件信息收集:自动收集插件中的四大组件
  3. 系统API重定向:将系统API调用重定向到Shadow实现

字节码插桩实现的功能:

  1. 生命周期增强:在生命周期方法中添加跟踪和初始化代码
  2. 异常处理增强:为所有方法添加统一的异常处理
  3. 字段注入:注入Shadow运行时所需的委托对象
  4. 构造方法增强:初始化注入的字段
  5. 资源ID转换:动态转换资源ID
  6. 性能监控:自动注入性能统计代码
  7. 日志注入:自动添加方法入口/出口日志

八、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 为何要求插件与宿主包名一致

之前的实战中报了错,现在我们看看原理

juejin.cn/editor/draf…

设计原理的核心: 要求插件与宿主包名一致是Shadow设计的核心约束,其根本目的是让插件代码成为宿主代码的"逻辑一部分",从而避免使用私有API。这种设计基于Android系统的安全模型和类加载机制。

技术深层次原因

  1. 类加载器委派模型的一致性
// 包名一致确保类加载器委派模型正常工作
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);
        }
    }
}
  1. 资源命名空间的统一
// 资源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);
        }
    }
}
  1. 权限继承与安全边界
// 权限检查机制
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;
    }
}

包名一致的实际优势

  1. 避免使用私有API
// 插件中的代码
public class PluginActivity extends Activity {
    // 由于包名相同,这个Activity被系统视为宿主的一部分
    // 因此可以正常使用所有public API
}
  1. 简化四大组件注册
<!-- 插件AndroidManifest.xml -->
<activity android:name=".PluginActivity" 
          android:exported="false">
    <!-- 由于包名相同,组件自动注册到宿主进程 -->
</activity>
  1. 无缝的数据共享
// 插件可以访问宿主的数据
public class PluginDataAccess {
    public void accessHostData() {
        // 使用宿主包名访问SharedPreferences
        SharedPreferences sp = context.getSharedPreferences(
            "host_data", Context.MODE_PRIVATE);
        // 可以直接访问,因为包名相同
    }
}
  1. 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
        );
    }
}

内存优化策略

  1. 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()
        );
    }
}
  1. 资源去重与共享
// 资源去重管理器
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);
        }
    }
}
  1. 内存缓存优化
// 智能内存缓存
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.2MB5.8MB+12%
资源内存3.1MB3.5MB+13%
Native内存2.4MB2.7MB+12%
类实例数1,2451,310+5%
总内存10.7MB12.0MB+12%

优化建议

  1. 延迟加载:非必要组件延迟到使用时加载
  2. 代码压缩:使用ProGuard/R8优化代码
  3. 资源精简:移除未使用资源
  4. Native库优化:按需加载Native库

8.6 核心类及其职责划分

整体架构

Shadow框架核心类
├── 运行时层 (Runtime)
│   ├── ShadowRuntime        # 运行时入口,管理插件环境
│   ├── PluginContext        # 插件上下文,提供插件资源访问
│   └── ShadowApplication    # 插件Application代理
├── 加载层 (Loader)
│   ├── PluginLoader         # 插件加载器接口
│   ├── PluginClassLoader    # 插件类加载器实现
│   └── ComponentManager     # 组件管理器
├── 管理层 (Manager)
│   ├── PluginManager        # 插件管理器接口
│   ├── PluginInfo           # 插件信息封装
│   └── PluginLifecycle      # 插件生命周期管理
└── 支撑层 (Support)
    ├── SecurityManager      # 安全管理器
    ├── CommunicationManager # 通信管理器
    └── ResourceManager      # 资源管理器

核心类详解

  1. 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();
    }
}
  1. 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);
        }
    }
}
  1. 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);
        }
    }
}
  1. PluginManager - 插件生命周期
public interface PluginManager {
    // 安装插件
    PluginInfo installPlugin(File pluginFile);
    
    // 加载插件
    Plugin loadPlugin(String pluginId);
    
    // 卸载插件
    boolean unloadPlugin(String pluginId);
    
    // 获取插件信息
    PluginInfo getPluginInfo(String pluginId);
    
    // 获取已加载插件
    List<Plugin> getLoadedPlugins();
}
  1. 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);
    }
}

设计模式应用

  1. 工厂模式 - 创建各种组件实例
  2. 代理模式 - 代理系统组件调用
  3. 装饰器模式 - 包装Context和Resources
  4. 观察者模式 - 监听插件生命周期
  5. 策略模式 - 不同的加载和优化策略

通过这样清晰的核心类划分,Shadow框架实现了高内聚、低耦合的架构设计,每个类都有明确的职责边界,便于维护和扩展。

九、宿主与插件通信过程

9.1 宿主向插件通信机制

设计原理: 宿主向插件通信主要通过接口调用、广播、Binder和消息总线四种方式实现。Shadow采用统一的服务发现和调用机制,使宿主能够透明地调用插件功能。

核心实现

  1. 服务接口调用(推荐方式)
// 定义通信接口(放在共享模块中)
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());
    }
}
  1. 广播通信
// 宿主发送广播到插件
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);
        }
    }
}
  1. 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;
        }
    }
}
  1. 消息总线通信
// 统一的消息总线
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确保插件在安全边界内与宿主交互。

核心实现

  1. 宿主服务接口调用
// 宿主提供给插件的服务接口
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) {
                // 处理错误
            }
        });
    }
}
  1. 回调机制
// 异步回调接口
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());
            }
        });
    }
}
  1. 共享存储通信
// 通过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确保插件间通信的安全性和隔离性。

核心实现

  1. 消息总线通信
// 插件间消息总线
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);
                });
            }
        }
    }
}
  1. 服务发现与调用
// 插件服务注册中心
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);
        });
    }
}
  1. 共享数据通信
// 插件间共享数据管理器
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);
    }
}
  1. 事件驱动通信
// 插件间事件系统
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);
    }
}

性能优化策略

  1. 通信连接池
// 复用通信连接
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);
    }
}
  1. 消息批处理
// 批量发送消息
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…

juejin.cn/post/728593…