8. Android 深入插件化Shadow源码:揭秘插件Activity启动的完整链路(源码解析)

20 阅读8分钟

最近看了下Shadow,别人写的博客,都是比较旧,几年前的,没基于最新的代码分析,于是我写了这篇文章分享下! 前面还有几篇Shadow实战的,太多,先跳过,后面再补上

引言

在 Android 开发中,插件化技术是实现应用模块化、动态更新与体积优化的重要手段。Shadow 是腾讯开源的一款高性能、零反射、零 Hook 的插件化框架,其核心设计目标是在不破坏 Android 系统机制的前提下,安全、稳定地运行插件代码。

本文将结合源码和官方文档,系统梳理 Shadow 启动插件 Activity 的完整流程,帮助开发者深入理解其底层原理。


一、Shadow 整体架构概览

1.1 整体架构分层设计

Shadow 的整体架构清晰分层,主要包含以下组件:

graph TB
    subgraph "宿主应用 (Host App)"
        A1[宿主AndroidManifest<br/>预注册代理Activity]
        A2[宿主Application]
        A3[入口Activity]
    end
    
    subgraph "管理器层 (Manager Layer)"
        B1[PluginManager<br/>插件管理器]
        B2[UuidManager<br/>插件实例管理]
        B3[ComponentManager<br/>组件管理]
    end
    
    subgraph "加载器层 (Loader Layer)"
        C1[DynamicPluginLoader<br/>动态加载器]
        C2[FixedPluginLoader<br/>固定加载器]
        C3[PluginClassLoader<br/>插件类加载器]
    end
    
    subgraph "运行时层 (Runtime Layer)"
        D1[ShadowActivity<br/>代理基类]
        D2[PluginContainerActivity<br/>容器Activity]
        D3[ShadowContext<br/>上下文代理]
    end
    
    subgraph "插件层 (Plugin Layer)"
        E1[插件APK<br/>业务模块]
        E2[插件Activity<br/>业务逻辑]
        E3[插件资源<br/>独立资源]
    end
    
    A3 --> B1
    B1 --> C1
    C1 --> D1
    D1 --> D2
    D2 --> E2
    E2 --> E3
    
    D3 -.-> C3
    D3 -.-> E3

1.2 核心模块说明

模块职责
Host App集成 Shadow SDK,提供代理容器 Activity
PluginManager插件入口管理,负责路由与调度
PluginLoader加载插件 APK,创建 ClassLoader 和 Resources
Runtime提供插件运行所需的基础能力(Context 代理、生命周期转发等)
Plugin APK独立业务模块,编译为标准 APK

核心思想"宿主壳 + 插件实现" —— 插件 Activity 实际由宿主中预注册的代理 Activity 承载。


二、Shadow启动Activity示例代码

public void startPlugin() {

    PluginHelper.getInstance().singlePool.execute(new Runnable() {
        @Override
        public void run() {
            HostApplication.getApp().loadPluginManager(PluginHelper.getInstance().pluginManagerFile);

            Bundle bundle = new Bundle();
            bundle.putString(TaoDuoduoConstant.KEY_PLUGIN_ZIP_PATH, PluginHelper.getInstance().pluginZipFile.getAbsolutePath());
            bundle.putString(TaoDuoduoConstant.KEY_PLUGIN_PART_KEY, getIntent().getStringExtra(TaoDuoduoConstant.KEY_PLUGIN_PART_KEY));
            bundle.putString(TaoDuoduoConstant.KEY_ACTIVITY_CLASSNAME, getIntent().getStringExtra(TaoDuoduoConstant.KEY_ACTIVITY_CLASSNAME));

            HostApplication.getApp().getPluginManager()
                    .enter(PluginLoadActivity.this, TaoDuoduoConstant.FROM_ID_START_ACTIVITY, bundle, new EnterCallback() {
                        @Override
                        public void onShowLoadingView(final View view) {
                            mHandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    mViewGroup.addView(view);


                                }
                            });
                        }

                        @Override
                        public void onCloseLoadingView() {
                            finish();
                        }

                        @Override
                        public void onEnterComplete() {
                        }
                    });

        }
    });
}

三、架构图与流程图解析

3.1 Shadow插件化整体架构图

graph LR
    subgraph "宿主环境"
        A[宿主Application] --> B[PluginManager]
        B --> C[Runtime环境]
        D[代理Activity容器] --> C
    end
    
    subgraph "插件环境"
        E[插件APK] --> F[插件ClassLoader]
        E --> G[插件Resources]
        F --> H[插件Activity]
        G --> H
    end
    
    subgraph "桥梁层"
        I[ShadowContext代理] --> J[ActivityContainer]
        J --> K[生命周期同步器]
    end
    
    C --> I
    I --> F
    I --> G
    D --> J
    J --> H
    K --> H

3.2 启动插件Activity的核心流程图

sequenceDiagram
    participant 开发者
    participant PluginManager
    participant 系统AMS
    participant 代理Activity
    participant PluginLoader
    participant 插件APK
    participant 插件Activity

    开发者->>PluginManager: 1.调用enter()方法
    PluginManager->>PluginManager: 2.构造代理Intent<br/>包装插件信息
    PluginManager->>系统AMS: 3.启动代理Activity<br/>PluginDefaultProxyActivity
    
    系统AMS->>代理Activity: 4.创建实例,调用onCreate()
    代理Activity->>PluginLoader: 5.请求加载插件
    PluginLoader->>插件APK: 6.创建插件ClassLoader
    PluginLoader->>插件APK: 7.加载插件资源
    PluginLoader->>插件APK: 8.创建插件Activity实例
    
    插件APK-->>PluginLoader: 9.返回插件Activity对象
    PluginLoader-->>代理Activity: 10.返回插件Activity
    代理Activity->>代理Activity: 11.创建ShadowContext
    代理Activity->>代理Activity: 12.替换插件Activity的Context
    代理Activity->>代理Activity: 13.建立生命周期同步
    
    代理Activity->>插件Activity: 14.调用onCreate()
    代理Activity->>插件Activity: 15.调用onStart()
    代理Activity->>插件Activity: 16.调用onResume()
    
    代理Activity-->>开发者: 17.显示插件界面

3.3 核心组件交互图

graph TD
    subgraph "启动阶段"
        A[PluginManager.enter] --> B[创建ShadowIntent]
        B --> C[启动代理Activity]
    end
    
    subgraph "初始化阶段"
        C --> D[代理Activity.onCreate]
        D --> E[解析插件信息]
        E --> F[调用PluginLoader.loadPlugin]
        F --> G[创建插件ClassLoader]
        G --> H[加载插件资源]
        H --> I[创建插件Activity实例]
    end
    
    subgraph "绑定阶段"
        I --> J[创建ShadowContext]
        J --> K[注入插件Activity]
        K --> L[建立ActivityContainer]
        L --> M[转发生命周期]
        M --> N[显示插件UI]
    end
    
    subgraph "运行阶段"
        N --> O[用户交互]
        O --> P[代理Activity接收]
        P --> Q[转发到插件Activity]
        Q --> R[插件处理业务逻辑]
        R --> S[更新UI]
    end

3.4 关键类关系图

classDiagram
    class PluginManager {
        +enter(Context, String, String, Bundle, Callback)
        +startPluginActivity(Intent, int, Bundle)
        -Map<String, PluginInfo> mPlugins
        +loadPlugin(String apkPath)
    }
    
    class PluginContainerActivity {
        -Activity mPluginActivity
        -ActivityContainer mContainer
        +onCreate(Bundle)
        +onStart()
        +onResume()
        -attachPluginActivity(Activity)
    }
    
    class DynamicPluginLoader {
        -Map<String, LoadedPlugin> mLoadedPlugins
        +loadPlugin(String) LoadedPlugin
        +createActivity(String) Activity
        +createAssetManager(String) AssetManager
    }
    
    class LoadedPlugin {
        -ClassLoader mClassLoader
        -Resources mResources
        -AssetManager mAssetManager
        -PluginInfo mPluginInfo
        +getClassLoader()
        +getResources()
    }
    
    class ShadowContext {
        -Context mHostContext
        -LoadedPlugin mPlugin
        +getResources()
        +getClassLoader()
        +getApplicationContext()
    }
    
    class ActivityContainer {
        -Activity mHostActivity
        -Activity mPluginActivity
        +setPluginActivity(Activity)
        +forwardLifecycle()
    }
    
    class PluginActivity {
        <<插件Activity>>
        -Context mBase
        -Application mApplication
        +onCreate(Bundle)
        +onResume()
    }
    
    PluginManager --> DynamicPluginLoader : 使用
    PluginContainerActivity --> DynamicPluginLoader : 调用
    DynamicPluginLoader --> LoadedPlugin : 创建
    PluginContainerActivity --> ActivityContainer : 包含
    ActivityContainer --> PluginActivity : 包装
    ShadowContext --> LoadedPlugin : 引用
    PluginActivity --> ShadowContext : 被注入

四、关键组件位置与部署结构

4.1 具体代码位置证明

在 Shadow 源码项目中:

shadow-sample/
├── host/                          # 宿主模块
│   ├── src/main/
│   │   ├── AndroidManifest.xml    # 包含代理Activity声明
│   │   └── java/com/tencent/shadow/sample/host/
│   │       └── SamplePluginManager.java  # 管理代理Activity
│   └── build.gradle               # 依赖 shadow-core-loader
│
├── plugin-manager/                # PluginManager 实现
│
└── core/
    └── loader/                    # 核心加载器模块
        └── src/main/java/com/tencent/shadow/core/loader/delegates/
            ├── PluginDefaultProxyActivity.java      # 代理Activity实现
            ├── PluginSingleTaskProxyActivity.java
            └── PluginContainerActivity.java         # 代理基类

4.2 实际部署时的位置

在 APK 文件结构中:

宿主 APK (host.apk):
├── AndroidManifest.xml
├── classes.dex
│   └── com/tencent/shadow/core/loader/delegates/
│       └── PluginDefaultProxyActivity.class  ✅ 代理Activity在这里
└── assets/plugins/
    └── plugin.apk                             # 插件APK

插件 APK (plugin.apk):
├── AndroidManifest.xml (仅编译期使用,不安装)
├── classes.dex
│   └── com/example/plugin/
│       └── PluginActivity.class               # 真正的业务Activity
└── res/                                      # 插件资源

五、启动流程详解(附关键源码)

5.1 第一步:PluginManager.enter() → 构造代理 Intent

启动插件 Activity 的典型调用方式如下:

PluginManager.getInstance().enter(
    context,
    "plugin-part-key",          // 插件标识(partKey)
    "com.example.PluginActivity", // 插件 Activity 全类名
    null,                       // Bundle 参数
    null                        // 回调
);

enter() 是启动流程的入口方法,但其背后涉及多层转发与代理机制。PluginManager 是一个接口,通常由 SamplePluginManager 实现。其内部会调用:

PendingIntent pendingIntent = mPluginLoader.getLaunchIntent(...);
context.startActivity(pendingIntent.getIntent());

关键点: Shadow 不会直接启动插件中的 Activity,而是构造一个指向宿主中预注册的占位 Activity(如 PluginDefaultProxyActivity)的 Intent,并将插件信息(类名、partKey 等)作为 extra 传入。

为什么需要占位 Activity? Android 系统要求所有 Activity 必须在 AndroidManifest.xml 中声明,否则会抛出 ActivityNotFoundException。Shadow 通过预注册一组通用代理 Activity 来绕过此限制。

5.2 第二步:宿主中启动代理 Activity(PluginDefaultProxyActivity

宿主 Manifest 中预先声明了多个代理 Activity,用于支持不同 launchMode:

<activity android:name="com.tencent.shadow.core.loader.delegates.PluginDefaultProxyActivity" />
<activity android:name="com.tencent.shadow.core.loader.delegates.PluginSingleTaskProxyActivity"
          android:launchMode="singleTask" />
<!-- ... -->

PluginDefaultProxyActivity 是 Shadow 框架在宿主应用中预先注册的代理容器

PluginDefaultProxyActivity 为例,它继承自 PluginContainerActivity,而后者又继承自 ShadowActivity

当系统启动该代理 Activity 时,会执行其 onCreate() 方法。

5.3 代理Activity的onCreate流程

当系统启动PluginDefaultProxyActivity后,其onCreate方法开始执行:

// PluginContainerActivity.java (基类)
@Override
final protected void onCreate(Bundle savedInstanceState) {
    isBeforeOnCreate = false;
    mHostTheme = null;//释放资源

    boolean illegalIntent = isIllegalIntent(savedInstanceState);
    if (illegalIntent) {
        super.hostActivityDelegate = null;
        hostActivityDelegate = null;
        Log.e(TAG, "illegalIntent savedInstanceState==" + savedInstanceState + " getIntent().getExtras()==" + getIntent().getExtras());
    }

    if (hostActivityDelegate != null) {
        hostActivityDelegate.onCreate(savedInstanceState);
    } else {
        //这里是进程被杀后重启后走到,当需要恢复fragment状态的时候,由于系统保留了TAG,会因为找不到fragment引起crash
        super.onCreate(null);
        Log.e(TAG, "onCreate: hostActivityDelegate==null finish activity");
        finish();
        System.exit(0);
    }
}

5.4 ShadowActivityDelegate

override fun onCreate(savedInstanceState: Bundle?) {
    val pluginInitBundle = savedInstanceState ?: mHostActivityDelegator.intent.extras!!

    mCallingActivity = pluginInitBundle.getParcelable(CM_CALLING_ACTIVITY_KEY)
    mBusinessName = pluginInitBundle.getString(CM_BUSINESS_NAME_KEY, "")
    val partKey = pluginInitBundle.getString(CM_PART_KEY)!!
    mPartKey = partKey
    mDI.inject(this, partKey)
    mDependenciesInjected = true

    val bundleForPluginLoader = pluginInitBundle.getBundle(CM_LOADER_BUNDLE_KEY)!!
    mBundleForPluginLoader = bundleForPluginLoader
    bundleForPluginLoader.classLoader = this.javaClass.classLoader
    val pluginActivityClassName = bundleForPluginLoader.getString(CM_CLASS_NAME_KEY)!!
    val pluginActivityInfo: PluginManifest.ActivityInfo =
        bundleForPluginLoader.getParcelable(CM_ACTIVITY_INFO_KEY)!!
    mPluginActivityInfo = pluginActivityInfo

    mCurrentConfiguration = Configuration(resources.configuration)
    mPluginHandleConfigurationChange =
        (pluginActivityInfo.configChanges
                or ActivityInfo.CONFIG_SCREEN_SIZE//系统本身就会单独对待这个属性,不声明也不会重启Activity。
                or ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE//系统本身就会单独对待这个属性,不声明也不会重启Activity。
                or 0x20000000 //见ActivityInfo.CONFIG_WINDOW_CONFIGURATION 系统处理属性
                )
    if (savedInstanceState == null) {
        mRawIntentExtraBundle = pluginInitBundle.getBundle(CM_EXTRAS_BUNDLE_KEY)
        mHostActivityDelegator.intent.replaceExtras(mRawIntentExtraBundle)
    }
    mHostActivityDelegator.intent.setExtrasClassLoader(mPluginClassLoader)

    try {
        val pluginActivity = mAppComponentFactory.instantiateActivity(
            mPluginClassLoader,
            pluginActivityClassName,
            mHostActivityDelegator.intent
        )
        initPluginActivity(pluginActivity, pluginActivityInfo)
        super.pluginActivity = pluginActivity

        if (mLogger.isDebugEnabled) {
            mLogger.debug(
                "{} mPluginHandleConfigurationChange=={}",
                mPluginActivity.javaClass.canonicalName,
                mPluginHandleConfigurationChange
            )
        }

        //使PluginActivity替代ContainerActivity接收Window的Callback
        mHostActivityDelegator.window.callback = pluginActivity

        //设置插件AndroidManifest.xml 中注册的WindowSoftInputMode
        mHostActivityDelegator.window.setSoftInputMode(pluginActivityInfo.softInputMode)

        //Activity.onCreate调用之前应该先收到onWindowAttributesChanged。
        if (mCallOnWindowAttributesChanged) {
            pluginActivity.onWindowAttributesChanged(
                mBeforeOnCreateOnWindowAttributesChangedCalledParams
            )
            mBeforeOnCreateOnWindowAttributesChangedCalledParams = null
        }

        val pluginSavedInstanceState: Bundle? =
            savedInstanceState?.getBundle(PLUGIN_OUT_STATE_KEY)
        pluginSavedInstanceState?.classLoader = mPluginClassLoader
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            notifyPluginActivityPreCreated(pluginActivity, pluginSavedInstanceState)
        }
        pluginActivity.onCreate(pluginSavedInstanceState)
        mPluginActivityCreated = true
    } catch (e: Exception) {
        throw RuntimeException(e)
    }
}

5.5 插件Activity实例化与赋值流程

插件Activity (pluginActivity) 的赋值过程可以概括为:

  1. 获取类名:从启动参数中提取插件 Activity 的完整类名
  2. 加载类:使用插件的 ClassLoader 加载指定的类
  3. 创建实例:通过反射调用 newInstance() 创建对象
  4. 类型转换:将实例转换为 ShadowActivity 类型
  5. 初始化:通过 initPluginActivity() 设置上下文、资源等
  6. 赋值:将初始化后的实例赋值给 super.pluginActivity

完整的赋值时序图

sequenceDiagram
    participant 宿主Activity
    participant ShadowActivityDelegate
    participant AppComponentFactory
    participant PluginClassLoader
    participant 插件Activity类
    participant 插件Activity实例

    宿主Activity->>ShadowActivityDelegate: onCreate()
    ShadowActivityDelegate->>ShadowActivityDelegate: 解析插件信息
    ShadowActivityDelegate->>AppComponentFactory: instantiateActivity()
    AppComponentFactory->>PluginClassLoader: loadClass(className)
    PluginClassLoader->>插件Activity类: 加载类
    插件Activity类-->>PluginClassLoader: 返回Class对象
    AppComponentFactory->>插件Activity类: newInstance()
    插件Activity类->>插件Activity实例: 创建实例
    插件Activity实例-->>AppComponentFactory: 返回实例
    AppComponentFactory-->>ShadowActivityDelegate: 返回插件Activity实例
    ShadowActivityDelegate->>ShadowActivityDelegate: initPluginActivity()
    ShadowActivityDelegate->>ShadowActivityDelegate: 设置Context、Application等
    ShadowActivityDelegate->>ShadowActivityDelegate: super.pluginActivity = pluginActivity
    ShadowActivityDelegate->>插件Activity实例: onCreate()
    插件Activity实例-->>宿主Activity: 完成初始化

第一步:通过 AppComponentFactory 创建实例

// 1. 获取插件 Activity 的类名
val pluginActivityClassName = bundleForPluginLoader.getString(CM_CLASS_NAME_KEY)!!

// 2. 使用 AppComponentFactory 创建插件 Activity 实例
val pluginActivity = mAppComponentFactory.instantiateActivity(
    mPluginClassLoader,           // 插件 ClassLoader
    pluginActivityClassName,      // 插件 Activity 完整类名
    mHostActivityDelegator.intent // Intent 参数
)

第二步:调用 initPluginActivity() 进行初始化

// 3. 初始化插件 Activity
initPluginActivity(pluginActivity, pluginActivityInfo)

initPluginActivity() 方法会:

  • 设置插件 Activity 的 Context(ShadowContext)
  • 设置插件 Application
  • 设置 Window 和 WindowManager
  • 设置主题等

第三步:赋值给父类属性

// 4. 赋值给父类的 pluginActivity 字段
super.pluginActivity = pluginActivity

这里 super 指的是 PluginContainerActivity 或类似基类。

AppComponentFactory 的来源

// 1. 从插件 Manifest 中获取 AppComponentFactory 配置
val appComponentFactory = pluginManifest.appComponentFactory

// 2. 创建 AppComponentFactory 实例
val clazz = pluginClassLoader.loadClass(appComponentFactory)
ShadowAppComponentFactory::class.java.cast(clazz.newInstance())

ShadowAppComponentFactory.instantiateActivity 方法

public class ShadowAppComponentFactory {
    public ShadowActivity instantiateActivity(ClassLoader cl, String className, Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        // 使用插件 ClassLoader 加载类并创建实例
        return (ShadowActivity) cl.loadClass(className).newInstance();
    }
}

插件 Activity 的类型转换

注意实例化后的类型转换:

// ShadowAppComponentFactory 返回的是 ShadowActivity 类型
return (ShadowActivity) cl.loadClass(className).newInstance();

// 但插件中的 Activity 实际上继承自 ShadowActivity(编译期插桩修改)
// 编译前的插件 Activity:
public class PluginMainActivity extends Activity { ... }

// 编译后(经过 Shadow Transform 插桩):
public class PluginMainActivity extends ShadowActivity { ... }

关键类的继承关系

// 宿主中的代理 Activity
public class PluginDefaultProxyActivity 
       extends PluginContainerActivity
       implements ComponentManager

// 插件中的 Activity(编译后)
public class PluginMainActivity 
       extends ShadowActivity  // 编译期插桩修改的父类
       
// ShadowActivity 是框架提供的基类
public abstract class ShadowActivity extends Activity {
    // 提供插件化所需的各种代理方法
}

5.6 生命周期同步(手动转发)

由于插件 Activity 不是系统管理的组件,其生命周期需由宿主代理 Activity 手动转发

// ShadowActivity.java
@Override
protected void onResume() {
    super.onResume();
    if (mPluginActivity != null) {
        mPluginActivity.onResume(); // 手动调用
    }
}

@Override
protected void onPause() {
    if (mPluginActivity != null) {
        mPluginActivity.onPause();
    }
    super.onPause();
}

public class ShadowActivity extends PluginActivity {

    @Override
    public void setContentView(int layoutResID) {
        if ("merge".equals(XmlPullParserUtil.getLayoutStartTagName(getResources(), layoutResID))) {
            //如果传进来的xml文件的根tag是merge时,需要特殊处理
            View decorView = hostActivityDelegator.getWindow().getDecorView();
            ViewGroup viewGroup = decorView.findViewById(android.R.id.content);
            LayoutInflater.from(this).inflate(layoutResID, viewGroup);
        } else {
            View inflate = LayoutInflater.from(this).inflate(layoutResID, null);
            hostActivityDelegator.setContentView(inflate);
        }
    }

    @Override
    public final ShadowApplication getApplication() {
        return mPluginApplication;
    }

    @Override
    public final ShadowActivity getParent() {
        return null;
    }

    @Override
    public void overridePendingTransition(int enterAnim, int exitAnim) {
        //如果使用的资源不是系统资源,我们无法支持这个特性。
        if ((enterAnim & 0xFF000000) != 0x01000000) {
            enterAnim = 0;
        }
        if ((exitAnim & 0xFF000000) != 0x01000000) {
            exitAnim = 0;
        }
        hostActivityDelegator.overridePendingTransition(enterAnim, exitAnim);
    }

    @Override
    public void startActivityForResult(Intent intent, int requestCode) {
        startActivityForResult(intent, requestCode, null);
    }

    @Override
    public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
        final Intent pluginIntent = new Intent(intent);
        pluginIntent.setExtrasClassLoader(mPluginClassLoader);
        ComponentName callingActivity = new ComponentName(getPackageName(), getClass().getName());
        final boolean success = mPluginComponentLauncher.startActivityForResult(hostActivityDelegator, pluginIntent, requestCode, options, callingActivity);
        if (!success) {
            hostActivityDelegator.startActivityForResult(intent, requestCode, options);
        }
    }

    @Override
    public SharedPreferences getPreferences(int mode) {
        return super.getSharedPreferences(getLocalClassName(), mode);
    }

    @Override
    public String getLocalClassName() {
        return this.getClass().getName();
    }

    @Override
    public boolean shouldUpRecreateTask(Intent targetIntent) {
        Intent intent = mPluginComponentLauncher.convertPluginActivityIntent(targetIntent);
        return hostActivityDelegator.shouldUpRecreateTask(intent);
    }

    @Override
    public boolean navigateUpTo(Intent upIntent) {
        Intent intent = mPluginComponentLauncher.convertPluginActivityIntent(upIntent);
        return hostActivityDelegator.navigateUpTo(intent);
    }

此外,onBackPressed()onActivityResult() 等回调也会被代理转发。

5.7 Context 替换与资源隔离

为了使插件 Activity 能正确访问资源、主题、ClassLoader 等,Shadow 会:

  • 创建 ShadowContext,封装插件的 ResourcesAssetManagerClassLoader
  • 通过 编译期字节码插桩(Transform)将插件中所有 this(作为 Context)替换为 getShadowDelegate().getPluginContext()
  • 在运行时通过 ReflectUtil.setField(activity, "mBase", shadowContext) 注入上下文(部分版本使用 delegate 机制避免反射)

"零反射"实现:Shadow 尽量在编译期完成适配,仅在必要时使用少量反射(如设置 mBase),相比其他框架大幅降低兼容性风险。

5.8 插件资源与类加载隔离

每个插件拥有独立的:

  • DexClassLoader:加载插件 DEX
  • AssetManager:加载插件资源(通过 addAssetPath()
  • Resources:基于插件 AssetManager 构建

DynamicPluginLoader.loadPlugin() 中:

DexClassLoader classLoader = new DexClassLoader(
    apkPath,
    optDir.getAbsolutePath(),
    libDir.getAbsolutePath(),
    getHostClassLoader()
);

AssetManager assetManager = createAssetManager(apkPath);
Resources resources = new Resources(assetManager, ..., ...);

这样确保插件资源与宿主完全隔离,同时支持跨插件或访问宿主资源(通过配置)。


六、关键设计思想总结

技术挑战Shadow 解决方案
Manifest 限制宿主预注册通用代理 Activity(Placeholder)
Activity 生命周期容器 Activity 手动转发所有生命周期回调
Context 正确性编译期插桩 + ShadowContext 代理
资源隔离每个插件独立 AssetManagerResources
无反射 / 无 Hook基于接口 + 代码生成 + 字节码修改,规避系统限制

七、结语

Shadow 通过 "代理容器 + 编译期插桩 + 运行时隔离" 的组合策略,实现了高度兼容、稳定可靠的插件化方案。其启动插件 Activity 的流程虽涉及多层抽象,但逻辑清晰、职责分明,是 Android 插件化技术中的优秀实践。

对于希望构建大型模块化 App 或实现热更新能力的团队,Shadow 值得深入研究与集成。