1 启动流程
2 Activiy启动
3 Activiy栈关系
4 Task
String affinity; // 是指root activity的affinity,即该Task中第一个
final int mTaskId; // 任务栈的ID
int mCallingUid; // 调用者的UID
String mCallingPackage; //调用者的包名
ActivityRecord mPausingActivity //正在pause
ActivityRecord mLastPausedActivity
ActivityRecord mResumedActivity //已经resumed
5 ActivityTask
ArrayList activities // 当前task的所有Activity列表
6 ActivityRecord
Task task:跑在那个Task
ActivityInfo info:Activity信息
ActivityState mState:Activity状态
ApplicationInfo appInfo:跑在哪个app
String packageName:包名
String processName:进程名
String taskAffinity;
int launchMode:启动模式
int mUserId:该Activity运行在哪个用户id
@ActivityType int mActivityType:Activity类型
frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java
7 实现启动一个没注册的Activity的思路
通过 动态代理 和 反射 实现 Hook
在 Android 系统中,Activity 默认需在 AndroidManifest.xml
中注册才能启动。动态加载未注册的 Activity 主要依赖 占位 Activity 和 Hook 技术,结合类加载机制实现。以下是具体方案:
一、核心实现原理
-
占位 Activity 替换
-
Hook AMS 通信
-
类加载与生命周期管理
二、具体实现步骤
-
创建占位 Activity
xml 复制 <!-- AndroidManifest.xml 中注册占位 Activity --> <activity android:name=".StubActivity" />
-
Hook AMS 拦截启动请求
kotlin 复制 // 替换 IActivityManager 代理对象 val singletonClass = Class.forName("android.app.ActivityManagerNative") val singletonField = singletonClass.getDeclaredField("gDefault") val singleton = singletonField.get(null) val mInstanceField = singleton.javaClass.getDeclaredField("mInstance") mInstanceField.isAccessible = true val originalAMS = mInstanceField.get(singleton) // 动态代理修改 Intent val proxy = Proxy.newProxyInstance( classLoader, arrayOf(Class.forName("android.app.IActivityManager")), InvocationHandler { proxy, method, args -> if (method.name == "startActivity") { // 将原始 Intent 替换为 StubActivity 的 Intent val rawIntent = args[2]() as Intent val stubIntent = Intent().apply { setClassName(rawIntent.component?.packageName ?: "", StubActivity::class.java.name) putExtra("TARGET_INTENT", rawIntent) } args[2]() = stubIntent } method.invoke(originalAMS, *args) } ) mInstanceField.set(singleton, proxy)
-
恢复目标 Activity
kotlin 复制 // 在 Instrumentation 回调中恢复原始 Intent val activityThreadClass = Class.forName("android.app.ActivityThread") val currentActivityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null) val mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation") mInstrumentationField.isAccessible = true val originalInstrumentation = mInstrumentationField.get(currentActivityThread) as Instrumentation // 自定义 Instrumentation 实现恢复逻辑 val proxyInstrumentation = object : Instrumentation() { override fun newActivity(cl: ClassLoader, className: String, intent: Intent): Activity { val targetIntent = intent.getParcelableExtra<Intent>("TARGET_INTENT") return super.newActivity(cl, targetIntent?.component?.className ?: className, targetIntent ?: intent) } } mInstrumentationField.set(currentActivityThread, proxyInstrumentation)
-
动态加载外部 Activity
kotlin 复制 // 加载 APK/DEX 中的类 val dexPath = "/sdcard/plugin.apk" val optimizedDir = context.getDir("dex", Context.MODE_PRIVATE).absolutePath val classLoader = DexClassLoader(dexPath, optimizedDir, null, context.classLoader) // 替换 LoadedApk 的 ClassLoader val loadedApkField = context.javaClass.getDeclaredField("mLoadedApk") loadedApkField.isAccessible = true val loadedApk = loadedApkField.get(context) val mClassLoaderField = loadedApk.javaClass.getDeclaredField("mClassLoader") mClassLoaderField.isAccessible = true mClassLoaderField.set(loadedApk, classLoader)
三、注意事项与优化
-
兼容性问题
- Android 10+ 使用
IActivityTaskManager
替代IActivityManager
,需调整 Hook 逻辑4。 - 不同厂商系统可能对 AMS 实现有差异,需测试适配。
- Android 10+ 使用
-
资源加载
- 外部 APK 的布局资源需通过
AssetManager
加载,或使用宿主 App 的资源3。
- 外部 APK 的布局资源需通过
-
性能与稳定性
- 避免频繁 Hook 系统服务,防止 ANR 或崩溃。
- 使用
try-catch
包裹反射代码,确保兼容性4。
通过上述方案,可实现动态加载未注册 Activity,适用于插件化开发、热修复等场景。具体实现需结合业务需求调整 Hook 粒度与资源管理策略。