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)
加载管理] RUNTIME[Runtime层
运行时容器] PLUGIN[Plugin层
业务插件] LOADER -->|提供运行环境| RUNTIME RUNTIME -->|承载运行| PLUGIN PLUGIN -->|回调接口| LOADER end
各层核心职责:
-
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的全流程。
一、完整加载启动时序图
(PluginLoaderBinder) participant DPL as DynamicPluginLoader
(Loader APK) participant SPL as ShadowPluginLoader
(核心加载器) participant LPB as LoadPluginBloc
(加载模块) participant LAB as LoadApkBloc
(创建ClassLoader) participant CAB as CreateApplicationBloc
(创建Application) participant CM as ComponentManager
(组件管理器) participant Proxy as 代理Activity
(Runtime APK) participant PluginApp as 插件Application participant PluginAct as 插件Activity Note over Host: 1. 启动插件流程开始 Host->>PM: loadPlugin(uuid, partKey) PM->>DPL: 通过Binder调用loadPlugin() Note over DPL: 2. 创建ShadowPluginLoader DPL->>DPL: 创建ShadowPluginLoader实例 DPL->>SPL: onCreate() Note over SPL: 3. 并行加载插件组件 SPL->>LPB: loadPlugin() par 并行任务1 LPB->>LAB: loadPlugin()创建ClassLoader LAB-->>LPB: 返回插件ClassLoader and 并行任务2 LPB->>LPB: 解析PluginManifest LPB-->>LPB: 返回PluginManifest and 并行任务3 LPB->>LPB: 创建Resources LPB-->>LPB: 返回Resources end Note over LPB: 4. 创建Application实例 LPB->>CAB: createShadowApplication() CAB->>PluginApp: newInstance()创建实例 CAB->>PluginApp: attachBaseContext() Note right of PluginApp: 此时onCreate()还未调用 CAB-->>LPB: 返回Application实例 Note over LPB: 5. 组装并缓存PluginParts LPB->>CM: addPluginApkInfo() LPB->>LPB: 创建PluginParts并缓存 LPB-->>SPL: 返回Future SPL-->>DPL: 加载完成 DPL-->>PM: 通过Binder返回结果 PM-->>Host: 加载完成 Note over Host: 6. 启动插件Activity Host->>PM: 启动插件Activity PM->>DPL: convertActivityIntent() Note over DPL: 7. 调用Application.onCreate() alt Application未onCreate DPL->>SPL: callApplicationOnCreate(partKey) SPL->>PluginApp: 获取Application实例 SPL->>SPL: 创建ContentProvider
并调用onCreate() SPL->>PluginApp: onCreate() PluginApp-->>SPL: 完成 end Note over DPL: 8. 创建代理Intent DPL->>DPL: 创建代理Activity的Intent DPL-->>PM: 返回代理Intent PM-->>Host: 返回代理Intent Note over Host: 9. 启动代理Activity Host->>Proxy: startActivity(代理Intent) Note over Proxy: 10. 代理Activity创建插件Activity Proxy->>Proxy: onCreate() Proxy->>SPL: 获取插件ClassLoader SPL-->>Proxy: 返回ClassLoader Proxy->>Proxy: 用插件ClassLoader加载真实Activity类 Proxy->>PluginAct: newInstance()创建实例 Proxy->>PluginAct: attachBaseContext() Proxy->>PluginAct: onCreate() PluginAct-->>Proxy: 完成 Proxy-->>Host: 显示插件界面
二、详细流程分解
阶段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
apkPath, odexDir, libDir, parent) DCL-->>PCL: 返回DexClassLoader实例 PCL-->>PL: 返回PluginClassLoader PL->>PCL: 加载插件Application类 Note over PCL: 类加载隔离流程 PCL->>DCL: loadClass("com.plugin.PluginApp") DCL->>FileSys: 从插件APK加载dex FileSys-->>DCL: 返回dex数据 DCL-->>PCL: 返回Class对象 PCL-->>PL: 返回插件Application Class PL-->>PM: 插件加载完成 PM-->>App: 返回LoadedPlugin实例
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创建与赋值时序图
base=宿主Context,
hostApp=宿主Application,
packageManager,
pluginClassLoader,
componentLauncher) PluginContext->>PluginContext: 1. super(base) 调用ContextWrapper
2. 创建mPluginAssetManager
3. 创建mPluginResources PluginContext-->>LoadedPlugin: 返回PluginContext实例 LoadedPlugin-->>PluginLoader: 返回LoadedPlugin Note over 宿主App,插件Activity: 插件Activity启动阶段 宿主App->>PluginDelegate: 启动插件Activity(intent) PluginDelegate->>插件Activity: 1. loadClass(activityClassName)
2. newInstance() PluginDelegate->>LoadedPlugin: getPluginContext() LoadedPlugin-->>PluginDelegate: 返回PluginContext Note over PluginDelegate,插件Activity: 关键注入步骤 PluginDelegate->>插件Activity: attachPluginActivity(context) Note over 系统Framework: 反射调用Activity.attach() 插件Activity->>系统Framework: Activity.attach(
context=PluginContext,
activityThread,
token,
application=宿主Application,
...) 系统Framework-->>插件Activity: 设置mBase = PluginContext 插件Activity->>插件Activity: 内部状态初始化 PluginDelegate->>插件Activity: onCreate(savedInstanceState) Note over 插件Activity: 运行时Context访问 插件Activity->>插件Activity: getResources() 插件Activity->>PluginContext: 委托调用getResources() PluginContext-->>插件Activity: 返回mPluginResources 插件Activity->>插件Activity: getClassLoader() 插件Activity->>PluginContext: 委托调用getClassLoader() PluginContext-->>插件Activity: 返回mPluginClassLoader Note over PluginContext,宿主App: 系统服务获取 插件Activity->>插件Activity: getSystemService(WINDOW_SERVICE) 插件Activity->>PluginContext: 委托调用getSystemService() PluginContext->>宿主App: super.getSystemService()
(委托给宿主Context) 宿主App-->>插件Activity: 返回WindowManager
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 的个人主页 - 文章 - 掘金