Android插件化框架之动态加载activity(二)

上一篇文章Android插件化框架之动态加载Activity(一)中介绍了context的相关知识,本篇文章将介绍activity启动的一些知识并给出动态加载的原理。首先看一下activity的启动过程。
关于activity的启动过程,网上的文章相当多,而且介绍的非常详细,这里不再详细讲述源码流程,而是粗略的看一下我们怎么找取hook点。

Android插件化框架之动态加载activity(二)_第1张图片
Paste_Image.png

启动activity的过程比较复杂,主要涉及两个进程间的通信,一个是App所在进程,另一个就会系统的system_server进程。涉及到几个重要的类:ActivityThreadApplicationThreadActivityManagerServiceIntrumentation。
ActivityThread:和线程类Thread没一点关系,但是它代表着一个应用的主线程,一个应用的启动就是从ActivityThread的main函数开始的。
这里多说一点,main函数是什么时候调用的?由谁调用的呢?其实一个应用的最初启动都是由Launcher完成的,而Launcher本身也是一个应用,安装后的app都会在launcher中存在一个图标,负责启动其他app的launcher实际上也是继承自activity,所以启动应用的过程,就是由launcher打开待启动应用默认activity的过程。启动过程中会在ActivityStack的resumeTopActivityLocked方法中检查app进程是否创建,最终会在ActivityManagerService中调用startProcessLocked方法来
创建线程,并导入ActivityThread类执行main方法。而main方法中会调用attach方法将进程pid等信息与ActivityThread进行绑定。所以说ActivityThread的main方法才是应用启动的入口。每一个应用都会有一个ActivityThread的实例进行对应。ActivityManagerService中的startProcessLocked关键代码如下。

if (entryPoint == null) entryPoint = "android.app.ActivityThread";
 Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);

ApplicationThread: 是ActivityThread的内部类,作为app进程与system_server进程交互的纽带。它继承自ApplicationThreadNative类,而ApplicationThreadNative类继承自Binder并实现了IApplicationThread接口(此接口继承自IInterface接口,用于Binder通信的接口必须继承自IInterface)。ApplicationThread作为Binder服务端运行于Binder线程池,通过Hander(ActivityThread中名为H的handler)与主线程进行通信,而system_server中会持有AppcationThread在客户端的引用,即AppcationThreadProxy,其主要目的是为了system_server进程控制App进程执行一些操作,比如activity的启动,恢复,暂停等操作。

ActivityManagerService: 继承自ActivityManagerNative,ActivityManagerNative继承自Binder并实现了IActivityManager接口,所以AMS也是一个Binder,运行于system_server进程。app进程中持有的是AMS的Binder代理对象:ActivityManagerProxy(由ActivityManagerNative.getDefault获得)。
Instrumentation: 该类与ActivityThread类一样,每个应用也都会对应一个Intrumentation实例,Instrumentation主要用来对系统和应用之间的交互进行监控,Instrumentation可以用来获取动态加载activity的时机,下文会进行说明。说明之前,先简要的分析下activity启动的大致流程,如下图所示。

Android插件化框架之动态加载activity(二)_第2张图片
activity启动流程图.png

图中给出的只是其中主要的函数调用过程,并不全面,这里我们只关注Intrumentation的execStartActivity和newActivity方法。每个activity启动的过程都会经历这两个方法,那么怎么实现activity的动态加载呢,所谓动态加载就是加载没有在AndroidManifest.xml文件中注册的activity。activity的真正启动实际上是由AMS在system_server进程实现的,包括activity是否注册以及权限的检查都不是在app进程完成的,一旦发现待启动的activity没有注册,就会抛出找不到activity的异常。而我们就是要实现activity的“非注册启动”,目前比较流行的方法有两种: 欺骗系统静态代理。静态代理的方式思想很简单,就是利用一个代理activity作为空壳,将待启动的activity包装进去,对系统而言,启动的是代理的activity,而实际上代理activity持有了待启动的actiivty的引用,在代理activity的各个方法中对应调用真正的activity的对应方法即可,优点是简单,兼容性问题好,缺点是对插件的开发入侵大,我个人觉得一个好的插件框架,对插件开发的入侵程度越小越好,所以对这种方式没有做深入的研究。下面主要介绍欺骗系统的方式实现动态加载。
Android插件化框架之动态加载activity(二)_第3张图片
Paste_Image.png

这里只介绍原理,具体实现及遇到的问题将在下篇文章给出。将待启动的activity称为realActivity,插桩的activity称为pluginActivity,插桩的activity在manifest.xml中注册。当执行execStartActivity时,将Intent中的目标activity替换成PluginActivity,此时保存realActivity的包名和类名,当AMS中启动activity过程进行合法性检查时,会发现是PluginActivity,因为已经注册过,毫无疑问会绕过系统检查,此时在真正加载activity类,是通过调用newActivity来完成的,我们再newActivity中将保存的包名和类名替换回来,ok,成功完成了对系统的欺骗,系统认为我们启动的是pluginActivity,实际上我们启动的是realActivity。

看似简单,但是具体怎么替换?替换谁?肯定不是单单替换下包名和类名就完了这么简单,粗略的想一下会涉及到classLoader,resource等等的处理,时间和篇幅关系,可能逻辑有点乱,多多包涵,具体实现请看下一篇文章。

参考文章链接:
1.Android应用程序启动过程源代码分析
2.Android 插件化原理解析——Activity生命周期管理
3.Activity启动过程源码分析
4.8个类搞定插件化——Activity实现方案

目前本人在公司负责热修复相关的工作,主要是基于robust的热修复相关工作。感兴趣的同学欢迎进群交流。


Android插件化框架之动态加载activity(二)_第4张图片
image.png

你可能感兴趣的:(Android插件化框架之动态加载activity(二))