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这些这么好理解了,所以还是多做点笔记比较好,如果有理解错的地方和写得不到位的地方,还望大神指点。

目录
相关文章
|
21天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
22天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【4月更文挑战第2天】随着移动应用开发的不断进步,开发者们寻求更流畅、高效的用户体验。在Android平台上,Kotlin语言凭借其简洁性和功能性赢得了开发社区的广泛支持。特别是Kotlin协程,作为一种轻量级的并发处理方案,使得异步编程变得更加简单和直观。本文将深入探讨Kotlin协程的核心概念、使用场景以及如何将其应用于Android开发中,以提高应用性能和响应能力。通过实际案例分析,我们将展示协程如何简化复杂任务,优化资源管理,并为最终用户提供更加流畅的体验。
|
22天前
|
开发框架 安全 Android开发
探索安卓系统的新趋势:智能家居应用的蓬勃发展
随着智能家居概念的兴起,安卓系统在智能家居应用领域的应用日益广泛。本文将探讨安卓系统在智能家居应用开发方面的最新趋势和创新,以及其对用户生活的影响。
14 2
|
25天前
|
缓存 监控 Java
构建高效Android应用:从优化用户体验到提升性能
在竞争激烈的移动应用市场中,为用户提供流畅和高效的体验是至关重要的。本文深入探讨了如何通过多种技术手段来优化Android应用的性能,包括UI响应性、内存管理和多线程处理。同时,我们还将讨论如何利用最新的Android框架和工具来诊断和解决性能瓶颈。通过实例分析和最佳实践,读者将能够理解并实施必要的优化策略,以确保他们的应用在保持响应迅速的同时,还能够有效地利用系统资源。
|
26天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
12 0
|
30天前
|
编解码 算法 Java
构建高效的Android应用:内存优化策略详解
随着智能手机在日常生活和工作中的普及,用户对移动应用的性能要求越来越高。特别是对于Android开发者来说,理解并实践内存优化是提升应用程序性能的关键步骤。本文将深入探讨针对Android平台的内存管理机制,并提供一系列实用的内存优化技巧,以帮助开发者减少内存消耗,避免常见的内存泄漏问题,并确保应用的流畅运行。
|
23天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践与优化
在响应式编程范式日益盛行的今天,Kotlin协程作为一种轻量级的线程管理解决方案,为Android开发带来了性能和效率的双重提升。本文旨在探讨Kotlin协程的核心概念、实践方法及其在Android应用中的优化策略,帮助开发者构建更加流畅和高效的应用程序。通过深入分析协程的原理与应用场景,结合实际案例,本文将指导读者如何优雅地解决异步任务处理,避免阻塞UI线程,从而优化用户体验。
|
28天前
|
Java 编译器 Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
在开发高性能的Android应用时,选择合适的编程语言至关重要。近年来,Kotlin因其简洁性和功能性受到开发者的青睐,但其性能是否与传统的Java相比有所不足?本文通过对比分析Kotlin与Java在Android平台上的运行效率,揭示二者在编译速度、运行时性能及资源消耗方面的具体差异,并探讨在实际项目中如何做出最佳选择。
17 4
|
1天前
|
存储 缓存 安全
Android系统 应用存储路径与权限
Android系统 应用存储路径与权限
5 0
Android系统 应用存储路径与权限
|
1天前
|
存储 安全 Android开发
Android系统 自定义系统和应用权限
Android系统 自定义系统和应用权限
10 0