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

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

引言

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

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


一、Shadow 整体架构概览

1.1 整体架构分层设计

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

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

typescript 复制代码
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
包装插件信息 PluginManager->>系统AMS: 3.启动代理Activity
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 mPlugins +loadPlugin(String apkPath) } class PluginContainerActivity { -Activity mPluginActivity -ActivityContainer mContainer +onCreate(Bundle) +onStart() +onResume() -attachPluginActivity(Activity) } class DynamicPluginLoader { -Map 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 源码项目中:

bash 复制代码
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 文件结构中:

bash 复制代码
宿主 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 的典型调用方式如下:

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

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

java 复制代码
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:

xml 复制代码
<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方法开始执行:

java 复制代码
// 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

kotlin 复制代码
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 创建实例

kotlin 复制代码
// 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() 进行初始化

kotlin 复制代码
// 3. 初始化插件 Activity
initPluginActivity(pluginActivity, pluginActivityInfo)

initPluginActivity() 方法会:

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

第三步:赋值给父类属性

kotlin 复制代码
// 4. 赋值给父类的 pluginActivity 字段
super.pluginActivity = pluginActivity

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

AppComponentFactory 的来源

kotlin 复制代码
// 1. 从插件 Manifest 中获取 AppComponentFactory 配置
val appComponentFactory = pluginManifest.appComponentFactory

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

ShadowAppComponentFactory.instantiateActivity 方法

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

插件 Activity 的类型转换

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

java 复制代码
// ShadowAppComponentFactory 返回的是 ShadowActivity 类型
return (ShadowActivity) cl.loadClass(className).newInstance();

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

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

关键类的继承关系

java 复制代码
// 宿主中的代理 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 手动转发

java 复制代码
// 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() 中:

java 复制代码
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 值得深入研究与集成。

相关推荐
wuhen_n2 小时前
Function Calling解剖:从请求到响应的完整数据流
前端·人工智能·ai编程
程序员清风2 小时前
OpenAI创始人学AI的底层逻辑,普通人照着做就能上手!
java·后端·面试
喝咖啡的女孩2 小时前
多智能体任务可视化界面
前端
whisper2 小时前
#新手必看!Map.size 和 Object.keys().length 的区别,看完再也不混淆
前端
Memory_荒年2 小时前
Netty面试终极指南:从“Hello World”到源码深处
java·后端
0xDevNull2 小时前
Java IO流教程:从入门到最佳实践
java·后端
好家伙VCC2 小时前
**发散创新:用 Rust实现数据编织(DataWrangling)的高效流式处理架构**在现
java·开发语言·python·架构·rust
要开心吖ZSH2 小时前
MP4 转 WAV 音频转码方案详解(ProcessBuilder + FFmpeg)
java·ffmpeg·音视频
秋天的一阵风2 小时前
【LeetCode 刷题系列|第 3 篇】详解大数相加:从模拟竖式到简洁写法的优化之路🔢
前端·算法·面试