Application, Activity, ContentProvider启动顺序

先交代一下本次问题的背景,由于最近在调研dex加壳,按照Jack_jia的加壳技术方案中的步骤做了demo,然后在android5.0以上系统上运行的时候发现,在解壳的过程中设置ContentProvider的地方有点问题,然后我就把那段代码注释掉了,结果发现程序竟然能够正常运行,感觉很奇怪,由于本人资历尚浅,对Android和加壳的机制都是一知半解,特别是对于ContentProvider更是完全不了解,所以决定把这部分搞清楚。

就先在Application,Activity和ContentProvider三个类的onCreate函数中打印了log,发现它们的启动顺序为:

Application->attachBaseContext =====>ContentProvider->onCreate =====>Application->onCreate =====>Activity->onCreate


如果是这个顺序就更奇怪了,因为在被加壳dex中定义的Provider这时候竟然已经被启动了,在脱壳程序中ProxyApplication->attachBaseContext只是创建了被加壳dex的DexClassLoader,并LoadedApk中的classloader替换掉,并没有做其它的操作,大部分的运行环境的替换是在Application->onCreate中,然后这个时候它并没有运行呢。为了搞清楚这个问题,我打印了Application,Activity和ContentProvider三个类onCreate函数的调用栈,再结合Android系统的源码,搞明白这三个东西的启动顺序及大致的过程。

当然最好有Zygote创建进程,Application启动以及Java类加载器等的相关基础,没有的话最好先学习一下,网上很多相关资料。下面是正文:


下面根据各个函数的调用栈分析这种运行顺序的原因,测试手机是Nexus4,系统5.1.1。


调用Application->attachBaseContext的调用栈如下:

可以看到Zygote在创建了进程以后,运行到了ActivityThread->main函数,然后运行到了Looper->loop函数,然后Looper给ActivityThread发送了BIND_APPLICATION消息,然后运行到ActivityThread->handleBindApplication函数,这个函数应该是用于处理Application的初始化工作,每个应用只能有一个Application。然后由LoadedApk->makeApplication来具体执行Application的初始化工作,最后调用到Applicatioin->attachBaseContext函数,这个函数应该是用来设置整个应用程序的Context对象的,也就是设置Application对象,因为Application是继承自Context类的,Application实际上就代表了整个应用的上下文。



下面再来看ContentProvider->onCreate的调用栈:


可以发现也是从handleBindApplication函数运行到Provider->onCreate的,说明是先初始化Application对象以后,才去处理Provider的,这也是正常的,因为Application是整个应用最外层的东西,四大组件什么的都是包含在Application里的,所以肯定要先初始化Application。看一下handleBindApplication函数部分代码:


其中data是ActivityThread的成员变量mBoundApplication,它的值是由Looper发消息时传过来的,里面包含了整个应用程序的所有信息,具体可以去看源码。然后就一步步调用到Provider->onCreate函数了。


再往后面看可以看到mInstrumentation.callApplicationOnCreate函数,里面调用了Application->onCreate函数。

注释中写这样的调用顺序是因为Instrumentation会在这时启动测试线程,不希望与创建Providers冲突,不太明白什么意思?


下面看Activity->onCreate调用栈:


由Looper向ActivityThread发送了LAUNCH_ACTIVITY消息,然后调用到了handleLaunchActivity函数,这个函数和上面一样是处理Activity的创建和初始化工作的。(经过实验,所有的Activity创建都是这个流程,并不只是主Activity)

从上面的分析可以看出,Provider的初始化实际上是在Application的初始化过程中发生的,而Activity是在这之后。

那么回到最初的问题,为什么在解壳时设置ContentProvider的代码被注释掉以后,这些Provider还依然可以使用呢?看一下ActivityThread->installProvider函数中的一段代码:


可以看到会从Context中取得ClassLoader,然后这个ClassLoader会根据info(也就是ProviderInfo记录Provider信息的对象)中的Provider的名字去加载对应的Provider类并实例化,然后会调用该对象的attachInfo方法,从上面的调用栈可以看出这个方法最终会调用到该Provider的onCreate函数,即完成它的初始化工作。

看到这里恍然大悟,原来在解壳程序中ProxyApplication->attachBaseContext函数里,创建被加壳dex的DexClassLoader并将其替换掉系统中现有的classloader才是最关键的步骤,因为这时候的classloader已经是被加壳的dex了,当然能够loadClass成功将Provider类加载进来并实例化,然后执行后面的初始化等操作。


另外,ActivityThread在handleBindApplication中使用的由Looper发过来的AppBindData类型的数据,包含了整个应用程序的几乎所有信息,我觉得它应该是通过解析应用的AndroidManifest.xml文件所得来的,这个回来有时间需要验证一下。


到这里其实还并没有解决为什么把脱壳程序中设置ContentProvider那段代码注释掉仍然可以正常运行的疑问,那段代码是为了设置Provider中Context对象用的,Context对象也就是Application对象,在程序运行过程中是非常重要的,脱壳过程中因为刚开始运行的Context是壳程序的Context,而脱壳以后这个Context就改为了被加壳程序的Context,但是这些Provider的Context并没有被重置,为什么还能正常运行呢?这个明天继续把它搞清楚吧,下班回家~


还有就是由于本人技术有限,上面的分析可能有不正确的地方,欢迎指出错误,欢迎大家一起讨论!


另外有一篇360的相关技术分析,链接:http://blogs.360.cn/blog/proxydelegate-application/

阅读更多

没有更多推荐了,返回首页