在Java平台要作到动态运行模块、热插拔可使用ClassLoader
技术进行动态类加载,好比普遍使用的OSGi
技术。在Android上固然也可使用动态加载技术,可是仅仅把类加载进来就足够了吗?Activity
,Service
等组件是有生命周期的,它们统一由系统服务AMS
管理;使用ClassLoader
能够从插件中建立Activity对象,可是,一个没有生命周期的Activity对象有什么用?因此在Android系统上,仅仅完成动态类加载是不够的;咱们须要想办法把咱们加载进来的Activity等组件交给系统管理,让AMS
赋予组件生命周期;这样才算是一个有血有肉的完善的插件化方案。java
接下来的系列文章会讲述 DroidPlugin对于Android四大组件的处理方式,咱们且看它如何采用Hook技术坑蒙拐骗把系统玩弄于股掌之中,最终赋予Activity,Service等组件生命周期,完成借尸还魂的。android
首先,咱们来看看DroidPlugin对于Activity
组件的处理方式。git
阅读本文以前,能够先clone一份 understand-plugin-framework,参考此项目的intercept-activity模块。另外,若是对于Hook技术不甚了解,请先查阅我以前的文章:github
读到这里,或许有部分读者以为疑惑了,启动Activity不就是一个startActivity
的事吗,有这么神秘兮兮的?app
启动Activity确实很是简单,可是Android却有一个限制:必须在AndroidManifest.xml中显示声明使用的Activity;我相信读者确定会遇到下面这种异常:框架
1 2 3 |
03-18 15:29:56.074 20709-20709/com.weishu.intercept_activity.app E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: com.weishu.intercept_activity.app, PID: 20709 android.content.ActivityNotFoundException: Unable to find explicit activity class {com.weishu.intercept_activity.app/com.weishu.intercept_activity.app.TargetActivity}; have you declared this activity in your AndroidManifest.xml? |
『必须在AndroidManifest.xml中显示声明使用的Activity』这个硬性要求很大程度上限制了插件系统的发挥:假设咱们须要启动一个插件的Activity,插件使用的Activity是没法预知的,这样确定也不会在Manifest文件中声明;若是插件新添加一个Activity,主程序的AndroidManifest.xml就须要更新;既然双方都须要修改升级,何须要使用插件呢?这已经违背了动态加载的初衷:不修改插件框架而动态扩展功能。ide
能不能想办法绕过这个限制呢?函数
一筹莫展啊,怎么办?借刀杀人偷梁换柱无中生有以逸待劳乘火打劫瞒天过海…等等!偷梁换柱瞒天过海?貌似能够一试。学习
咱们能够耍个障眼法:既然AndroidManifest文件中必须声明,那么我就声明一个(或者有限个)替身Activity好了,当须要启动插件的某个Activity的时候,先让系统觉得启动的是AndroidManifest中声明的那个替身,暂时骗过系统;而后到合适的时候又替换回咱们须要启动的真正的Activity;所谓瞒天过海,莫过如此!this
如今有了方案了,可是该如何作呢?兵书又说,知己知彼百战不殆!若是连Activity的启动过程都不熟悉,怎么完成这个瞒天过海的过程?
启动Activity很是简单,一个startActivity
就完事了;那么在这个简单调用的背后发生了什么呢?Look the fucking source code!
关于Activity 的启动过程,也不是三言两语能解释清楚的,若是按照源码一步一步走下来,插件化系列文章就不用写了;因此这里我就给出一个大体流程,只列出关键的调用点(以Android 6.0源码为例);若是读者但愿更详细的讲解,能够参考老罗的 Android应用程序的Activity启动过程简要介绍和学习计划
首先是Activity类的startActivity
方法:
1 2 3 |
public void startActivity(Intent intent) { startActivity(intent, null); } |
跟着这个方法一步一步跟踪,会发现它最后在startActivityForResult
里面调用了Instrument对象的execStartActivity
方法;接着在这个函数里面调用了ActivityManagerNative类的startActivity
方法;这个过程在前文已经反复举例讲解了,咱们知道接下来会经过Binder IPC到AMS
所在进程调用AMS
的startActivity
方法;Android系统的组件生命周期管理就是在AMS
里面完成的,那么在AMS
里面到底作了什么呢?
ActivityManagerService的startActivity
方法以下:
1 2 3 4 5 6 7 8 |
public final int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile, ParcelFileDescriptor profileFd, Bundle options) { return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd, options, UserHandle.getCallingUserId()); } |
很简单,直接调用了startActivityAsUser
这个方法;接着是ActivityStackSupervisor
类的startActivityMayWait
方法。这个ActivityStackSupervisor类究竟是个啥?若是仔细查阅,低版本的Android源码上是没有这个类的;后来AMS的代码进行了部分重构,关于Activity栈管理的部分单独提取出来成为了ActivityStackSupervisor
类;好了,继续看代码。
startActivityMayWait这个方法前面对参数进行了一系列处理,咱们须要知道的是,在这个方法内部对传进来的Intent进行了解析,并尝试从中取出关于启动Activity的信息。
而后这个方法调用了startActivityLocked方法;在startActivityLocked方法内部进行了一系列重要的检查:好比权限检查,Activity的exported属性检查等等;咱们上文所述的,启动没有在Manifestfest中显示声明的Activity抛异常也是这里发生的:
1 2 3 4 5 |
if (err == ActivityManager.START_SUCCESS && aInfo == null) { // We couldn't find the specific class specified in the Intent. // Also the end of the line. err = ActivityManager.START_CLASS_NOT_FOUND; } |
这里返回ActivityManager.START_CLASS_NOT_FOUND以后,在Instrument的execStartActivity返回以后会检查这个值,而后跑出异常:
1 2 3 4 5 6 |
case ActivityManager.START_CLASS_NOT_FOUND: if (intent instanceof Intent && ((Intent)intent).getComponent() != null) throw new ActivityNotFoundException( "Unable to find explicit activity class " + ((Intent)intent).getComponent().toShortString() + "; have you declared this activity in your AndroidManifest.xml?"); |
源码看到这里,咱们已经确认了『必须在AndroidManifest.xml中显示声明使用的Activity』的缘由;然而这个校检过程发生在AMS
所在的进程system_server
,咱们没有办法篡改,只能另寻他路。
OK,咱们继续跟踪源码;在startActivityLocked以后处理的都是Activity任务栈相关内容,这一系列ActivityStack和ActivityStackSupervisor纠缠不清的调用看下图就明白了;不明白也不要紧: D 目前用处不大。
这一系列调用最终到达了ActivityStackSupervisor的realStartActivityLocked方法;人如其名,这个方法开始了真正的“启动Activity”:它调用了ApplicationThread的scheduleLaunchActivity方法,开始了真正的Activity对象建立以及启动过程。
这个ApplicationThread是什么,是一个线程吗?与ActivityThread有什么区别和联系?
不要被名字迷惑了,这个ApplicationThread其实是一个Binder对象,是App所在的进程与AMS所在进程system_server通讯的桥梁;在Activity启动的过程当中,App进程会频繁地与AMS进程进行通讯:
App进程与AMS进程的通讯过程如图所示:
App进程内部的ApplicationThread server端内部有本身的Binder线程池,它与App主线程的通讯经过Handler完成,这个Handler存在于ActivityThread类,它的名字很简单就叫H
,这一点咱们接下来就会讲到。
如今咱们明白了这个ApplicationThread究竟是个什么东西,接上文继续跟踪Activity的启动过程;咱们查看ApplicationThread的scheduleLaunchActivity
方法,这个方法很简单,就是包装了参数最终使用Handler发了一个消息。
正如刚刚所说,ApplicationThread所在的Binder服务端使用Handler与主线程进行通讯,这里的scheduleLaunchActivity方法直接把启动Activity的任务经过一个消息转发给了主线程;咱们查看Handler类对于这个消息的处理:
1 2 3 4 5 6 7 |
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); ActivityClientRecord r = (ActivityClientRecord)msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
能够看到,这里直接调用了ActivityThread的handleLaunchActivity方法,在这个方法内部有一句很是重要:
1 |
Activity a = performLaunchActivity(r, customIntent); |
绕了这么多弯,咱们的Activity终于被建立出来了!继续跟踪这个performLaunchActivity方法看看发生了什么;因为这个方法较长,我就不贴代码了,读者能够自行查阅;要指出的是,这个方法作了两件很重要的事情:
1 2 3 4 5 |
java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); |
1 2 3 4 5 6 7 8 |
Application app = r.packageInfo.makeApplication(false, mInstrumentation); // ... 省略 if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } |
Activity的启动过程到这里就结束了,可能读者仍是以为迷惑:不就是调用了一系列方法吗?具体作了什么仍是不太清楚,并且为何Android要这么设计?
方法调用链再长也木有关系,有两点须要明白:
Application, Activity
除了表明Android应用层一般所表明的“组件”以外,它们其实都是普通的Java对象,也是须要被构造函数构造出来的对象的;在这个过程当中,咱们明白了这些对象究竟是如何被建立的。AMS
正如名字所说,管理全部的“活动”,整个系统的Activity堆栈,Activity生命周期回调都是由AMS所在的系统进程system_server帮开发者完成的;Android的Framework层帮忙完成了诸如生命周期管理等繁琐复杂的过程,简化了应用层的开发。经过上文的分析,咱们已经对Activity的启动过程了如指掌了;就让咱们干点坏事吧 :D
对与『必须在AndroidManifest.xml中显示声明使用的Activity』这个问题,上文给出了思路——瞒天过海;咱们能够在AndroidManifest.xml里面声明一个替身Activity,而后在合适的时候把这个假的替换成咱们真正须要启动的Activity就OK了。
那么问题来了,『合适的时候』究竟是何时?在前文Hook机制之动态代理中咱们提到过Hook过程最重要的一步是寻找Hook点;若是是在同一个进程,startActivity
到Activity真正启动起来这么长的调用链,咱们随便找个地方Hook掉就完事儿了;可是问题木有这么简单。
Activity启动过程当中不少重要的操做(正如上文分析的『必须在AndroidManifest.xml中显式声明要启动的Activity』)都不是在App进程里面执行的,而是在AMS所在的系统进程system_server完成,因为进程隔离的存在,咱们对别的进程无能为力;因此这个Hook点就须要花点心思了。
这时候Activity启动过程的知识就派上用场了;虽然整个启动过程很是复杂,但其实一张图就能总结:
先从App进程调用startActivity
;而后经过IPC调用进入系统进程system_server,完成Activity管理以及一些校检工做,最后又回到了APP进程完成真正的Activioty对象建立。
因为这个检验过程是在AMS进程完成的,咱们对system_server进程里面的操做无能为力,只有在咱们APP进程里面执行的过程才是有可能被Hook掉的,也就是第一步和第三步;具体应该怎么办呢?
既然须要一个显式声明的Activity,那就声明一个!能够在第一步伪装启动一个已经在AndroidManifest.xml里面声明过的替身Activity,让这个Activity进入AMS进程接受检验;最后在第三步的时候换成咱们真正须要启动的Activity;这样就成功欺骗了AMS进程,瞒天过海!
说到这里,是否是有点小激动呢?咱们写个demo验证一下:『启动一个并无在AndroidManifest.xml中显示声明的Activity』
具体来讲,咱们打算实现以下功能:在MainActivity中启动一个并无在AndroidManifest.xml中声明的TargetActivity;按照上文分析,咱们须要声明一个替身Activity,咱们叫它StubActivity;
那么,咱们的AndroidManifest.xml以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.weishu.intercept_activity.app"> <application android:allowBackup="true" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" > <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!-- 替身Activity, 用来欺骗AMS --> <activity android:name=".StubActivity"/> </application> </manifest> |
OK,那么咱们启动TargetActivity很简单,就是个startActivity
调用的事:
1 |
startActivity(new Intent(MainActivity.this, TargetActivity.class)); |
若是你直接这么运行,确定会直接抛出ActivityNotFoundException而后直接退出;咱们接下来要作的就是让这个调用成功启动TargetActivity。
因为AMS
进程会对Activity作显式声明验证,所以在
启动Activity的控制权转移到AMS
进程以前,咱们须要想办法临时把TargetActivity替换成替身StubActivity;在这之间有很长的一段调用链,咱们能够轻松Hook掉;选择什么地方Hook是一个很自由的事情,可是Hook的步骤越后越可靠——Hook得越早,后面的调用就越复杂,越容易出错。
咱们能够选择在进入AMS
进程的入口进行Hook,具体来讲也就是Hook AMS
在本进程的代理对象ActivityManagerNative。若是你不知道如何Hook掉这个AMS的代理对象,请查阅我以前的文章Hook机制之AMS&PMS
咱们Hook掉ActivityManagerNative对于startActivity方法的调用,替换掉交给AMS的intent对象,将里面的TargetActivity的暂时替换成已经声明好的替身StubActivity;这种Hook方式 前文 讲述的很详细,不赘述;替换的关键代码以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
if ("startActivity".equals(method.getName())) { // 只拦截这个方法 // 替换参数, 任你所为;甚至替换原始Activity启动别的Activity偷梁换柱 // API 23: // public final Activity startActivityNow(Activity parent, String id, // Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state, // Activity.NonConfigurationInstances lastNonConfigurationInstances) { // 找到参数里面的第一个Intent 对象 Intent raw; int index = 0; for (int i = 0; i < args.length; i++) { if (args[i] instanceof Intent) { index = i; break; } } raw = (Intent) args[index]; Intent newIntent = new Intent(); // 这里包名直接写死,若是再插件里,不一样的插件有不一样的包 传递插件的包名便可 String targetPackage = "com.weishu.intercept_activity.app"; // 这里咱们把启动的Activity临时替换为 StubActivity ComponentName componentName = new ComponentName(targetPackage, StubActivity.class.getCanonicalName()); newIntent.setComponent(componentName); // 把咱们原始要启动的TargetActivity先存起来 newIntent.putExtra(HookHelper.EXTRA_TARGET_INTENT, raw); // 替换掉Intent, 达到欺骗AMS的目的 args[index] = newIntent; Log.d(TAG, "hook success"); return method.invoke(mBase, args); } return method.invoke(mBase, args); |
经过这个替换过程,在ActivityManagerNative的startActivity调用以后,system_server端收到Binder驱动的消息,开始执行ActivityManagerService里面真正的startActivity
方法;这时候AMS看到的intent
参数里面的组件已是StubActivity
了,所以能够成功绕过检查,这时候若是不作后面的Hook,直接调用
1 |
startActivity(new Intent(MainActivity.this, TargetActivity.class)); |
也不会出现上文的ActivityNotFoundException
行百里者半九十。如今咱们的startActivity
启动一个没有显式声明的Activity已经不会抛异常了,可是要真正正确地把TargetActivity启动起来,还有一些事情要作。其中最重要的一点是,咱们用替身StubActivity临时换了TargetActivity,确定须要在『合适的』时候替换回来;接下来咱们就完成这个过程。
在AMS进程里面咱们是没有办法换回来的,所以咱们要等AMS把控制权交给App所在进程,也就是上面那个『Activity启动过程简图』的第三步。AMS进程转移到App进程也是经过Binder调用完成的,承载这个功能的Binder对象是IApplicationThread;在App进程它是Server端,在Server端接受Binder远程调用的是Binder线程池,Binder线程池经过Handler将消息转发给App的主线程;(我这里不厌其烦地叙述Binder调用过程,但愿读者不要反感,其一加深印象,其二懂Binder真的很重要)咱们能够在这个Handler里面将替身恢复成真身。
这里不打算讲述Handler 的原理,咱们简单看一下Handler是如何处理接收到的Message的,若是咱们能拦截这个Message的接收过程,就有可能完成替身恢复工做;Handler类的dispathMesage
以下:
1 2 3 4 5 6 7 8 9 10 11 12 |
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } |
从这个方法能够看出来,Handler类消息分发的过程以下:
mCallback
存在,那么首先执行这个mCallback
回调;mCallback
的回调返回true
,那么表示消息已经成功处理;直接结束。mCallback
的回调返回false
,那么表示消息没有处理完毕,会继续使用Handler类的handleMessage
方法处理消息。那么,ActivityThread中的Handler类H
是如何实现的呢?H
的部分源码以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); ActivityClientRecord r = (ActivityClientRecord)msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; case RELAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart"); ActivityClientRecord r = (ActivityClientRecord)msg.obj; handleRelaunchActivity(r); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); // 如下略 } } |
能够看到H
类仅仅重载了handleMessage
方法;经过dispathMessage的消息分发过程得知,咱们能够拦截这一过程:把这个H
类的mCallback
替换为咱们的自定义实现,这样dispathMessage
就会首先使用这个自定义的mCallback
,而后看状况使用H
重载的handleMessage
。
这个Handler.Callback
是一个接口,咱们可使用动态代理或者普通代理完成Hook,这里咱们使用普通的静态代理方式;建立一个自定义的Callback类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
/* package */ class ActivityThreadHandlerCallback implements Handler.Callback { Handler mBase; public ActivityThreadHandlerCallback(Handler base) { mBase = base; } @Override public boolean handleMessage(Message msg) { switch (msg.what) { // ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100 // 原本使用反射的方式获取最好, 这里为了简便直接使用硬编码 case 100: handleLaunchActivity(msg); break; } mBase.handleMessage(msg); return true; } private void handleLaunchActivity(Message msg) { // 这里简单起见,直接取出TargetActivity; Object obj = msg.obj; // 根据源码: // 这个对象是 ActivityClientRecord 类型 // 咱们修改它的intent字段为咱们原来保存的便可. /* switch (msg.what) { / case LAUNCH_ACTIVITY: { / Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); / final ActivityClientRecord r = (ActivityClientRecord) msg.obj; / / r.packageInfo = getPackageInfoNoCheck( / r.activityInfo.applicationInfo, r.compatInfo); / handleLaunchActivity(r, null); */ try { // 把替身恢复成真身 Field intent = obj.getClass().getDeclaredField("intent"); intent.setAccessible(true); Intent raw = (Intent) intent.get(obj); Intent target = raw.getParcelableExtra(HookHelper.EXTRA_TARGET_INTENT); raw.setComponent(target.getComponent()); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } |
这个Callback类的使命很简单:把替身StubActivity恢复成真身TargetActivity;有了这个自定义的Callback以后咱们须要把ActivityThread里面处理消息的Handler类H
的的mCallback
修改成自定义callback类的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// 先获取到当前的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread"); currentActivityThreadField.setAccessible(true); Object currentActivityThread = currentActivityThreadField.get(null); // 因为ActivityThread一个进程只有一个,咱们获取这个对象的mH Field mHField = activityThreadClass.getDeclaredField("mH"); mHField.setAccessible(true); Handler mH = (Handler) mHField.get(currentActivityThread); // 设置它的回调, 根据源码: // 咱们本身给他设置一个回调,就会替代以前的回调; // public void dispatchMessage(Message msg) { // if (msg.callback != null) { // handleCallback(msg); // } else { // if (mCallback != null) { // if (mCallback.handleMessage(msg)) { // return; // } // } // handleMessage(msg); // } // } Field mCallBackField = Handler.class.getDeclaredField("mCallback"); mCallBackField.setAccessible(true); mCallBackField.set(mH, new ActivityThreadHandlerCallback(mH)); |
到这里,咱们已经成功地绕过AMS
,完成了『启动没有在AndroidManifest.xml中显式声明的Activity』的过程;瞒天过海,这种玩弄系统与股掌之中的快感大家能体会到吗?
虽然咱们完成了『启动没有在AndroidManifest.xml中显式声明的Activity 』,可是启动的TargetActivity是否有本身的生命周期呢,咱们还须要额外的处理过程吗?
实际上TargetActivity已是一个有血有肉的Activity了:它具备本身正常的生命周期;能够运行Demo代码验证一下。
这个过程是如何完成的呢?咱们以onDestroy
为例简要分析一下:
从Activity的
finish
方法开始跟踪,最终会经过ActivityManagerNative到AMS
而后接着经过ApplicationThread到ActivityThread,而后经过H
转发消息到ActivityThread的handleDestroyActivity,接着这个方法把任务交给performDestroyActivity完成。
在真正分析这个方法以前,须要说明一点的是:不知读者是否感觉获得,App进程与AMS
交互几乎都是这么一种模式,几个角色 ActivityManagerNative, ApplicationThread, ActivityThread以及Handler类H
分工明确,读者能够按照这几个角色的功能分析AMS
的任何调用过程,屡试不爽;这也是个人初衷——但愿分析插件框架的过程当中能帮助深刻理解Android Framework。
好了继续分析performDestroyActivity,关键代码以下:
1 2 3 4 5 |
ActivityClientRecord r = mActivities.get(token); // ...略 mInstrumentation.callActivityOnDestroy(r.activity); |
这里经过mActivities
拿到了一个ActivityClientRecord,而后直接把这个record里面的Activity交给Instrument类完成了onDestroy的调用。
在咱们这个demo的场景下,r.activity是TargetActivity仍是StubActivity?按理说,因为咱们欺骗了AMS
,AMS
应该只知道StubActivity
的存在,它压根儿就不知道TargetActivity是什么,为何它能正确完成对TargetActivity生命周期的回调呢?
一切的秘密在token
里面。AMS
与ActivityThread
之间对于Activity的生命周期的交互,并无直接使用Activity对象进行交互,而是使用一个token来标识,这个token是binder对象,所以能够方便地跨进程传递。Activity里面有一个成员变量mToken
表明的就是它,token能够惟一地标识一个Activity对象,它在Activity的attach
方法里面初始化;
在AMS
处理Activity的任务栈的时候,使用这个token标记Activity,所以在咱们的demo里面,AMS
进程里面的token对应的是StubActivity,也就是AMS
还在傻乎乎地操做StubActivity(关于这一点,你能够dump出任务栈的信息,能够观察到dump出的确实是StubActivity)。可是在咱们App进程里面,token对应的倒是TargetActivity!所以,在ActivityThread执行回调的时候,能正确地回调到TargetActivity相应的方法。
为何App进程里面,token对应的是TargetActivity呢?
回到代码,ActivityClientRecord是在mActivities
里面取出来的,确实是根据token取;那么这个token是何时添加进去的呢?咱们看performLaunchActivity就完成明白了:它经过classloader加载了TargetActivity,而后完成一切操做以后把这个activity添加进了mActivities
!另外,在这个方法里面咱们还能看到对Ativityattach
方法的调用,它传递给了新建立的Activity一个token对象,而这个token是在ActivityClientRecord构造函数里面初始化的。
至此咱们已经能够确认,经过这种方式启动的Activity有它本身完整而独立的生命周期!
本文讲述了『启动一个并无在AndroidManifest.xml中显示声明的Activity』的解决办法,咱们成功地绕过了Android的这个限制,这个是插件Activity管理技术的基础;可是要作到启动一个插件Activity问题远没有这么简单。
首先,在Android中,Activity有不一样的启动模式;咱们声明了一个替身StubActivity,确定没有知足全部的要求;所以,咱们须要在AndroidManifest.xml中声明一系列的有不一样launchMode的Activity,还须要完成替身与真正Activity launchMode的匹配过程;这样才能完成启动各类类型Activity的需求,关于这一点,在 DroidPlugin 的com.morgoo.droidplugin.stub包下面能够找到。
另外,每启动一个插件的Activity都须要一个StubActivity,可是AndroidManifest.xml中确定只能声明有限个,若是一直startActivity
而不finish的话,那么理论上就须要无限个StubActivity;这个问题该如何解决呢?事实上,这个问题在技术上没有好的解决办法。可是,若是你的App startActivity了十几回,而没有finish任何一个Activity,这样在Activity的回退栈里面有十几个Activity,用户难道按back十几回回到主页吗?有这种需求说明你的产品设计有问题;一个App一级页面,二级页面..到五六级的页面已经影响体验了,因此,每种LauchMode声明十个StubActivity绝对能知足需求了。
最后,在本文所述例子中,TargetActivity与StubActivity存在于同一个Apk,所以系统的ClassLoader可以成功加载并建立TargetActivity的实例。可是在实际的插件系统中,要启动的目标Activity确定存在于一个单独的文件中,系统默认的ClassLoader没法加载插件中的Activity类——系统压根儿就不知道要加载的插件在哪,谈何加载?所以还有一个很重要的问题须要处理: