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

目录
相关文章
|
2月前
|
IDE Java 开发工具
深入探索安卓应用开发:从环境搭建到第一个"Hello, World!"应用
本文将引导读者完成安卓应用开发的初步入门,包括安装必要的开发工具、配置开发环境、创建第一个简单的安卓项目,以及解释其背后的一些基本概念。通过一步步的指导和解释,本文旨在为安卓开发新手提供一个清晰、易懂的起点,帮助读者顺利地迈出安卓开发的第一步。
223 65
|
2月前
|
存储 Java Android开发
探索安卓应用开发:构建你的第一个"Hello World"应用
【9月更文挑战第24天】在本文中,我们将踏上一段激动人心的旅程,深入安卓应用开发的奥秘。通过一个简单而经典的“Hello World”项目,我们将解锁安卓应用开发的基础概念和步骤。无论你是编程新手还是希望扩展技能的老手,这篇文章都将为你提供一次实操体验。从搭建开发环境到运行你的应用,每一步都清晰易懂,确保你能顺利地迈出安卓开发的第一步。让我们开始吧,探索如何将一行简单的代码转变为一个功能齐全的安卓应用!
|
13天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
14天前
|
存储 搜索推荐 Java
打造个性化安卓应用:从设计到实现
【10月更文挑战第30天】在数字化时代,拥有一个个性化的安卓应用不仅能够提升用户体验,还能加强品牌识别度。本文将引导您了解如何从零开始设计和实现一个安卓应用,涵盖用户界面设计、功能开发和性能优化等关键环节。我们将以一个简单的记事本应用为例,展示如何通过Android Studio工具和Java语言实现基本功能,同时确保应用流畅运行。无论您是初学者还是希望提升现有技能的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧。
|
17天前
|
搜索推荐 开发工具 Android开发
打造个性化Android应用:从设计到实现的旅程
【10月更文挑战第26天】在这个数字时代,拥有一个能够脱颖而出的移动应用是成功的关键。本文将引导您了解如何从概念化阶段出发,通过设计、开发直至发布,一步步构建一个既美观又实用的Android应用。我们将探讨用户体验(UX)设计的重要性,介绍Android开发的核心组件,并通过实际案例展示如何克服开发中的挑战。无论您是初学者还是有经验的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧,帮助您在竞争激烈的应用市场中脱颖而出。
|
19天前
|
算法 Java 数据库
Android 应用的主线程在什么情况下会被阻塞?
【10月更文挑战第20天】为了避免主线程阻塞,我们需要合理地设计和优化应用的代码。将耗时操作移到后台线程执行,使用异步任务、线程池等技术来提高应用的并发处理能力。同时,要注意避免出现死循环、不合理的锁使用等问题。通过这些措施,可以确保主线程能够高效地运行,提供流畅的用户体验。
31 2
|
23天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
48 5
|
23天前
|
移动开发 Dart 搜索推荐
打造个性化安卓应用:从零开始的Flutter之旅
【10月更文挑战第20天】本文将引导你开启Flutter开发之旅,通过简单易懂的语言和步骤,让你了解如何从零开始构建一个安卓应用。我们将一起探索Flutter的魅力,实现快速开发,并见证代码示例如何生动地转化为用户界面。无论你是编程新手还是希望扩展技能的开发者,这篇文章都将为你提供价值。
|
1月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
46 4
|
1月前
|
编解码 Android开发 UED
构建高效Android应用:从内存优化到用户体验
【10月更文挑战第11天】本文探讨了如何通过内存优化和用户体验改进来构建高效的Android应用。介绍了使用弱引用来减少内存占用、懒加载资源以降低启动时内存消耗、利用Kotlin协程进行异步处理以保持UI流畅,以及采用响应式设计适配不同屏幕尺寸等具体技术手段。
49 2