Android应用启动过程-阿里云开发者社区

开发者社区> 开发与运维> 正文

Android应用启动过程

简介: 前言: 最近发现自己好像做了android这么久,竟然还不知道一个应用是如何去启动的,所以决定去一探究竟,结果发现这个过程好像有点难,好像有点繁杂,毕竟我以前从未接触过framework层的内容。

前言:

最近发现自己好像做了android这么久,竟然还不知道一个应用是如何去启动的,所以决定去一探究竟,结果发现这个过程好像有点难,好像有点繁杂,毕竟我以前从未接触过framework层的内容。其实我也是一面探究一面来写这篇文章的。没办法,毕竟我又不会,而且在开发应用层的时候也没怎么接触过。但是这东西内容太多了,如果我不写点东西的话长时间不接触肯定会忘记,所以决定一面探究这个过程一面写下这篇文章。最后肯定会有写得不好或者我理解错的地方,还希望有大神能指点一二。
注意,这里只讲启动是怎么样的一个流程,而不讲具体怎么去实现


先看看一些基础的概念:

Linux进程通信做了什么事

(1)数据传输
(2)资源和数据共享
(3)通知
(4)进程控制

Linux进程通信的方式

了解一下就行,至于详哪种方式用于哪种场景,我也不是很清楚。
(1)管道
(2)信号量
(3)消息队列
(4)信号
(5)共享内存
(6)套接字

IPC机制

什么是IPC,好像接触安卓的时候经常能听到IPC但是又不知道是什么,IPC的全称是Inter-Process Communication,就是指进程间的通信,那么IPC机制可以简单的理解为就是进程间通信的机制。

一.应用启动过程涉及到的内容

首先肯定要知道这个过程涉及到哪些东西,才好梳理出整个流程。我也是加班加点的看了很多文章和博客,下面说说我的看法。
我们都知道在android中每个应用都可以当做一个进程,那么应用的启动过程无疑会涉及到进程通信
据我了解,这个过程大致涉及到3个进程:
(1)Launcher 也就是桌面,可以把我们的手机桌面当成一个进程
(2)systemserver就是所有的服务,可以当成是手机开机之后系统启动的一个进程
(3)zygote进程,可以当成是一个创建进程的进程,好像也是开机后启动的

那么整个过程就是这3个进程间用IPC机制进行通信的过程,所以说设计到的内容大概会有:
(1)上面提的3个进程
(2)Android进程通信会用到的Binder机制
(3)Android进程通信会用到的AIDL
我大概就是这样理解的,详细的下面会说,不过会按我的思路去说。

二.点击Launcher 中的图标后发生的事

先看看点击桌面的图标后会发生什么事情,我在网上找到文章这样写(当然他是贴源码的,源码我这就先不贴)


img_455337552243edbed254a48b2702f02d.png

那么我是不是可以把这个过程看成这样


img_ddf1ea399174fe58be1d1c3c8af73870.png

那么是不是可以看出点击桌面图标之后其实最后是调用了我们熟悉的startActivity方法。
到这里,我打算先不研究Android应用启动过程,不如我先研究startActivity,也就是一个页面跳转到另一个页面的过程。

三.startActivity的过程

找文章,看源码,发现startActivity的过程是这样的。(别人贴的代码,我就先直接拿来用了)

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {  
        if (mParent == null) {//只要关心mParent==null的情况就可以了  
            Instrumentation.ActivityResult ar =  
                mInstrumentation.execStartActivity(  
                    this, mMainThread.getApplicationThread(), mToken, this,  
                    intent, requestCode, options);  
            .........  
        } else {  
            ......  
        }  
    }  

不需要看全部代码,正如我所说,我们的目的是先探索过程,而不是探索具体的实现。
在这只要知道startActivity内部调用一个Instrumentation类的execStartActivity方法就行。
startActivity内部调用一个Instrumentation类的execStartActivity方法
startActivity内部调用一个Instrumentation类的execStartActivity方法
startActivity内部调用一个Instrumentation类的execStartActivity方法
重要的事说三遍。然后这里又新涉及到了一个Instrumentation

它是做啥子的最好肯定是看官方的注释:
instrumentation can load both a test package and the application under test into the same process. Since the application components and their tests are in the same process, the tests can invoke methods in the components, and modify and examine fields in the components.

其实我还是不太看得懂它是做什么的,暂时就先不管它,当成是一个中介就行。直接点进看execStartActivity这个方法(还是别人贴的代码)

public ActivityResult execStartActivity(  
       Context who, IBinder contextThread, IBinder token, Activity target,  
       Intent intent, int requestCode) {  
                               ......  
       try {  
       //ActivityManagerNative.getDefault()实际返回的是一个ActivityManagerProxy对象,也就是AMS的代理  
           int result = ActivityManagerNative.getDefault()  
               .startActivity(whoThread, intent,  
                       intent.resolveTypeIfNeeded(who.getContentResolver()),  
                       null, 0, token, target != null ? target.mEmbeddedID : null,  
                       requestCode, false, false);  
           checkStartActivityResult(result, intent);  
       } catch (RemoteException e) {  
       }  
       return null;  
   }  

发现调用的是ActivityManagerNative.getDefault().startActivity()这个方法,那就涉及到了ActivityManagerNative,其实也就是涉及到了AMS这个服务。
AMS(ActivityManagerService)就是systemserver中的一个服务
AMS(ActivityManagerService)就是systemserver中的一个服务
AMS(ActivityManagerService)就是systemserver中的一个服务

看到这里,会发现多出个AMS,其实这个服务很重要,你可以暂时看成是操作管理Activity的(下面再介绍它),既然是systemserver的,那么是不是可以说就用到了进程通信。
其实到这里我就有点不了解,你们想想,同一个进程里面的两个页面的跳转为什么要用到另一个进程,直接在这个进程里面做操作不行吗?
我的理解是这样的,其实管理页面的是AMS,如果要在一个进程内做跳转的操作,是不是每个进程都要有AMS,那缺点就很明显了,所以谷歌要把AMS提出来放到一个进程里面供所有的进程去使用。

四.AMS

如果要直接接着上边的代码去看页面之间的跳转的话不太好看懂,因为涉及到了ActivityManagerService这个服务和进程间的通信,所以至少我觉得我们要先把这两个内容大概了解一下才能看懂下面的操作,先简单说说AMS再谈通信。

1.AMS是什么

ActivityManagerService,人如其名,管理Activity的服务,但其实不单只有Activity,应该是四大组件。

2.AMS做了什么

img_67bb43819d5ce855042dba9b0344bac6.png

这个出自https://www.jianshu.com/p/47eca41428d6,功能肯定很多,反正它最主要的肯定是实现了“Manager”的功能。

3.AMS怎么实现的

ActivityManagerNative 继承了Binder 类,这就是接下来我想介绍的Binder机制。

五.Binder 机制

大概了解下AMS之后再来看看Binder 是如何实现进程间的通信,Binder 是Android的一种IPC,当然Android是基于Linux的,所以它本身也能使用Linux的进程IPC。
那个这个Binder我就讲讲我的一些简单的理解,毕竟没用过,也不是很懂。

(1)首先Binder的设计是基于C/S模型的,很容易想到我们的普通请求网络的情况也是基于C/S,而且好像网络请求也是一个进程间通信的过程。所以你也可以把Binder通信想成一个请求网络的过程。
(2)涉及到三个比较主要的部分,C/S中的Client和Server,还有一个ServiceManager。

抛开所有细节,总体的通信流程大概就是这样的。


img_c3bc04b0a3f0e83c00c3aea77d22ab7b.png

其实在这个过程中我们在稍微扩展一点点的细节,就是Server会注册到ServiceManager中,然后Client调用是去查询ServiceManager中的Server。比如Client进程想要调用Server进程的object对象的一个方法add。(我这里是引用了别人写的文章http://weishu.me/2016/01/12/binder-index-for-newer/

img_6cf77ae12f192f18640db9fbc0f04b42.png

这里我要说明一点,我这里是因为以前看过了代理模式所以比较好理解,如果不知道代理模式的话可能不太能明白这个返回的proxy是干啥子用的。

简单来说代理模式模式就是一个原本类的代理类。我想实现add功能,我让代理类来做,代理类内部会自己用某种方法调用原本类的add功能,如果还是看不懂的话建议可以先去了解一下这个设计模式。

有点说偏了,再看看图 ,其实这个查询的过程目的为了拿到某个东西之后能调用Server中的方法,因为一般我们没办法跨进程调直接调用其它进程类的方法,所以这里借助了Binder驱动和代理类来实现
好好想想这个过程,你要调用某个方法,肯定要拿到这个类的对象,然后对象再调用方法吧,这个图就是拿对象的一个过程。

我还没说完,而这个拿对象的过程嘛,其实进程间就算你要直接拿代理也不可能实现,而这个实现的过程还是通过Binder驱动,而底层用的肯定不是java去写,所以暂时不用关心,但是既然用了底层Binder驱动对吧,那就肯定在上层会有个规范,所以Server就是所谓的Binder类,而Client端获取的就是BinderProxy
那这里是不是可以简单的解释ActivityManagerNative 继承Binder 就是为了要用Binder驱动来实现通信。

OK,就这样简单讲讲就行了,再深入我也不是很懂,而且我目前只是为了看流程而不是为了看实现。现在你只是心里大概了解了Binder机制进行进程间通信的一个过程。那就跳回execStartActivity方法,回头看看页面跳转到页面间的操作。

六.页面间跳转与Binder机制的联系

为了方便,我再贴一次上边execStartActivity方法的代码

public ActivityResult execStartActivity(  
       Context who, IBinder contextThread, IBinder token, Activity target,  
       Intent intent, int requestCode) {  
                               ......  
       try {  
       //ActivityManagerNative.getDefault()实际返回的是一个ActivityManagerProxy对象,也就是AMS的代理  
           int result = ActivityManagerNative.getDefault()  
               .startActivity(whoThread, intent,  
                       intent.resolveTypeIfNeeded(who.getContentResolver()),  
                       null, 0, token, target != null ? target.mEmbeddedID : null,  
                       requestCode, false, false);  
           checkStartActivityResult(result, intent);  
       } catch (RemoteException e) {  
       }  
       return null;  
   }  

我们所在的这个应用里面,是没法直接拿到AMS对象的,所以这里通过ActivityManagerNative.getDefault() 能获取到AMS的代理对象,这是ActivityManagerProxy类的对象。获取的过程就是内部通过Binder进制,先不研究代码,反正你知道这句话能拿到AMS的代理,而且通过上面我说的Binder机制你也知道拿到代理是为了做什么,那就行了。

获取代理之后发现它调用代理的这个方法


img_3a86a850dd35927eb2aaeafd16f36c94.png

这个过程中,我们可以简单看看这个代理类中的startActivity方法,就看看,不深入(还是别人贴的代码)

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        if (profilerInfo != null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        if (options != null) {
            data.writeInt(1);
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

看到这里可以了解里面主要操作了两个Parcel 类的对象data 和reply,简单查查Parcel 是啥玩意。简单的理解这个就是用于Binder通信的一个对象,反正先不管它底层到底做了什么,既然是代理模式,就说明这里调用了startActivity方法,他的内部肯定会调用原本类中的startActivity方法,而这个原本类就是ActivityManagerService(AMS),所以我们可以直接去看看AMS的startActivity方法。

里面调用了很多的方法,我就不全贴了,而且代码我也没认真去了解,例如可以找到里面有调用一个startActivityUncheckedLocked方法(还是别人的代码)


img_1c07a4fe6e6d24ea6851a0dd09f19fc1.png

可以看到这里用到了ActivityStack,所以想想就中的里面肯定是对Activity的栈做了一大堆操作,然后再去调用activity的生命周期。

其实这里的AMS是用了双向的,因为C/S结构是单向的,比如网络请求,你只能客户端发起请求,这里是服务端也可以发起请求,具体怎么做先不说了,我是有很多细节没说,但从我上面说的流程能知道页面间的跳转总体是如何使用实现的,接下来就会到原本的问题,应用启动流程

七.启动新进程

最开始说了点击Launcher 的图标其实就是调用了startActivity,再加上上面的解释,我们这里就可以说点击Launcher 的图标后,现在已经跳到了AMS里面,所以我们只需要接着在AMS里面找到是如何启动新应用的就行。

也就说说我们在上面其实已经知道了怎么从Launcher 进程调到AMS所在的进程,现在我们要探究它内部是怎么从AMS所在的进程跳到zygote进程,因为最开始我说了zygote就是用了创建进程的,所以AMS和zygote这两个进程的通信的流程是怎么样的

毕竟我是看了很多文章,对照了别人的文章和我自己的理解,我找到了一张很容易理解这个过程的图(出自https://blog.csdn.net/ccjhdopc/article/details/52893738

img_be37cc58a04ee8739250a0c7b3b072f0.png

这图我只截了一部分,因为我们直需要暂时看AMS怎么走流程走到zygote的(这里的SystemServer就是AMS所在的进程,最上边我有讲)。

可以看出我们上面讲页面与页面间的跳转时正好对用图中的


img_04dd0efb0e941fce846a741ed3059715.png

在AMS内部执行startactivity之后会再执行一个schedulePauseActivity方法,也是用了Binder,不过这回AMS属于客户端所以它拿到服务端的ApplicationThread的代理类ApplicationThreadProxy来做操作,那自然会调用原本应用进程中的ApplicationThread类的schedulePauseActivity方法
这个方法可以从图中看出其实就是做了两步操作
(1)让当前的activity执行onPause生命周期
(2)再用Binder调用activityPaused方法通知AMS当前的activity已经onPause了,你可以启动新的activity了

我看了一些代码,没有找到在activityPaused方法之后是在哪里判断进程是否已经创建,如果有大神知道,麻烦请告诉我一下
反正就是如果进程没被创建会调用startProcessLocked方法去调用Zygote创建新进程,不过好像这两个进程间的通信不是用Binder而是用socket

至于Zygote是如何去创建新进程的,这里就先不管,反正它能通过某种方式去创建进的进程,然后新的进程中又通过Binder和AMS进行通信,大概是告诉AMS它已经创建完成,这时AMS就再去调用新的入口activity的生命周期,我的理解大概是这样的。

八.总结

先对流程做下总结,可以看出所有的逻辑操作都是由AMS来做的,而创建进程的操作是有zygote来做的,而应用进程与AMS的通信都是使用Binder机制来进行。这篇文章主要是简单的探究应用的启动的过程,所以没有放太多的代码,也没有深入去讲,因为我不是很能看懂AMS里面的代码,AIDL也没有讲。
AMS和zygote还有AIDL我想之后单独分出来写文章,特别是AMS,看了一下它的代码,感觉真的不是三言两语能够讲清楚的,我是真的感觉有点难。写这篇文章的目的最主要的还是为了做个比较,感觉framework的东西不再像写自定义view这些这么好理解了,所以还是多做点笔记比较好,如果有理解错的地方和写得不到位的地方,还望大神指点。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章