Shadow源码超级大,而且是非常复杂,很多设计模式,跨进程, 类加载机制,ASM插桩,AOP
本文将剥离复杂的底层细节,聚焦于 Shadow 插件化框架的六大核心模块,深入剖析其实现原理:
-
📦 插件打包
- 探讨插件的构建流程与包结构规范。
-
⚡ 动态化能力
- 分析如何实现代码与资源的动态下发与更新。
-
🛠️ 插件管理
- 生命周期管理、版本控制及状态流转机制。
-
🚀 插件加载
- 核心加载流程:从文件读取到内存映射。
-
🖼️ Activity 加载
- 欺骗系统 AMS 的技巧与栈管理策略(Shadow 的核心亮点)。
-
🔜 后续预告
- 资源加载机制(Resource Loading)
- SO 库加载机制(Native Library Loading)
问题: Q1: Shadow是如何解决ClassLoader双亲委派带来的类冲突问题的?
Q2:插件的classLoader是怎么样的?
Q3:Loader和Runtime是干嘛的?
Q4: 插件框架本身的代码(如Loader、Runtime)也是动态加载的,而非打包在宿主里
Q5: 如果在插件中使用Flutter
一.Shadow整体架构与设计原则
简单图解: host--->manage----->下面的模块
Shadow 的三层架构(Loader / Runtime / Plugin)
各层核心职责:
-
Loader层 - 插件加载器
- 插件生命周期管理(加载/卸载)
- ClassLoader和Resources构造
- 组件信息解析与注册
-
Runtime层 - 运行时容器
- 代理系统组件调用
- 提供插件运行环境
- 资源ID映射与转发
-
Plugin层 - 业务插件
- 独立业务逻辑实现
- 无需关心插件化细节
- 透明运行在Shadow环境中
1.1 模块关系分析
PluginManager 的启动流程
核心源码分析:
java
// PluginManager.java - 核心接口
public interface PluginManager {
static PluginManager getInstance(Context context) {
return PluginManagerImpl.getInstance(context);
}
void init();
Plugin loadPlugin(String pluginId);
}
// PluginManagerImpl.java - 具体实现
public class PluginManagerImpl implements PluginManager {
private static PluginManagerImpl sInstance;
private Context mHostContext;
public static PluginManagerImpl getInstance(Context context) {
if (sInstance == null) {
synchronized (PluginManagerImpl.class) {
if (sInstance == null) {
sInstance = new PluginManagerImpl(context);
}
}
}
return sInstance;
}
@Override
public void init() {
// 1. 初始化Shadow运行时
ShadowRuntime.init(mHostContext);
// 2. 创建插件目录结构
createPluginDirectories();
// 3. 加载已安装插件信息
loadInstalledPluginsInfo();
// 4. 初始化组件管理器
ComponentManager.getInstance().init(mHostContext);
}
private void createPluginDirectories() {
// 创建插件存储目录
new File(mHostContext.getFilesDir(), "plugins").mkdirs();
new File(mHostContext.getFilesDir(), "odex").mkdirs();
new File(mHostContext.getFilesDir(), "lib").mkdirs();
}
}
启动流程时序图:
1.2 Manager加载Loader
核心源码分析:
java
// FastPluginManager.java - Loader加载器
public class FastPluginManager extends PluginManagerImpl {
private static final String LOADER_CLASS_NAME =
"com.tencent.shadow.core.loader.ShadowPluginLoader";
protected void loadPluginLoaderAndRuntime(String pluginId, Bundle extras) {
// 1. 获取Loader APK文件
File loaderApk = getLoaderApkFile();
// 2. 创建Loader ClassLoader
DexClassLoader loaderClassLoader = new DexClassLoader(
loaderApk.getAbsolutePath(),
odexDir.getAbsolutePath(),
libraryPath,
getClass().getClassLoader()
);
// 3. 动态加载Loader类
Class<?> loaderClass = loaderClassLoader.loadClass(LOADER_CLASS_NAME);
ShadowPluginLoader loader = (ShadowPluginLoader)
loaderClass.newInstance();
// 4. 初始化Loader
loader.init(createLoaderArgs(pluginId, extras));
// 5. 缓存Loader实例
mPluginLoaderMap.put(pluginId, loader);
// 6. 加载Runtime
loadPluginRuntime(pluginId, loader);
}
private void loadPluginRuntime(String pluginId, ShadowPluginLoader loader) {
// 1. 获取Runtime信息
PluginRuntimeInfo runtimeInfo = loader.getRuntimeInfo();
// 2. 创建Runtime ClassLoader
DexClassLoader runtimeClassLoader = new DexClassLoader(
runtimeInfo.getApkPath(),
runtimeOdexDir.getAbsolutePath(),
libraryPath,
loader.getClassLoader()
);
// 3. 加载Runtime类
ShadowRuntime runtime = (ShadowRuntime)
runtimeClassLoader.loadClass(runtimeInfo.getClassName())
.newInstance();
// 4. 初始化Runtime
runtime.init(createRuntimeArgs(pluginId));
// 5. 建立Loader-Runtime关联
loader.bindRuntime(runtime);
// 6. 缓存Runtime实例
mPluginRuntimeMap.put(pluginId, runtime);
}
}
Loader加载流程:
1.3 Loader 如何构造 ClassLoader 与 Resources
ClassLoader构造源码:
java
// PluginClassLoader.java - 插件ClassLoader
public class PluginClassLoader extends DexClassLoader {
private final String mPluginId;
private final ClassLoader mHostClassLoader;
public PluginClassLoader(String pluginId, String dexPath,
String optimizedDirectory, String libraryPath,
ClassLoader parent) {
super(dexPath, optimizedDirectory, libraryPath, parent);
mPluginId = pluginId;
mHostClassLoader = parent;
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// 1. 检查已加载类
Class<?> clazz = findLoadedClass(name);
if (clazz != null) return clazz;
// 2. 系统类委派给父加载器
if (name.startsWith("android.") || name.startsWith("java.")) {
return mHostClassLoader.loadClass(name);
}
// 3. Shadow框架类委派
if (name.startsWith("com.tencent.shadow.")) {
return mHostClassLoader.loadClass(name);
}
// 4. 尝试自己加载(打破双亲委派)
try {
clazz = findClass(name);
if (resolve) resolveClass(clazz);
return clazz;
} catch (ClassNotFoundException e) {
// 自己加载失败,委派给父加载器
}
// 5. 最终委派给父加载器
return super.loadClass(name, resolve);
}
}
Resources构造源码:
java
// PluginResources.java - 插件Resources包装器
public class PluginResources extends Resources {
private final Resources mHostResources;
private final Resources mPluginResources;
private final ResourceMapping mResourceMapping;
public PluginResources(Resources hostResources,
AssetManager pluginAssetManager,
ResourceMapping mapping) {
super(pluginAssetManager,
hostResources.getDisplayMetrics(),
hostResources.getConfiguration());
mHostResources = hostResources;
mResourceMapping = mapping;
mPluginResources = new Resources(pluginAssetManager,
hostResources.getDisplayMetrics(),
hostResources.getConfiguration());
}
@Override
public int getIdentifier(String name, String defType, String defPackage) {
// 先在插件资源中查找
int pluginResId = mPluginResources.getIdentifier(name, defType, defPackage);
if (pluginResId != 0) {
// 映射到宿主资源ID
return mResourceMapping.mapToHostId(pluginResId);
}
// 在宿主资源中查找
return mHostResources.getIdentifier(name, defType, defPackage);
}
@Override
public Drawable getDrawable(int id) throws NotFoundException {
// 检查是否是插件资源ID
Integer originalId = mResourceMapping.getOriginalId(id);
if (originalId != null) {
try {
return mPluginResources.getDrawable(originalId);
} catch (NotFoundException e) {
// 回退到宿主资源
}
}
return mHostResources.getDrawable(id);
}
}
资源ID映射原理:
1.4 运行时容器(Loader + Runtime)的协同工作机制
Loader核心源码:
java
// ShadowPluginLoader.java - Loader实现
public class ShadowPluginLoader {
private Context mHostContext;
private PluginClassLoader mPluginClassLoader;
private PluginResources mPluginResources;
private ComponentManager mComponentManager;
private ShadowRuntime mBoundRuntime;
public void init(Bundle args) {
// 1. 创建插件目录
createPluginWorkspace();
// 2. 加载插件APK
File pluginApk = loadPluginApk();
// 3. 创建ClassLoader和Resources
mPluginClassLoader = createPluginClassLoader(pluginApk);
mPluginResources = createPluginResources(pluginApk);
// 4. 初始化组件管理器
mComponentManager = new ComponentManager(mHostContext);
mComponentManager.init();
// 5. 解析插件清单
parsePluginManifest(pluginApk);
}
public void bindRuntime(ShadowRuntime runtime) {
mBoundRuntime = runtime;
// 建立双向关联
runtime.setPluginClassLoader(mPluginClassLoader);
runtime.setPluginResources(mPluginResources);
runtime.setComponentManager(mComponentManager);
runtime.bindLoader(this);
}
public void startPluginActivity(Intent intent) {
// 1. 获取目标Activity信息
ComponentName component = intent.getComponent();
String activityName = component.getClassName();
// 2. 查找Activity信息
ActivityInfo activityInfo = mComponentManager.getActivityInfo(activityName);
// 3. 委托Runtime创建代理Activity
ShadowActivity shadowActivity = mBoundRuntime.createShadowActivity(
activityInfo, intent);
// 4. 启动代理Activity
Intent shadowIntent = new Intent(mHostContext, shadowActivity.getClass());
shadowIntent.putExtra(KEY_PLUGIN_ID, mPluginId);
shadowIntent.putExtra(KEY_ACTIVITY_INFO, activityInfo);
mHostContext.startActivity(shadowIntent);
}
}
Runtime核心源码:
java
// ShadowRuntime.java - Runtime实现
public class ShadowRuntime {
private ShadowPluginLoader mBoundLoader;
private PluginClassLoader mPluginClassLoader;
private PluginResources mPluginResources;
private ComponentManager mComponentManager;
public void init(Bundle args) {
String pluginId = args.getString(KEY_PLUGIN_ID);
// 1. 初始化插件环境
initPluginEnvironment();
// 2. 创建插件上下文
Context pluginContext = createPluginContext();
// 3. 初始化插件Application
if (mComponentManager != null) {
String appClassName = mComponentManager.getApplicationClassName();
if (appClassName != null) {
createPluginApplication(appClassName, pluginContext);
}
}
}
public void bindLoader(ShadowPluginLoader loader) {
mBoundLoader = loader;
}
public ShadowActivity createShadowActivity(ActivityInfo activityInfo, Intent intent) {
// 1. 加载插件Activity类
Class<?> pluginActivityClass = mPluginClassLoader.loadClass(activityInfo.name);
// 2. 创建ShadowActivity子类
Class<?> shadowActivityClass = generateShadowActivityClass(
pluginActivityClass.getName(), pluginActivityClass);
// 3. 实例化ShadowActivity
ShadowActivity shadowActivity = (ShadowActivity) shadowActivityClass.newInstance();
// 4. 设置插件Activity实例
Object pluginActivity = pluginActivityClass.newInstance();
shadowActivity.setPluginActivity(pluginActivity);
// 5. 注入依赖
injectDependencies(shadowActivity, pluginActivity);
return shadowActivity;
}
}
Loader-Runtime协作时序图:
协同工作机制关键点:
- 双向绑定:Loader和Runtime通过bind方法建立双向关联
- 职责分离 :
- Loader:资源加载、组件注册、目录管理
- Runtime:类生成、方法调用、服务代理
- 上下文传递:通过PluginContext传递插件运行环境
- 透明代理:ShadowActivity透明代理插件Activity调用
- 资源隔离:每个插件独立ClassLoader和Resources
通过这样的三层架构设计,Shadow实现了插件的完全透明化运行,插件开发者无需关心插件化实现细节,可以像开发普通Android应用一样开发插件。
对应的核心的源码:这些架包是对应怎么样的源码?

Runtime和Loader分别包含哪些核心的类?
二、插件加载机制
2.1 宿主加载插件流程 (重点)
腾讯Shadow是一个渐进式、组件化、去中心化的Android插件化框架。
它的核心设计思想是 "复用系统ClassLoader" 和 "单ClassLoader多Dex" ,这与早期插件化框架(如DynamicLoadApk)的"双亲委派破坏+多ClassLoader"方案有本质区别。
下面我将详细梳理Shadow的整体加载插件流程,并解释其核心思想。
一、核心设计思想(先理解这个,再看流程)
- 宿主与插件完全平等 :Shadow认为宿主和插件都是平等的APK,插件甚至可以不依赖宿主而独立运行。这通过将插件框架本身也封装为一个Runtime APK 和Loader APK来实现。
- 复用公共库 :宿主和插件可以共享相同的依赖库(如
androidx.appcompat),避免类冲突和包体积膨胀。这是通过精巧的ClassLoader委派机制实现的。 - 动态运行时:插件代码在运行时动态加载,无需安装。
二、三大核心组件
理解流程前,需要知道三个关键部分:
- 宿主(Host):主APP,负责管理插件的下载、安装、加载入口。它只包含最少的Shadow API。
- 插件(Plugin) :业务功能模块,被打包成一个独立的APK文件(不包含Shadow框架代码和公共库)。 插件里面包含了loader.apk和runtime.apk
- Manager(管理包) :
- Loader :(基础模块)一个特殊的APK,负责加载插件APK。它包含了插件加载器的实现和公共库。
- Runtime :一个特殊的APK,负责提供插件组件的运行时环境(如代理Activity、Service等)。它包含公共库和代理组件。
关键点:Loader和Runtime本身也是独立的APK,它们和宿主、插件一样,可以独立编译和更新。
三、整体加载流程(图文结合)
下图清晰地展示了Shadow插件化的核心加载流程与组件协作关系:

以下是流程各阶段的详细说明:
阶段一:环境初始化(图中A部分)
这个阶段在宿主App启动时进行,目的是准备好插件运行的"地基"。
- 宿主启动:用户启动宿主App。
- 安装/更新Manager :
- 宿主检查本地是否存在指定版本的
Loader和RuntimeAPK。 - 如果不存在或版本过低,会从网络下载这两个APK,并使用
PackageManager静默安装(PackageManagerCompat.installPackage)。
- 宿主检查本地是否存在指定版本的
- 创建PluginManager :
- 宿主通过固定的类名(
com.tencent.shadow.dynamic.host.DynamicPluginManager),使用PathClassLoader加载Loader APK 中的PluginManager实现类。 - 这样就获得了操作插件的入口对象。
- 宿主通过固定的类名(
阶段二:插件加载与启动(图中B部分)
当需要启动一个插件时(例如点击某个插件功能图标),流程如下:
-
加载插件:
- 宿主调用
PluginManager.enter(...)方法,传递插件APK的路径等信息。 - 这个调用会通过Binder跨进程(或同一进程的类加载)跳转到Loader APK中。
- 宿主调用
-
Loader创建PluginLoader:
- Loader APK中的
PluginManager实现收到请求。 - 它根据插件信息,创建一个专属于该插件的
PluginLoader实例。 - 关键步骤 :
PluginLoader会为插件创建一个新的ClassLoader。这个ClassLoader的parent被设置为Loader APK的ClassLoader。这意味着:- 插件可以访问Loader APK中的类(即公共依赖库)。
- 不同插件的
ClassLoader是隔离的。 - 宿主、Loader、插件形成了清晰的类加载委派关系。
- Loader APK中的
-
加载插件资源:
PluginLoader使用Android的AssetManager和Resources构造方法,创建出插件自己的Resources对象,用于加载插件APK中的资源(如图片、布局)。
-
启动插件组件:
PluginLoader调用loadRuntime或相关方法,确保Runtime APK 被加载。Runtime APK中包含了PluginActivity、PluginService等代理组件。- 宿主通过
PluginManager获得一个启动插件的Intent,这个Intent指向的是Runtime APK中的代理Activity (如com.tencent.shadow.plugin.runtime.PluginDefaultActivity)。
-
代理组件接管:
- 系统启动这个代理Activity。
- 代理Activity在
onCreate等生命周期方法中,通过PluginLoader和Binder,调用到插件APK中真正的业务Activity的对应生命周期方法。 - 关键桥梁 :
PluginLoader充当了代理组件和插件实现类之间的桥梁。它用插件的ClassLoader去加载插件中真正的业务类(如com.example.plugin.MainActivity),并实例化、调用其方法。
加载manager插件的流程
scss
Shadow
FixedPathPmUpdater
DynamicPluginManager
ManagerImplLoader
PluginManagerImpl
SamplePluginManager----->enter()
FastPluginManager----> installPlugin()
具体的了loader插件流程:(核心)

csharp
BinderPluginLoader
PluginLoaderBinder
DynamicPluginLoader
ShadowPluginLoader
LoadPluginBloc
LoadApkBloc
初始化插件的Application: CreateApplicationBloc
callApplicationOnCreate() 的调用时机: 时机一:启动插件 Activity 前
真正的加载流程是:
-
DynamicPluginLoader 作为 Loader APK 的入口,接收宿主请求
-
委托给 ShadowPluginLoader(实际是它的子类)
-
ShadowPluginLoader 调用 LoadPluginBloc 执行实际加载
-
LoadPluginBloc 负责:
- 创建插件的
ShadowClassLoader - 创建插件的
Resources - 加载插件的
Application - 解析插件组件并注册到
ComponentManager - 缓存
PluginParts供后续使用
- 创建插件的

我来绘制一个完整的时序图,展示宿主加载插件并启动插件Application的全流程。
一、完整加载启动时序图
二、详细流程分解
阶段1:宿主发起加载请求
java
// 宿主代码
DynamicPluginManager pluginManager = new DynamicPluginManager(context);
// 1. 加载插件
pluginManager.loadPlugin(uuid, partKey, callback);
// 2. 启动插件Activity
Intent pluginIntent = new Intent();
pluginIntent.setClassName(pluginPackageName, pluginActivityClassName);
pluginManager.startPluginActivity(context, pluginIntent);
阶段2:Binder通信到Loader进程
kotlin
// PluginLoaderBinder (宿主的Binder代理)
class PluginLoaderBinder : IPluginLoader.Stub() {
override fun loadPlugin(uuid: String, partKey: String, callback: LoadPluginCallback) {
// 调用到Loader进程的DynamicPluginLoader
mDynamicPluginLoader.loadPlugin(uuid, partKey, callback)
}
}
阶段3:DynamicPluginLoader初始化
kotlin
// DynamicPluginLoader.kt
internal class DynamicPluginLoader(hostContext: Context, uuid: String) {
init {
// 1. 加载CoreLoaderFactoryImpl
val coreLoaderFactory = mDynamicLoaderClassLoader.getInterface(
CoreLoaderFactory::class.java,
CORE_LOADER_FACTORY_IMPL_NAME
)
// 2. 创建ShadowPluginLoader
mPluginLoader = coreLoaderFactory.build(hostContext)
// 3. 设置代理提供者
DelegateProviderHolder.setDelegateProvider(mPluginLoader)
// 4. 初始化
mPluginLoader.onCreate()
}
fun loadPlugin(partKey: String) {
// 获取插件信息
val installedApk = mUuidManager.getPlugin(mUuid, partKey)
// 调用ShadowPluginLoader
val future = mPluginLoader.loadPlugin(installedApk)
future.get() // 等待完成
}
}
阶段4:LoadPluginBloc并行加载
kotlin
// LoadPluginBloc.kt - 并行加载流程
object LoadPluginBloc {
fun loadPlugin(...): Future<*> {
// 1. 创建ClassLoader (LoadApkBloc)
val buildClassLoader = executorService.submit {
LoadApkBloc.loadPlugin(installedApk, loadParameters, pluginPartsMap)
}
// 2. 解析Manifest (并行)
val buildPluginManifest = executorService.submit {
val pluginClassLoader = buildClassLoader.get()
pluginClassLoader.loadPluginManifest()
}
// 3. 创建Resources (并行)
val buildResources = executorService.submit {
CreateResourceBloc.create(installedApk.apkFilePath, hostAppContext)
}
// 4. 创建Application (CreateApplicationBloc)
val buildApplication = executorService.submit {
val pluginClassLoader = buildClassLoader.get()
val resources = buildResources.get()
val pluginManifest = buildPluginManifest.get()
CreateApplicationBloc.createShadowApplication(
pluginClassLoader,
loadParameters,
pluginManifest,
resources,
hostAppContext,
componentManager,
pluginApplicationInfo,
appComponentFactory
)
}
// 5. 最终组装
val buildRunningPlugin = executorService.submit {
val pluginClassLoader = buildClassLoader.get()
val resources = buildResources.get()
val shadowApplication = buildApplication.get()
lock.withLock {
// 缓存PluginParts
pluginPartsMap[loadParameters.partKey] = PluginParts(
appComponentFactory,
shadowApplication, // 已attachBaseContext,未onCreate
pluginClassLoader,
resources,
pluginPackageManager
)
}
}
return buildRunningPlugin
}
}
阶段5:启动时调用Application.onCreate()
kotlin
// ShadowPluginLoader.kt
abstract class ShadowPluginLoader(hostAppContext: Context) {
@Synchronized
fun callApplicationOnCreate(partKey: String) {
// 确保在主线程执行
if (isUiThread()) {
realAction()
} else {
mUiHandler.post { realAction() }
}
}
private fun realAction() {
val pluginParts = getPluginParts(partKey)
pluginParts?.let {
val application = pluginParts.application
// 1. 先创建ContentProvider并调用onCreate()
mPluginContentProviderManager.createContentProviderAndCallOnCreate(
application, partKey, pluginParts
)
// 2. 再调用Application.onCreate()
application.onCreate()
}
}
}
阶段6:代理Activity加载插件Activity
java
// 代理Activity (Runtime APK中)
public class PluginDefaultActivity extends ShadowActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1. 获取插件Activity类名
String pluginActivityClassName = getIntent().getStringExtra(
KEY_ACTIVITY_CLASSNAME);
// 2. 通过PluginLoader获取插件的ClassLoader
ClassLoader pluginClassLoader = mPluginLoader.getPluginClassLoader(partKey);
// 3. 加载插件Activity类
Class<?> pluginActivityClass = pluginClassLoader.loadClass(
pluginActivityClassName);
// 4. 创建插件Activity实例
Activity pluginActivity = (Activity) pluginActivityClass.newInstance();
// 5. 设置桥接关系
setPluginActivity(pluginActivity);
// 6. 调用插件Activity的生命周期方法
pluginActivity.onCreate(savedInstanceState);
}
}
三、关键设计要点
1. 三级加载架构
scss
宿主 → DynamicPluginLoader → ShadowPluginLoader → LoadPluginBloc
(Binder桥接) (核心加载器) (并行加载)
四、总结流程

- 宿主发起请求:通过PluginManager加载插件
- Binder跨进程:调用到Loader进程的DynamicPluginLoader
- 初始化加载器:创建ShadowPluginLoader,设置代理
- 并行加载组件:LoadPluginBloc并行创建ClassLoader、Resources、解析Manifest
- 创建Application:CreateApplicationBloc创建Application实例,调用attachBaseContext
- 缓存PluginParts:组装所有组件并缓存
- 延迟onCreate:Application.onCreate()延迟到组件启动前
- 启动代理组件:宿主启动代理Activity
- 调用onCreate:DynamicPluginLoader调用callApplicationOnCreate()
- 加载真实组件:代理Activity用插件ClassLoader加载真实Activity
2.1.1 宿主和插件同名,是怎么加载的?
宿主调用插件,插件都没有起来,怎么使用binder!
理解的插件框架有两个作用:一是"自解耦",二是"免安装"。
插件Activity想调用的父类方法,比如getIntent()方法,也需要通过中间件层ShadowActivity转调回壳子Activity
如果我的插件有个, BaseActivity, 它能否被继承 Activity的启动模式,对于它是否有影响
插件的service,Activity,如何感知宿主的service等生命周期,就是通信!
shadow天然具有热修复的能力吗? 是全量的,不是增量的!
Android的虚拟机加载宿主里面class的时候,是一次全部加载进去还是用到的时候再加载进去呢?
按需加载:Android虚拟机不会一次性加载所有类,而是用到时才加载
2.2.2. Manager / Loader 初始化逻辑重
📌 核心流程
Manager 和 Loader 是动态加载的,其初始化在 PPS 进程中同步执行。
| 文件 | 作用 |
|---|---|
PPSControllerImpl.java |
PPS 的 Binder 实现,负责加载 Runtime/Loader |
DynamicRuntime.java |
动态加载 Runtime 和 Container |
LoaderFactory.java |
创建 Loader 实例 |
💡 关键代码片段
typescript
// PPSControllerImpl.java
@Override
public void onLoadRuntime(String uuid, String partKey, ...) {
// 在 PPS 主线程中加载 Runtime
DynamicRuntime.install(uuid, partKey, ...); // ← 同步阻塞
}
// DynamicRuntime.java
public static void install(...) {
// 反射加载 Runtime 类并初始化
Class<?> runtimeClass = classLoader.loadClass("RuntimeImpl");
runtimeClass.getMethod("init", ...).invoke(null, ...); // ← 若 init 逻辑重,卡主线程
}
✅ 优化建议 :将
Runtime/Loader中非 UI 初始化逻辑移至子线程或懒加载。
总结:性能瓶颈与源码对应表
| 瓶颈 | 关键源码文件 | 优化切入点 |
|---|---|---|
| 进程启动慢 | PluginProcessService.java, ShadowApplication.java |
宿主 Application 按进程裁剪初始化 |
| Dex 加载慢 | PluginClassLoader.java, LoaderImpl.java |
插件包瘦身、开启混淆、预加载 |
| 包体积大 | 构建脚本(build.gradle) |
shrinkResources + minifyEnabled + zipalign |
| 初始化重 | PPSControllerImpl.java, DynamicRuntime.java |
异步初始化、懒加载 |
2.3 插件卸载的实现原理
Shadow 本身不提供"插件卸载"的运行时 API 通过自己封装:删除APk,停止进程,退出页面
-
插件运行在独立进程(由
PluginProcessService启动); -
当宿主
unbindService(PPS)后:- 若该进程无其他活跃组件(Activity finish、Service stop);
- Android 系统会自动回收该进程;
- 进程内所有类、静态变量、资源全部释放。
三、核心组件支持(Activity+资源+So)
3.1 Activity 启动的完整代理链路(从 enter() 到 PluginActivity)
宿主启动插件 Activity 完整源码流程
一、整体调用链源码
scss
宿主Activity → PluginManagerThatUseDynamicLoader → BinderPluginLoader →
DynamicPluginLoader → ShadowPluginLoader → ComponentManager →
代理Activity(PluginDefaultActivity) → 插件Activity
二、源码详细分析
1. 宿主发起启动(宿主进程)
java
// 宿主Activity.java
public class HostActivity extends AppCompatActivity {
private PluginManagerThatUseDynamicLoader mPluginManager;
private void startPluginMainActivity() {
try {
// 1. 创建插件Activity的Intent
Intent pluginIntent = new Intent();
// 设置插件Activity的完整类名
pluginIntent.setClassName("com.example.plugin",
"com.example.plugin.MainActivity");
// 2. 添加额外参数(可选)
pluginIntent.putExtra("param1", "value1");
pluginIntent.putExtra("param2", 123);
// 3. 启动插件Activity
// 这里mPluginManager需要实现一个便捷方法
mPluginManager.startPluginActivity(this, pluginIntent);
} catch (Exception e) {
Log.e("HostActivity", "启动插件失败", e);
}
}
}
2. PluginManagerThatUseDynamicLoader 便捷方法(宿主进程)
java
// PluginManagerThatUseDynamicLoader.java 或其子类
public class MyPluginManager extends PluginManagerThatUseDynamicLoader {
/**
* 启动插件Activity的便捷方法
*/
public void startPluginActivity(Context context, Intent pluginIntent)
throws RemoteException, FailedException {
// 1. 提取uuid(通常从pluginIntent或配置中获取)
String uuid = extractUuidFromIntent(pluginIntent);
// 2. 加载Runtime(如果未加载)
loadRunTime(uuid);
// 3. 加载PluginLoader(如果未加载)
loadPluginLoader(uuid);
// 4. 通过PluginLoader转换Intent
Intent proxyIntent = mPluginLoader.convertActivityIntent(pluginIntent);
if (proxyIntent == null) {
throw new RuntimeException("转换Intent失败");
}
// 5. 启动代理Activity
context.startActivity(proxyIntent);
}
private String extractUuidFromIntent(Intent intent) {
// 从Intent中提取uuid,可能来自:
// 1. Intent的extra
// 2. 根据插件包名映射
// 3. 默认uuid
return intent.getStringExtra("uuid")
? intent.getStringExtra("uuid")
: "default_uuid";
}
}
3. BinderPluginLoader.convertActivityIntent(宿主进程)
java
// BinderPluginLoader.java(宿主进程中的Binder代理)
@Override
public Intent convertActivityIntent(Intent pluginActivityIntent) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
Intent _result;
try {
// 1. 写入接口标识
_data.writeInterfaceToken(DESCRIPTOR);
// 2. 写入Intent参数(支持null检查)
if ((pluginActivityIntent != null)) {
_data.writeInt(1); // 标记Intent不为null
pluginActivityIntent.writeToParcel(_data, 0); // 序列化Intent
} else {
_data.writeInt(0); // 标记Intent为null
}
// 3. 发起Binder调用
// TRANSACTION_convertActivityIntent是方法标识符
mRemote.transact(TRANSACTION_convertActivityIntent, _data, _reply, 0);
// 4. 读取异常(如果有)
_reply.readException();
// 5. 读取返回的Intent
if ((0 != _reply.readInt())) {
_result = Intent.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
return _result;
} finally {
// 6. 回收Parcel对象
_reply.recycle();
_data.recycle();
}
}
4. DynamicPluginLoader.convertActivityIntent(Loader进程)
kotlin
// DynamicPluginLoader.kt(Loader进程)
internal class DynamicPluginLoader(hostContext: Context, uuid: String) {
// Binder Stub实现
private val mBinder: IPluginLoader.Stub = object : IPluginLoader.Stub() {
override fun convertActivityIntent(pluginActivityIntent: Intent): Intent? {
// 委托给DynamicPluginLoader.this的实现
return this@DynamicPluginLoader.convertActivityIntent(pluginActivityIntent)
}
}
// 实际的Intent转换实现
fun convertActivityIntent(pluginActivityIntent: Intent): Intent? {
// 1. 提取partKey
val partKey = extractPartKeyFromIntent(pluginActivityIntent)
// 2. 确保插件已加载
ensurePluginLoaded(partKey)
// 3. 确保Application已调用onCreate()
ensureApplicationOnCreate(partKey)
// 4. 委托给ShadowPluginLoader转换Intent
return mPluginLoader.convertActivityIntent(pluginActivityIntent)
}
private fun extractPartKeyFromIntent(intent: Intent): String {
// 从Intent中提取partKey的方式:
// 1. 从component的包名
// 2. 从extra参数
// 3. 默认值
val component = intent.component
if (component != null) {
// 从包名映射partKey,或直接使用包名
return component.packageName
}
// 或者从extra中读取
return intent.getStringExtra("partKey") ?: "default"
}
private fun ensurePluginLoaded(partKey: String) {
// 检查插件是否已加载
val loadedPlugins = mPluginLoader.getLoadedPlugin()
if (!loadedPlugins.containsKey(partKey)) {
// 插件未加载,先加载
loadPlugin(partKey)
}
}
private fun ensureApplicationOnCreate(partKey: String) {
// 获取插件加载状态
val loadedPlugins = mPluginLoader.getLoadedPlugin()
val isOnCreateCalled = loadedPlugins[partKey] as? Boolean ?: false
if (!isOnCreateCalled) {
// Application未调用onCreate,调用之
mPluginLoader.callApplicationOnCreate(partKey)
}
}
}
5. ShadowPluginLoader.convertActivityIntent(Loader进程)
kotlin
// ShadowPluginLoader.kt
abstract class ShadowPluginLoader(hostAppContext: Context) {
lateinit var mComponentManager: ComponentManager
fun convertActivityIntent(pluginActivityIntent: Intent): Intent? {
// 直接委托给ComponentManager处理
return mComponentManager.convertPluginActivityIntent(pluginActivityIntent)
}
@Synchronized
fun callApplicationOnCreate(partKey: String) {
// 确保在主线程执行
if (isUiThread()) {
realAction()
} else {
val waitUiLock = CountDownLatch(1)
mUiHandler.post {
realAction()
waitUiLock.countDown()
}
waitUiLock.await()
}
}
private fun realAction() {
val pluginParts = getPluginParts(partKey)
pluginParts?.let {
val application = pluginParts.application
// 1. 创建并初始化ContentProvider
mPluginContentProviderManager.createContentProviderAndCallOnCreate(
application, partKey, pluginParts
)
// 2. 调用Application.onCreate()
application.onCreate()
}
}
}
6. ComponentManager.convertPluginActivityIntent(Loader进程)
kotlin
// SampleComponentManager.kt(ComponentManager的实现类)
class SampleComponentManager(
private val mHostAppContext: Context
) : ComponentManager() {
// 代理Activity的类名(在Runtime APK中)
private val PROXY_ACTIVITY_CLASS =
"com.tencent.shadow.plugin.runtime.PluginDefaultActivity"
// 代理FragmentActivity的类名(可选)
private val PROXY_FRAGMENT_ACTIVITY_CLASS =
"com.tencent.shadow.plugin.runtime.PluginDefaultFragmentActivity"
override fun convertPluginActivityIntent(pluginIntent: Intent): Intent {
// 1. 提取插件信息
val partKey = extractPartKey(pluginIntent)
val pluginActivityClassName = pluginIntent.component?.className
val pluginPackageName = pluginIntent.component?.packageName
// 2. 创建代理Activity的Intent
val proxyIntent = Intent()
// 3. 判断使用哪种代理Activity
// 根据插件Activity的类型选择对应的代理Activity
val isFragmentActivity = isFragmentActivity(pluginActivityClassName)
val proxyActivityClass = if (isFragmentActivity) {
PROXY_FRAGMENT_ACTIVITY_CLASS
} else {
PROXY_ACTIVITY_CLASS
}
// 4. 设置代理Activity类名
proxyIntent.setClassName(mHostAppContext, proxyActivityClass)
// 5. 传递插件信息给代理Activity
proxyIntent.putExtra(Constants.KEY_PART_KEY, partKey)
proxyIntent.putExtra(Constants.KEY_ACTIVITY_CLASSNAME, pluginActivityClassName)
proxyIntent.putExtra(Constants.KEY_PLUGIN_PACKAGE_NAME, pluginPackageName)
// 6. 传递原始插件的Intent
proxyIntent.putExtra(Constants.KEY_PLUGIN_INTENT, pluginIntent)
// 7. 传递插件的启动模式
val launchMode = getLaunchMode(pluginActivityClassName)
proxyIntent.putExtra(Constants.KEY_ACTIVITY_LAUNCH_MODE, launchMode)
// 8. 设置启动标志
proxyIntent.flags = pluginIntent.flags or Intent.FLAG_ACTIVITY_NEW_TASK
return proxyIntent
}
private fun extractPartKey(intent: Intent): String {
// 根据实际情况提取partKey
// 可能从Intent的extra中,或者根据包名映射
return intent.getStringExtra("partKey")
?: intent.component?.packageName
?: "default"
}
private fun isFragmentActivity(className: String?): Boolean {
// 判断插件Activity是否是FragmentActivity
// 可以通过反射检查,或者根据命名约定
return className?.contains("FragmentActivity") ?: false
}
private fun getLaunchMode(className: String?): Int {
// 获取插件Activity的启动模式
// 可以从插件Manifest中解析,或使用默认值
return ActivityInfo.LAUNCH_MULTIPLE // 默认standard模式
}
}
7. 代理Activity PluginDefaultActivity(Runtime进程)
java
// PluginDefaultActivity.java(Runtime APK中)
public class PluginDefaultActivity extends ShadowActivity {
private static final String TAG = "PluginDefaultActivity";
// 插件Activity实例
private Activity mPluginActivity;
// 插件Activity的类名
private String mPluginActivityClassName;
// partKey
private String mPartKey;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1. 从Intent中获取插件信息
extractPluginInfoFromIntent();
// 2. 获取插件的ClassLoader
ClassLoader pluginClassLoader = getPluginClassLoader(mPartKey);
// 3. 加载并创建插件Activity实例
createPluginActivity(pluginClassLoader);
// 4. 将代理Activity与插件Activity绑定
bindPluginActivity();
// 5. 调用插件Activity的onCreate()
mPluginActivity.onCreate(savedInstanceState);
}
private void extractPluginInfoFromIntent() {
Intent intent = getIntent();
// 从Intent中提取关键信息
mPartKey = intent.getStringExtra(Constants.KEY_PART_KEY);
mPluginActivityClassName = intent.getStringExtra(Constants.KEY_ACTIVITY_CLASSNAME);
if (mPartKey == null || mPluginActivityClassName == null) {
throw new RuntimeException("缺少插件信息: partKey=" + mPartKey
+ ", className=" + mPluginActivityClassName);
}
Log.d(TAG, "启动插件Activity: partKey=" + mPartKey
+ ", className=" + mPluginActivityClassName);
}
private ClassLoader getPluginClassLoader(String partKey) {
// 通过PluginLoader获取插件的ClassLoader
// 这里需要跨进程调用到Loader进程
try {
// 1. 获取PluginLoader(通过全局单例或Binder)
PluginLoader pluginLoader = PluginLoaderManager.getInstance().getPluginLoader();
// 2. 获取已加载的插件信息
Map<String, Boolean> loadedPlugins = pluginLoader.getLoadedPlugin();
if (!loadedPlugins.containsKey(partKey)) {
throw new RuntimeException("插件未加载: " + partKey);
}
// 3. 获取插件ClassLoader(需要跨进程)
// 实际上,这里可能需要另一个Binder调用
// 或者通过PluginPartsManager直接获取
return PluginPartsManager.getInstance().getPluginParts(partKey).getClassLoader();
} catch (RemoteException e) {
throw new RuntimeException("获取插件ClassLoader失败", e);
}
}
private void createPluginActivity(ClassLoader pluginClassLoader) {
try {
// 1. 加载插件Activity类
Class<?> pluginActivityClass = pluginClassLoader.loadClass(mPluginActivityClassName);
// 2. 创建实例
mPluginActivity = (Activity) pluginActivityClass.newInstance();
Log.d(TAG, "成功创建插件Activity实例: " + mPluginActivityClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException("找不到插件Activity类: " + mPluginActivityClassName, e);
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("创建插件Activity实例失败", e);
}
}
private void bindPluginActivity() {
// 创建PluginContextWrapper
Context pluginContext = createPluginContext();
// 调用插件Activity的attach方法
attachPluginActivity(pluginContext);
// 设置代理关系
setPluginActivityDelegate();
}
private Context createPluginContext() {
// 获取插件的Resources
Resources pluginResources = PluginPartsManager.getInstance()
.getPluginParts(mPartKey)
.getResources();
// 获取插件的Application
Application pluginApplication = PluginPartsManager.getInstance()
.getPluginParts(mPartKey)
.getApplication();
// 创建PluginContextWrapper
return new PluginContextWrapper(
this, // 代理Activity作为base
pluginClassLoader, // 插件ClassLoader
pluginResources, // 插件Resources
pluginApplication // 插件Application
);
}
private void attachPluginActivity(Context pluginContext) {
try {
// 通过反射调用Activity的attach方法
Method attachMethod = Activity.class.getDeclaredMethod("attach",
Context.class, // context
ActivityThread.class, // activityThread
String.class, // className
IBinder.class, // token
Application.class, // application
Intent.class, // intent
ActivityInfo.class, // info
CharSequence.class, // title
Activity.class, // parent
String.class, // embeddedId
NonConfigurationInstances.class // lastNonConfigurationInstances
);
attachMethod.setAccessible(true);
// 获取ActivityInfo
ActivityInfo pluginActivityInfo = getPluginActivityInfo();
// 调用attach方法
attachMethod.invoke(mPluginActivity,
pluginContext, // Context
null, // ActivityThread
mPluginActivityClassName, // className
getActivityToken(), // token (代理Activity的token)
pluginContext.getApplicationContext(), // Application
getIntent(), // Intent
pluginActivityInfo, // ActivityInfo
null, // title
this, // parent (代理Activity)
null, // embeddedId
null // lastNonConfigurationInstances
);
} catch (Exception e) {
throw new RuntimeException("attach插件Activity失败", e);
}
}
private void setPluginActivityDelegate() {
// 创建委托关系,后续生命周期方法转发给插件Activity
mPluginActivityDelegate = new PluginActivityDelegate(this, mPluginActivity);
}
@Override
protected void onStart() {
super.onStart();
if (mPluginActivity != null) {
mPluginActivity.onStart();
}
}
@Override
protected void onResume() {
super.onResume();
if (mPluginActivity != null) {
mPluginActivity.onResume();
}
}
@Override
protected void onPause() {
super.onPause();
if (mPluginActivity != null) {
mPluginActivity.onPause();
}
}
@Override
protected void onStop() {
super.onStop();
if (mPluginActivity != null) {
mPluginActivity.onStop();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPluginActivity != null) {
mPluginActivity.onDestroy();
}
}
// ... 其他生命周期方法
}
8. PluginContextWrapper(Runtime进程)
java
// PluginContextWrapper.java
public class PluginContextWrapper extends ContextWrapper {
private final ClassLoader mPluginClassLoader;
private final Resources mPluginResources;
private final Resources.Theme mPluginTheme;
private final Application mPluginApplication;
public PluginContextWrapper(Context base,
ClassLoader pluginClassLoader,
Resources pluginResources,
Application pluginApplication) {
super(base);
mPluginClassLoader = pluginClassLoader;
mPluginResources = pluginResources;
mPluginApplication = pluginApplication;
// 创建插件主题
mPluginTheme = mPluginResources.newTheme();
mPluginTheme.setTo(super.getTheme());
}
四、时序图总结
五、设计要点总结
总结宿主拉起插件activity代理流程:
scss
SamplePluginManager----->convertActivityIntent()
DynamicPluginLoader------>convertActivityIntent()
ShadowPluginLoader------>convertActivityIntent()
ComponentManager ------>convertActivityIntent()
PluginDefaultProxyActivity
PluginContainerActivity : (在runtime中)
ShadowActivityDelegate
宿主的清单文件中注册的是这个activity!
ini
<activity
android:name="com.tencent.shadow.sample.plugin.runtime.PluginDefaultProxyActivity"
android:launchMode="standard"
android:screenOrientation="portrait"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize|layoutDirection"
android:hardwareAccelerated="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
android:multiprocess="true" />
宿主启动插件 Activity 的过程是一个 多进程协作的代理模式 实现。宿主无法直接启动插件 Activity,而是通过 代理 Activity 间接实现。整个流程涉及 三个进程(宿主进程、Loader进程、Runtime进程
核心思想重点:
如果插件Activity不是一个真的Activity,只是一个跟Activity有差不多方法的普通类,这件事就简单多了,只需要让壳子Activity持有它,转调它就行了
3.1.2.1 插件Activity生命周期的管理机制
PluginActivityDelegate
一个ContainerActivity可以对应多个PluginActivity?
我的一个插件中,有好多个SingleTask的activity,而我看你你的AndroidMinest中只预埋了一个,这你是怎么处理的呢
ClassLoader是怎么获取的?怎么赋值的?
依赖注入(DI)的实现
3.1.2 2个核心问题:插件的ClassLoader和插件的Context
1. 插件ClassLoader隔离问题
- 问题:插件 Activity 需要自己的 ClassLoader 加载类
- 解决 :代理 Activity 通过 Binder 获取插件 ClassLoader,使用
loadClass()加载插件类
3.1.2.1 ClassLoader创建时序图
markdown
PluginLoaderBinder
ShadowPluginLoader
LoadPluginBloc
LoadApkBloc
PluginClassLoader
3.1.2.2 八、总结图
markdown
Shadow ClassLoader隔离核心思想:
┌─────────────────────────────────────────────────────────────┐
│ 隔离理念:分层隔离 │
├─────────────────────────────────────────────────────────────┤
│ 系统层: BootClassLoader ← 所有Android应用共享 │
│ 宿主层: PathClassLoader ← 宿主应用独享 │
│ 插件层: PluginClassLoader ← 每个插件独享,隔离核心 │
│ 实现层: DexClassLoader ← 实际加载插件APK │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 核心实现:包装与委托 │
├─────────────────────────────────────────────────────────────┤
│ 1. 包装: PluginClassLoader包装DexClassLoader │
│ 2. 委托: 插件类 → 插件DexClassLoader │
│ 3. 回退: 系统类 → 宿主ClassLoader → BootClassLoader │
│ 4. 隔离: 相同类名在不同ClassLoader中是不同类 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 实际效果:安全与灵活 │
├─────────────────────────────────────────────────────────────┤
│ 安全: 插件崩溃不影响宿主,插件间互不影响 │
│ 灵活: 插件可独立更新,依赖库版本可独立 │
│ 兼容: 遵循双亲委派,兼容Android类加载机制 │
│ 高效: 单进程内隔离,避免跨进程性能损失 │
└─────────────────────────────────────────────────────────────┘
2. Context问题
- 问题:插件 Activity 需要访问插件资源,但系统提供的是宿主 Context
- 解决 :创建
PluginContextWrapper,重写getResources()、getClassLoader()等方法
PluginContext是通过宿主的Context包装的,那么宿主的context会影响插件的context吗? 影响是有限、可控且有设计目的的
PluginContext包装宿主Context,是为了在Android系统限制下实现最大程度的隔离
一、PluginContext创建源码
scala
// 1. PluginContext构造方法
public class PluginContext extends ShadowContext {
private ComponentLauncher mComponentLauncher;
public PluginContext(Context base, // 宿主Context作为base
Application hostApplication,
ShadowPackageManager packageManager,
ClassLoader pluginClassLoader,
ComponentLauncher componentLauncher) {
// 调用父类构造,建立ContextWrapper包装关系
super(base, hostApplication, packageManager, pluginClassLoader);
mComponentLauncher = componentLauncher;
}
}
// 2. ShadowContext构造方法
public class ShadowContext extends ContextWrapper {
public ShadowContext(Context base, // 宿主Context
Application hostApplication,
ShadowPackageManager packageManager,
ClassLoader pluginClassLoader) {
super(base); // ContextWrapper保存宿主Context到mBase
// 创建插件自己的资源管理器
mPluginClassLoader = pluginClassLoader;
mPluginAssetManager = createPluginAssetManager();
mPluginResources = createPluginResources(mPluginAssetManager);
}
}
二、PluginContext注入Activity源码
arduino
// 1. 反射调用Activity.attach()注入Context
public class PluginActivityDelegate {
private void attachPluginActivity(Activity activity, Context context) {
try {
// 获取Activity.attach方法
Method attachMethod = Activity.class.getDeclaredMethod("attach",
Context.class, // ★关键参数:传入PluginContext★
ActivityThread.class,
String.class,
IBinder.class,
Application.class,
Intent.class,
ActivityInfo.class,
CharSequence.class,
Activity.class,
String.class,
NonConfigurationInstances.class,
Configuration.class);
attachMethod.setAccessible(true);
// ★调用attach,将PluginContext注入Activity★
attachMethod.invoke(activity,
context, // PluginContext实例
// 其他参数...
);
} catch (Exception e) {
throw new RuntimeException("Attach plugin activity failed", e);
}
}
}
三、Android Framework内部实现
arduino
// Activity.java (Android源码)
final void attach(Context context, // PluginContext被传入这里
ActivityThread aThread,
String token,
/* ... */) {
// ★关键赋值:将PluginContext设置为Activity的mBase★
attachBaseContext(context); // 内部:mBase = context
// Activity使用传入的Context初始化其他属性
mResources = context.getResources(); // 使用插件Resources
mTheme = context.getTheme(); // 使用插件Theme
// ...
}
四、运行时方法委托
typescript
// PluginContext重写关键方法
public class PluginContext extends ShadowContext {
@Override
public ClassLoader getClassLoader() {
// 返回插件ClassLoader,不是宿主的
return mPluginClassLoader;
}
@Override
public Resources getResources() {
// 返回插件Resources,不是宿主的
return mPluginResources;
}
@Override
public void startActivity(Intent intent) {
// 插件组件启动,通过Shadow框架
if (isPluginComponent(intent)) {
mComponentLauncher.startActivity(this, intent);
} else {
// 非插件组件,委托给宿主Context
super.startActivity(intent);
}
}
}
PluginContext赋值过程时序图
一、完整的PluginContext创建与赋值时序图
3.2 shadow加载资源的?如何解决资源的冲突问题
参考后续独立文章
3.2.1 如何访问插件资源
参考后续独立文章
3.2.2 资源 ID 冲突问题
参考后续独立文章
3.2.3 插件只能访问到插件自己的资源 而不能访问到宿主的资源?
参考后续独立文章
3.3 插件的so是如何加载的?
参考后续独立文章
四.四大组件的支持情况和原理
开始说了Activity,现在说下其他2个,service,broadCast,ContentProvider不用
4.1 Shadow的跨进程设计与插件Service原理
- Binder传输机制:插件Service的Binder通过PPS的Binder跨进程传输到Manager进程,再经由Loader的Binder传输到其他进程,使得插件Service能够正常跨进程工作。
- 无单独代理Service:Shadow不需要为每个插件Service创建代理壳,仅依赖PPS即可支持任意数量的插件Service。
- 异常处理:PPS和Loader的Binder采用半手工实现(基于AIDL改造),支持自定义可序列化异常,避免跨进程调用失败时直接Crash。
根据您提供的类关系,我来绘制一个调用关系图:
2. 各组件职责
| 组件 | 所在进程 | 主要职责 |
|---|---|---|
| SamplePluginManager | 宿主进程 | 业务层接口,提供插件Service启动的便捷API |
| BinderPluginLoader | 宿主进程 | Binder客户端代理,负责与插件进程通信 |
| PluginLoaderBinder | 插件进程 | Binder服务端实现,接收宿主请求并转发 |
| DynamicPluginLoader | 插件进程 | 插件加载器,负责加载插件并管理组件 |
| PluginServiceManager | 插件进程 | 插件Service管理器,负责生命周期管理 |
| BasePluginProcessService | 插件进程 | 插件Service基类或具体实现 |
3. 完整调用链示例
kotlin
// 1. 宿主应用调用
SamplePluginManager.getInstance(context)
.startPluginService(pluginIntent)
// 2. BinderPluginLoader转发
class BinderPluginLoader : IPluginLoader.Stub {
override fun startPluginService(intent: Intent): ComponentName {
// 序列化Intent,通过Binder发送
return mRemote.startPluginService(intent)
}
}
// 3. PluginLoaderBinder接收
class PluginLoaderBinder : Binder() {
override fun onTransact(code: Int, data: Parcel, reply: Parcel, flags: Int): Boolean {
when (code) {
START_PLUGIN_SERVICE -> {
val intent = Intent.CREATOR.createFromParcel(data)
val result = mDynamicPluginLoader.startPluginService(intent)
// 序列化结果返回
result.writeToParcel(reply, 0)
return true
}
}
}
}
// 4. DynamicPluginLoader处理
class DynamicPluginLoader {
fun startPluginService(intent: Intent): ComponentName {
// 加载插件(如果需要)
loadPluginIfNeeded(intent)
// 交给PluginServiceManager处理
return pluginServiceManager.startPluginService(intent)
}
}
// 5. PluginServiceManager管理
class PluginServiceManager {
fun startPluginService(intent: Intent): ComponentName {
val componentName = intent.component!!
// 创建Service实例
if (!mAliveServicesMap.containsKey(componentName)) {
val service = createServiceAndCallOnCreate(intent)
mAliveServicesMap[componentName] = service
}
// 调用onStartCommand
mAliveServicesMap[componentName]?.onStartCommand(intent, 0, getNewStartId())
return componentName
}
}
// 6. BasePluginProcessService执行
class MyPluginService : BasePluginProcessService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// 执行业务逻辑
handleBusiness(intent)
return START_STICKY
}
}
markdown
SamplePluginManager
BinderPluginLoader
PluginLoaderBinder
DynamicPluginLoader
PluginServiceManager
BasePluginProcessService
4.2 Shadow对于广播的源码实现
宿主发送广播,插件接收 插件发送广播,宿主接收
PluginBroadcastReceiverTest 核心源码:ReceiverSupportTransform
Shadow 框架实现插件广播的原理
Shadow 框架实现插件广播的核心原理是 代理转发 + 字节码转换 + 环境隔离 的三层架构。让我详细解析其实现原理:
一、整体架构图
二、核心实现原理
1. 静态广播:代理转发机制
问题:
- 静态广播必须在
AndroidManifest.xml中注册 - 插件的
AndroidManifest.xml不会被系统识别 - 系统无法直接将广播发送给插件
解决方案:代理转发
java
// 宿主AndroidManifest.xml中声明代理接收器
<receiver android:name=".ShadowBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<!-- 其他需要转发的系统广播 -->
</intent-filter>
</receiver>
转发流程:
系统发送广播 → 宿主代理接收器 → Binder跨进程 → 插件进程 → 插件广播接收器
2. 动态广播:Context包装机制
问题:
- 插件调用
registerReceiver()时,需要正确的 Context - 系统需要知道广播接收器的注册信息
解决方案:Context包装
kotlin
class PluginContextWrapper(base: Context, private val pluginInfo: PluginInfo) :
ContextWrapper(base) {
override fun registerReceiver(
receiver: BroadcastReceiver?,
filter: IntentFilter?
): Intent? {
// 1. 创建代理接收器
val proxyReceiver = createProxyReceiver(receiver, pluginInfo.classLoader)
// 2. 在宿主中注册代理接收器
val result = base.registerReceiver(proxyReceiver, filter)
// 3. 保存映射关系
receiverMap[receiver] = proxyReceiver
return result
}
override fun unregisterReceiver(receiver: BroadcastReceiver?) {
val proxyReceiver = receiverMap.remove(receiver)
proxyReceiver?.let {
base.unregisterReceiver(it)
}
}
}
3. 环境隔离:字节码转换机制
问题:
- 插件广播接收器收到的 Context 是宿主 Context
- Intent 中的 ClassLoader 是宿主 ClassLoader
- 插件无法访问自己的资源
解决方案:字节码转换
kotlin
// 构建时修改插件字节码
class ReceiverSupportTransform : SpecificTransform() {
override fun transform(ctClass: CtClass) {
// 1. 将原始onReceive重命名
originalOnReceiveMethod.name = "onReceiveShadowContext"
// 2. 添加新的onReceive方法
val newMethod = CtMethod.make("""
public void onReceive(Context context, Intent intent) {
// 获取插件ClassLoader和Context
ClassLoader cl = this.getClass().getClassLoader();
PluginPartInfo info = PluginPartInfoManager.getPluginInfo(cl);
Context shadowContext = info.application;
// 修正Intent的ClassLoader
Intent intentCopy = new Intent(intent);
intentCopy.setExtrasClassLoader(cl);
// 调用原始方法
onReceiveShadowContext(shadowContext, intentCopy);
}
""", ctClass)
ctClass.addMethod(newMethod)
}
}
4.3 Fragment 在插件中的支持方式
不支持,有其他方法,参考实战的demo! 绑定Fragment的生命周期原理,源码如下: juejin.cn/post/762108...
五.Shadow 框架如何实现全动态化
动态化的目的是什么?举一个具体的业务场景
插件开发者不再关心宿主版本 :
无论用户用的是 6 个月前还是 2 年前的宿主,只要能加载最新插件包,就能获得完整功能
具体场景:Android 14 兼容性危机
- 根本原因 :Android 14 修改了
Activity的attach()方法签名(新增了String attributionTag参数) - 问题现象 :Shadow 的 Loader 组件中有一处对
Activity.attach()的反射调用,因参数不匹配导致NoSuchMethodException→ 崩溃 - 业务影响:每天有数万 Android 14 新用户无法使用信用卡功能,直接影响开户转化率和营收
- 传统方案局限:宿主 App 已提交应用商店审核,预计 3 周后才能上线
使用 Shadow(Manager + Loader 可动态化)后的解决方案
-
快速修复 Loader
- 修改 Loader 中对
Activity.attach()的调用逻辑,兼容 Android 14 新签名 - 重新打包 新的动态 Loader + Runtime(作为插件框架的一部分)
- 通过插件下发系统推送给用户,绕过应用商店审核
- 修改 Loader 中对
-
灰度发布验证
- 先推送给 10% 的 Android 14 用户
- 监控崩溃率和业务指标
- 确认稳定后全量发布
-
结果对比
- 修复时间:从 3 周缩短到 3 小时
- 影响范围:可精确控制只影响 Android 14 用户
- 安全性:随时可回滚到旧版本
对 Manager 动态化来说:
- 策略灵活:让插件管理策略成为可配置、可更新的部分
- 业务定制:不同业务可以有自己的插件加载逻辑
- 灰度控制:可以按用户、按地区、按场景精细化控制
- A/B 测试:同时测试多种插件管理策略
对 Loader 动态化来说:
- 技术自由:让不同技术栈的模块可以共存
- 安全可控:安全漏洞可以快速修复,不影响业务
- 平滑演进:支持技术架构的渐进式升级
- 兼容性保障:可针对不同 Android 版本提供适配
对 Container 动态化来说:
- UI 框架演进:支持不同 UI 框架的平滑切换
- 性能优化:可动态更新渲染引擎
- 体验升级:不断优化用户体验组件
- 多态容器:支持不同形态的插件容器
5.1 动态化的基本原理
Shadow 框架的全动态化基于 三级插件化架构,将框架核心组件也设计为可动态更新的插件:
5.1.1 三级动态化架构
css
┌─────────────────────────────────────────────────┐
│ 宿主壳(Bootstrap) │
│ - 最小化实现,只包含引导逻辑 │
│ - 固定不变,通过应用商店分发 │
└─────────────────────┬───────────────────────────┘
│ 动态加载
▼
┌─────────────────────────────────────────────────┐
│ 框架插件层(动态化) │
│ ├─ Manager插件:插件管理策略 │
│ ├─ Loader插件:插件加载逻辑 │
│ └─ Container插件:运行时容器 │
└─────────────────────┬───────────────────────────┘
│ 动态加载
▼
┌─────────────────────────────────────────────────┐
│ 业务插件层(动态化) │
│ ├─ 功能模块A:独立业务功能 │
│ ├─ 功能模块B:独立业务功能 │
│ └─ 功能模块C:独立业务功能 │
└─────────────────────────────────────────────────┘
5.1.2 核心设计原则
-
依赖倒置原则
kotlin// 宿主只依赖抽象接口 interface IPluginManager { fun loadPlugin(pluginId: String): Plugin } // 具体实现在插件中 class DynamicPluginManager : IPluginManager { override fun loadPlugin(pluginId: String): Plugin { // 动态加载逻辑 } } -
可插拔架构
kotlin// 插件注册机制 class PluginRegistry { private val pluginLoaders = ConcurrentHashMap<String, IPluginLoader>() fun registerLoader(name: String, loader: IPluginLoader) { pluginLoaders[name] = loader } fun getLoaderForPlugin(pluginInfo: PluginInfo): IPluginLoader { return pluginLoaders[pluginInfo.loaderVersion] ?: defaultLoader } } -
版本隔离机制
kotlin// 多版本 ClassLoader 共存 class VersionedClassLoader : ClassLoader { private val versionedLoaders = ConcurrentHashMap<String, ClassLoader>() fun loadClass(className: String, version: String): Class<*> { val loader = versionedLoaders[version] return loader?.loadClass(className) ?: super.loadClass(className) } }
5.1.3 动态更新流程
markdown
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 检测更新 │───▶│ 下载插件包 │───▶│ 安全验证 │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
└──────────────────┴──────────────────┘
│
▼
┌──────────────────┐
│ 创建插件运行时 │
└──────────────────┘
│
▼
┌──────────────────┐
│ 热切换/重启 │
└──────────────────┘
5.2 Manager 的动态化
Manager 插件负责插件管理体系,其动态化使插件管理策略可在线更新。
5.2.1 Manager 的核心职责
kotlin
interface IPluginManager {
// 插件生命周期管理
fun installPlugin(pluginInfo: PluginInfo): Boolean
fun uninstallPlugin(pluginId: String): Boolean
fun updatePlugin(pluginId: String, newVersion: PluginInfo): Boolean
// 插件调度
fun loadPlugin(pluginId: String): LoadResult
fun unloadPlugin(pluginId: String): Boolean
// 策略管理
fun setPluginPolicy(policy: PluginPolicy)
fun getPluginPolicy(): PluginPolicy
// 版本控制
fun checkForUpdates(): List<UpdateInfo>
fun rollbackPlugin(pluginId: String, version: String): Boolean
}
5.2.2 Manager 动态化实现
-
宿主引导机制
kotlin// 宿主中的 Bootstrap class ShadowBootstrap { fun initialize() { // 1. 从固定位置加载 Manager 插件 val managerApk = loadManagerApkFromAssets() // 2. 创建 Manager 的 ClassLoader val managerClassLoader = PluginClassLoader( managerApk.path, getSystemClassLoader() ) // 3. 加载 Manager 实现 val managerClass = managerClassLoader.loadClass( "com.tencent.shadow.dynamic.manager.DynamicPluginManager" ) // 4. 实例化并初始化 Manager val manager = managerClass.newInstance() as IPluginManager manager.init(hostContext, config) // 5. 设置为全局 Manager PluginManagerFactory.setInstance(manager) } } -
Manager 插件结构
markdownmanager-plugin.apk ├── AndroidManifest.xml ├── classes.dex ├── lib/ (可选) ├── res/ └── assets/ ├── manager_config.json └── strategy_rules.json -
配置动态化示例
json// manager_config.json { "version": "2.1.0", "updateStrategy": { "autoCheck": true, "checkInterval": 3600, "updateMode": "wifi_only", "forceUpdate": false }, "pluginPolicies": { "finance": { "minVersion": "1.2.0", "loadStrategy": "on_demand", "preloadDependencies": true }, "game": { "minVersion": "2.0.0", "loadStrategy": "preload", "memoryLimit": 100 } } }
5.2.3 Manager 热更新流程
kotlin
class ManagerHotUpdater {
fun updateManager(newManagerApk: File): Boolean {
// 1. 验证新 Manager 插件
if (!verifyManagerPlugin(newManagerApk)) {
return false
}
// 2. 创建备份(用于回滚)
createBackup(currentManagerApk)
// 3. 复制新 Manager 插件
newManagerApk.copyTo(managerApkPath, overwrite = true)
// 4. 重新加载 Manager
return reloadManager()
}
private fun reloadManager(): Boolean {
// 使用新 ClassLoader 重新加载
val newClassLoader = PluginClassLoader(
managerApkPath,
getSystemClassLoader()
)
// 反射创建新 Manager 实例
val newManager = createManagerInstance(newClassLoader)
// 迁移状态(如已加载插件列表)
migrateState(currentManager, newManager)
// 替换全局引用
PluginManagerFactory.replaceInstance(newManager)
// 清理旧资源
cleanupOldResources()
return true
}
}
5.3 Loader 的动态化
Loader 插件负责插件加载和技术适配,其动态化使技术架构可在线演进。
5.3.1 Loader 的核心职责
kotlin
interface IPluginLoader {
// 插件加载
fun loadPlugin(pluginInfo: PluginInfo): LoadResult
// 组件转换
fun convertComponent(component: ComponentInfo): ComponentInfo
fun createPluginContext(hostContext: Context): Context
// 兼容性适配
fun adaptForAndroidVersion(version: Int): AdaptResult
fun patchSystemApi(className: String, methodName: String, vararg args: Any?): Any?
// 资源管理
fun loadPluginResources(pluginInfo: PluginInfo): Resources
fun mergeResources(hostRes: Resources, pluginRes: Resources): Resources
}
5.3.2 Loader 动态化实现
-
Android 14 兼容性修复示例
kotlinclass Android14CompatibleLoader : IPluginLoader { override fun patchSystemApi(className: String, methodName: String, vararg args: Any?): Any? { // 处理 Activity.attach() 方法签名变化 if (className == "android.app.Activity" && methodName == "attach") { return patchActivityAttach(*args) } return null } private fun patchActivityAttach(vararg args: Any?): Any? { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // Android 14 及以上 patchActivityAttachForAndroid14(*args) } else { // Android 14 以下 patchActivityAttachLegacy(*args) } } @TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) private fun patchActivityAttachForAndroid14(vararg args: Any?): Any? { val method = Activity::class.java.getDeclaredMethod( "attach", Context::class.java, String::class.java, // attributionTag 参数 // ... 其他参数 ) method.isAccessible = true return method.invoke(*args) } private fun patchActivityAttachLegacy(vararg args: Any?): Any? { val method = Activity::class.java.getDeclaredMethod( "attach", Context::class.java, // ... 其他参数(没有 attributionTag) ) method.isAccessible = true return method.invoke(*args) } } -
Loader 多版本管理
kotlinclass LoaderVersionManager { private val loaders = ConcurrentHashMap<String, IPluginLoader>() // 注册不同版本的 Loader fun registerLoader(version: String, loader: IPluginLoader) { loaders[version] = loader } // 根据插件需求选择 Loader fun getLoaderForPlugin(pluginInfo: PluginInfo): IPluginLoader { return pluginInfo.requiredLoaderVersion?.let { version -> loaders[version] } ?: getDefaultLoader() } // 热更新 Loader fun updateLoader(version: String, newLoaderApk: File): Boolean { // 1. 加载新 Loader val newLoader = loadLoaderFromApk(newLoaderApk) // 2. 验证兼容性 if (!validateLoaderCompatibility(newLoader)) { return false } // 3. 替换旧版本 loaders[version] = newLoader // 4. 迁移状态 migrateLoaderState(version, newLoader) return true } } -
Loader 插件配置
json// loader_config.json { "version": "3.2.1", "compatibility": { "minAndroidVersion": 21, "maxAndroidVersion": 34, "supportedArchitectures": ["arm64-v8a", "armeabi-v7a"] }, "features": { "hotReload": true, "incrementalLoad": true, "resourceOverlay": true }, "performance": { "preloadThreshold": 0.5, "cacheSize": 50 } }
5.3.3 Loader 性能优化
kotlin
class LoaderPerformanceOptimizer {
// 预加载优化
fun preloadFrequentlyUsedPlugins() {
val frequentPlugins = analyzeUsagePatterns()
frequentPlugins.forEach { pluginId ->
val pluginInfo = getPluginInfo(pluginId)
preloadPluginResources(pluginInfo)
preloadPluginClasses(pluginInfo)
}
}
// 懒加载优化
fun implementLazyLoading() {
// 按需加载非关键类
val lazyClasses = listOf(
"com.plugin.analytics.AnalyticsManager",
"com.plugin.logging.Logger"
)
lazyClasses.forEach { className ->
registerLazyClass(className) {
// 使用时才加载
loadClassOnDemand(className)
}
}
}
// 缓存优化
fun optimizeCacheStrategy() {
// LRU 缓存策略
classCache.applyEvictionPolicy(
maxSize = 100, // 最多缓存 100 个类
policy = LruEvictionPolicy()
)
// 资源缓存
resourceCache.applyEvictionPolicy(
maxSize = 50 * 1024 * 1024, // 50MB
policy = SizeBasedEvictionPolicy()
)
}
}
5.4 Container 的动态化
Container 插件负责运行时环境和 UI 渲染,其动态化使 UI 框架可在线升级。
5.4.1 Container 的核心职责
kotlin
interface IPluginContainer {
// 组件代理
fun createActivityProxy(pluginActivity: Activity): ActivityProxy
fun createServiceProxy(pluginService: Service): ServiceProxy
fun createBroadcastProxy(pluginReceiver: BroadcastReceiver): BroadcastProxy
// UI 渲染
fun createPluginLayoutInflater(context: Context): LayoutInflater
fun createPluginView(context: Context, name: String, attrs: AttributeSet): View?
// 事件处理
fun dispatchKeyEvent(event: KeyEvent): Boolean
fun dispatchTouchEvent(event: MotionEvent): Boolean
// 状态管理
fun saveInstanceState(): Bundle
fun restoreInstanceState(state: Bundle)
}
5.4.2 Container 动态化实现
-
代理类动态生成
kotlinclass DynamicContainer : IPluginContainer { private val proxyClassCache = ConcurrentHashMap<String, Class<*>>() override fun createActivityProxy(pluginActivity: Activity): ActivityProxy { val pluginClassName = pluginActivity::class.java.name val proxyClassName = generateProxyClassName(pluginClassName) // 动态生成或加载代理类 val proxyClass = proxyClassCache.getOrPut(proxyClassName) { generateProxyClass(pluginClassName, proxyClassName) } // 创建代理实例 val proxy = proxyClass.newInstance() as ActivityProxy proxy.bindPluginActivity(pluginActivity) return proxy } private fun generateProxyClass( pluginClassName: String, proxyClassName: String ): Class<*> { // 使用字节码技术动态生成代理类 val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS) // 生成类结构 classWriter.visit(...) // 生成代理逻辑 generateProxyMethods(classWriter, pluginClassName) // 加载生成的类 val bytecode = classWriter.toByteArray() return defineClass(proxyClassName, bytecode) } } -
UI 框架热切换
kotlinclass UIFrameworkSwitcher { fun switchUIFramework(newContainerApk: File): Boolean { // 1. 暂停当前 UI 渲染 pauseUIRendering() // 2. 加载新 Container val newContainer = loadContainerPlugin(newContainerApk) // 3. 迁移 UI 状态 val uiState = captureCurrentUIState() // 4. 应用新 Container applyNewContainer(newContainer) // 5. 恢复 UI 状态 newContainer.restoreUIState(uiState) // 6. 恢复渲染 resumeUIRendering() return true } private fun captureCurrentUIState(): UIState { // 捕获当前 UI 状态,包括: // - 视图层次结构 // - 视图属性 // - 事件监听器 // - 动画状态 return UIState( viewHierarchy = captureViewHierarchy(), viewProperties = captureViewProperties(), eventListeners = captureEventListeners(), animations = captureAnimations() ) } } -
Container 插件配置
json// container_config.json { "version": "2.0.0", "uiFramework": { "type": "native", "renderEngine": "hardware", "animationSupport": true, "customViewSupport": true }, "performance": { "viewReuse": true, "incrementalRender": true, "asyncLayout": true }, "compatibility": { "minSdkVersion": 21, "maxSdkVersion": 34, "screenSizeSupport": ["small", "normal", "large", "xlarge"] } }
5.4.3 Container 性能优化
kotlin
class ContainerPerformanceOptimizer {
// 视图复用优化
fun optimizeViewReuse() {
// 1. 视图池
val viewPool = ViewPool(
maxSize = 20,
createView = { context, type ->
createViewByType(context, type)
}
)
// 2. 布局缓存
val layoutCache = LayoutCache(
maxSize = 50,
computeLayout = { view, widthSpec, heightSpec ->
measureAndLayout(view, widthSpec, heightSpec)
}
)
}
// 异步布局优化
fun implementAsyncLayout() {
// 使用协程或线程池进行异步布局
val layoutDispatcher = Executors.newFixedThreadPool(2)
fun asyncMeasure(view: View, widthSpec: Int, heightSpec: Int) {
layoutDispatcher.submit {
val width = View.MeasureSpec.getSize(widthSpec)
val height = View.MeasureSpec.getSize(heightSpec)
// 在后台线程计算布局
view.measure(widthSpec, heightSpec)
view.layout(0, 0, width, height)
// 回到主线程更新
runOnUiThread {
applyLayoutResult(view)
}
}
}
}
// 内存优化
fun optimizeMemoryUsage() {
// 1. 图片资源优化
imageLoader.setMemoryCacheSize(50 * 1024 * 1024) // 50MB
// 2. 视图层级优化
viewHierarchyOptimizer.optimize(
maxDepth = 10,
mergeSimilarViews = true
)
// 3. 定时清理
scheduledExecutor.scheduleAtFixedRate({
cleanupUnusedResources()
shrinkMemoryIfNeeded()
}, 1, 1, TimeUnit.HOURS)
}
}
5.5 Shadow全动态化的工作流程
5.5.1 完整启动流程
kotlin
class ShadowDynamicFramework {
fun start() {
// 阶段 1:宿主引导
val bootstrap = ShadowBootstrap()
bootstrap.initialize()
// 阶段 2:加载 Manager 插件
val manager = bootstrap.loadManagerPlugin()
manager.init(hostContext, config)
// 阶段 3:Manager 加载 Loader 插件
val loaderPlugins = manager.loadLoaderPlugins()
loaderPlugins.forEach { loader ->
loader.init(hostContext)
}
// 阶段 4:Manager 加载 Container 插件
val containerPlugins = manager.loadContainerPlugins()
containerPlugins.forEach { container ->
container.init(hostContext)
}
// 阶段 5:加载业务插件
val businessPlugins = manager.loadBusinessPlugins()
// 阶段 6:启动业务
businessPlugins.forEach { plugin ->
plugin.start()
}
}
}
5.5.2 热更新流程
kotlin
class HotUpdateManager {
fun hotUpdateComponent(
componentType: ComponentType,
newApk: File,
options: UpdateOptions
): UpdateResult {
// 1. 验证新组件
val verification = verifyComponent(newApk, componentType)
if (!verification.success) {
return UpdateResult.error(verification.error)
}
// 2. 创建备份
createBackup(componentType)
// 3. 应用新组件
return when (componentType) {
ComponentType.MANAGER -> updateManager(newApk, options)
ComponentType.LOADER -> updateLoader(newApk, options)
ComponentType.CONTAINER -> updateContainer(newApk, options)
else -> updateBusinessPlugin(newApk, options)
}
}
private fun updateManager(newApk: File, options: UpdateOptions): UpdateResult {
// 1. 暂停所有插件
pauseAllPlugins()
// 2. 更新 Manager
val result = managerUpdater.update(newApk)
// 3. 恢复插件(使用新 Manager)
if (result.success) {
resumeAllPlugins()
} else {
// 4. 失败时回滚
rollbackManager()
resumeAllPlugins()
}
return result
}
}
5.6 动态化的优势与挑战
5.6.1 技术优势
| 优势维度 | 具体表现 |
|---|---|
| 快速迭代 | 修复时间从天/周级缩短到小时级 |
| 灵活部署 | 支持灰度发布、A/B测试、按需下发 |
| 安全可控 | 可回滚、可降级、可熔断 |
| 资源高效 | 按需加载、共享复用、动态卸载 |
| 兼容性强 | 多版本共存、渐进升级、向前兼容 |
5.6.2 技术挑战与解决方案
| 挑战 | 解决方案 |
|---|---|
| ClassLoader 泄漏 | 弱引用管理 + 定期清理 + 监控告警 |
| 资源 ID 冲突 | 动态重写 ID + 资源映射表 |
| Native 库冲突 | 独立 Native 目录 + 动态链接 |
| 性能开销 | 懒加载 + 预加载 + 缓存优化 |
| 版本兼容性 | 版本检测 + 适配层 + 降级策略 |
| 安全问题 | 签名校验 + 沙箱隔离 + 权限控制 |
5.7 总结
Shadow 框架的全动态化是其核心技术优势,通过将 Manager、Loader、Container 三层核心组件都设计为可动态更新的插件,实现了:
- 技术架构的在线演进:无需发布新宿主版本即可升级技术架构
- 紧急问题的快速响应:小时级的修复能力,而非周级的等待
- 业务策略的灵活调整:可针对不同用户、不同场景制定策略
- 技术栈的自由选择:支持多种技术栈并行发展
- 用户体验的持续优化:可不断更新 UI 框架和性能优化
全动态化不仅解决了 Android 版本兼容性等具体问题,更重要的是为大型应用的长期演进提供了可持续的技术架构支撑,使应用能够快速响应市场变化,持续提供优质的用户体验。
六、插件打包、插件管理与动态更新
6.1 Shadow源码解析之插件打包流程(重点)
6.1.1 插件ZIP的打包方式与内部结构
设计原理 :
Shadow采用双层打包设计:插件APK内部是一个标准的Android应用包,外层是一个包含插件元数据和依赖组件的ZIP包。这种设计实现了插件框架与业务逻辑的完全解耦。
插件包内部结构:
python
plugin.zip
├── config.json # 插件配置文件(核心元数据)
├── plugin-manifest.json # 插件AndroidManifest解析信息
├── plugin.apk # 插件业务APK
├── runtime.apk # 运行时框架(可选)
├── loader.apk # 加载器组件(可选)
└── lib/ # Native库目录
打包流程源码示例:
kotlin
// ShadowPluginAssembleTask.kt - 插件打包任务
open class ShadowPluginAssembleTask : DefaultTask() {
@TaskAction
fun assemblePlugin() {
// 1. 收集所有依赖文件
val pluginApk = collectPluginApk()
val runtimeApk = collectRuntimeApk()
val loaderApk = collectLoaderApk()
// 2. 生成配置文件
val config = generateConfig(pluginApk, runtimeApk, loaderApk)
val manifest = parseManifest(pluginApk)
// 3. 创建插件包ZIP
project.zipTree(pluginApk).files.forEach { file ->
// 动态修改字节码:将Activity父类改为ShadowActivity
if (file.name.endsWith(".dex")) {
transformDexFile(file)
}
}
// 4. 打包成ZIP文件
createPluginZip(config, manifest, pluginApk, runtimeApk, loaderApk)
}
private fun transformDexFile(dexFile: File) {
// 使用Javassist进行字节码转换
val classPool = ClassPool()
classPool.insertClassPath(dexFile.absolutePath)
// 修改Activity继承关系
val activityClass = classPool.get("android.app.Activity")
activityClass.subclasses.forEach { subclass ->
if (shouldTransform(subclass)) {
subclass.superclass = classPool.get("com.tencent.shadow.core.runtime.ShadowActivity")
subclass.writeFile()
}
}
}
}
6.1.2 ConfigJson的设计与作用
设计原理 :
ConfigJson是插件的"身份证",包含了插件的所有元数据信息。采用JSON格式便于序列化/反序列化,支持动态更新和版本兼容。
核心字段说明:
json
{
"version": 4, // 插件包格式版本
"compact_version": 1, // 向下兼容版本
"UUID": "uuid-value", // 插件唯一标识符(内容变化时改变)
"UUID_NickName": "业务名称", // 便于人工识别的昵称
"businessName": "finance", // 业务名称(用于逻辑隔离)
"partKey": "finance-part", // 部件键(用于物理隔离)
"pluginLoader": { // 加载器配置
"className": "com.tencent.shadow.dynamic.loader.impl.CoreLoaderFactoryImpl"
}
}
UUID设计原理:
- 确定性生成:基于插件内容哈希值生成,确保内容相同则UUID相同
- 版本追踪:内容修改后UUID自动变化,便于版本管理
- 唯一性保障:使用UUID算法保证全局唯一性
6.2 Shadow对插件包管理的设计
6.2.1 PluginManager与Plugin
设计原理 :
采用"管理器-插件"两层架构,PluginManager作为统一的管理接口,Plugin作为插件的运行时表示。
源码结构示例:
kotlin
// PluginManager接口 - 统一管理接口
interface PluginManager {
// 插件安装管理
fun installPlugin(pluginInfo: PluginInfo): InstallResult
fun uninstallPlugin(pluginId: String): Boolean
// 插件加载管理
fun loadPlugin(pluginId: String): LoadResult
fun getLoadedPlugin(pluginId: String): Plugin?
// 插件依赖解析
fun resolveDependencies(pluginId: String): List<PluginInfo>
}
// Plugin类 - 插件运行时表示
data class Plugin(
val pluginId: String, // 插件ID
val version: String, // 版本号
val classLoader: ClassLoader, // 插件ClassLoader(隔离核心)
val resources: Resources, // 插件Resources(资源隔离)
val components: ComponentInfo // 组件信息
)
6.2.2 管理器的原理 - ManagerImplLoader
设计原理 :
ManagerImplLoader实现了插件的"元插件化" - 插件管理器本身也是一个插件,可以被动态加载和更新。
核心源码示例:
kotlin
// ManagerImplLoader.kt - 管理器加载器
class ManagerImplLoader(private val context: Context) {
fun loadManagerImpl(managerApk: File): PluginManager {
// 1. 安全验证(签名校验)
if (!verifySignature(managerApk)) {
throw SecurityException("管理器签名验证失败")
}
// 2. 创建管理器ClassLoader(打破双亲委派)
val dexPath = managerApk.absolutePath
val classLoader = PluginClassLoader(
dexPath,
getManagerParentClassLoader(), // 特殊父加载器
context
)
// 3. 加载管理器实现类(反射创建)
val managerClass = classLoader.loadClass(
"com.tencent.shadow.dynamic.manager.DynamicPluginManagerImpl"
)
// 4. 初始化管理器实例
val manager = managerClass.newInstance() as PluginManager
manager.init(context, config)
return manager
}
}
6.3 多插件的加载与管理策略
设计原理 :
采用"依赖图+拓扑排序"的加载策略,支持复杂的插件依赖关系和并行加载。
核心算法:
kotlin
// PluginDependencyResolver.kt - 依赖解析器
class PluginDependencyResolver {
fun resolveLoadOrder(pluginId: String): List<String> {
// 1. 构建依赖有向图
val graph = buildDependencyGraph(pluginId)
// 2. 拓扑排序(检测循环依赖)
val sortedPlugins = topologicalSort(graph)
// 3. 检查版本冲突
checkVersionConflicts(sortedPlugins)
return sortedPlugins
}
private fun buildDependencyGraph(rootPluginId: String): DirectedGraph<String> {
val graph = DefaultDirectedGraph<String>()
// 深度优先遍历构建依赖树
fun dfs(currentId: String) {
val dependencies = getDependencies(currentId)
dependencies.forEach { depId ->
graph.addVertex(depId)
graph.addEdge(currentId, depId)
dfs(depId)
}
}
dfs(rootPluginId)
return graph
}
}
6.4 插件版本控制与热更新机制
设计原理 :
基于"配置文件驱动+版本比对"的更新机制,支持灰度发布和回滚。
PluginConfig与JSON映射:
less
// PluginConfig.kt - 插件配置实体类
@Serializable
data class PluginConfig(
@SerialName("version") val version: Int,
@SerialName("compact_version") val compactVersion: Int,
@SerialName("UUID") val uuid: String,
@SerialName("UUID_NickName") val uuidNickName: String,
@SerialName("businessName") val businessName: String,
@SerialName("partKey") val partKey: String,
@SerialName("plugins") val plugins: List<PluginInfoConfig>
)
// 对应的config.json
{
"version": 4,
"compact_version": 1,
"UUID": "123e4567-e89b-12d3-a456-426614174000",
"UUID_NickName": "finance-module",
"businessName": "finance",
"partKey": "finance-part",
"plugins": [...]
}
热更新机制源码示例:
kotlin
// PluginHotUpdateManager.kt
class PluginHotUpdateManager {
fun performHotUpdate(pluginId: String, newPluginZip: File): UpdateResult {
// 1. 解压新插件到临时目录
val tempDir = extractPlugin(newPluginZip)
// 2. 预验证新插件
if (!prevalidatePlugin(tempDir)) {
return UpdateResult.failed("预验证失败")
}
// 3. 热切换逻辑
return if (canHotSwap(pluginId)) {
// 热替换(无需重启)
hotSwapPlugin(pluginId, tempDir)
} else {
// 冷更新(需要重启)
coldUpdatePlugin(pluginId, tempDir)
}
}
private fun hotSwapPlugin(pluginId: String, newPluginDir: File): UpdateResult {
// 创建新的ClassLoader
val newClassLoader = createPluginClassLoader(newPluginDir)
// 状态迁移:将旧插件的状态迁移到新插件
val oldPlugin = getLoadedPlugin(pluginId)
val migratedState = migratePluginState(oldPlugin, newClassLoader)
// 替换ClassLoader引用
replaceClassLoader(pluginId, newClassLoader)
return UpdateResult.success("热更新完成")
}
}
6.5 插件依赖管理
设计原理 :
采用声明式依赖配置,支持静态依赖分析和动态依赖注入。
依赖配置示例:
css
// 插件A依赖插件B和插件C
{
"plugins": [{
"businessName": "plugin-a",
"dependsOn": ["plugin-b", "plugin-c"]
}],
"dependencies": [
{
"pluginId": "plugin-b",
"minVersion": "1.0.0",
"maxVersion": "2.0.0"
},
{
"pluginId": "plugin-c",
"version": "1.2.0"
}
]
}
依赖解析源码:
kotlin
// DependencyResolver.kt
class DependencyResolver {
fun resolveTransitiveDependencies(pluginId: String): ResolvedDependencies {
// 构建依赖传递闭包
val closure = mutableSetOf<String>()
fun traverse(currentId: String) {
val deps = getDirectDependencies(currentId)
deps.forEach { depId ->
if (closure.add(depId)) {
traverse(depId)
}
}
}
traverse(pluginId)
// 拓扑排序确保加载顺序
val sorted = topologicalSort(closure)
return ResolvedDependencies(
allDependencies = closure.toList(),
loadOrder = sorted
)
}
}
参考博客:
Shadow官方开发的作者: shifujun 的个人主页 - 文章 - 掘金