APP启动流程解析

前言

当我们点击手机屏幕上的软件图标时,就可以打开这个软件,看似很简单的过程其实包含了许多的底层交互,看了还不明白,欢迎来打我。

一 . 启动流程简介

首先要知道的是,手机屏幕其实就是一个Activity,我们专业点将其称为Launcher,相信做过车载设备开发的朋友肯定不会陌生,Launcher是手机厂商提供的,不同的手机厂商比拼的就是Launcher的设计。当然我们自己也可以去编写Launcher,运行在手机上使用自己的桌面风格,当然这里我们不去讲如何去编写一个Launcher,如果你感兴趣欢迎关注我。

写过AndroidDemo的朋友肯定都知道,我们必须在AndroidManifest配置文件中配置默认启动的Activity

<intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

其实就是告诉Launcher该启动这个App的哪个页面。这样当系统启动的时候,PackageManger-Service就可以从配置文件中读取到该启动哪个Activity。

其次要知道的是,Launch和其他APP,运行在不同的进程中,所以他们之间的通信是通过Binder去完成的,所以AMS是必不可少的。下面我们以启动微信为例,看看启动流程是怎样的。

                           

                                             

                                                             

                                       

                                                                          

                

                                                                

          

                                                             

         

简单概括启动微信的流程就是:

1.Launcher通知AMS 要启动微信了,并且告诉AMS要启动的是哪个页面也就是首页是哪个页面

2.AMS收到消息告诉Launcher知道了,并且把要启动的页面记下来

3.Launcher进入Paused状态,告诉AMS,你去找微信吧

上述就是Launcher和AMS的交互过程

4.AMS检查微信是否已经启动了也就是是否在后台运行,如果是在后台运行就直接启动,如果不是,AMS会在新的进程中创建一个ActivityThread对象,并启动其中的main函数。

5.微信启动后告诉AMS,启动好了

6.AMS通过之前的记录找出微信的首页,告诉微信应该启动哪个页面

7.微信按照AMS通知的页面去启动就启动成功了。

      

上述阶段是微信和AMS的交互过程。那么接下来我们分析下具体过程

二  启动流程分析

1.点击Launcher上的微信图标时,会调用startActivitySafely方法,intent中携带微信的关键信息也就是我们在配置文件中配置的默认启动页信息,其实在微信安装的时候,Launcher已经将启动信息记录下来了,图标只是一个快捷方式的入口。

startActivitySafely的方法最终还是会调用Activity的startActivity方法

@Override
public void startActivity(Intent intent, @Nullable Bundle options) {if (options != null) {startActivityForResult(intent, -1, options);} else {// Note we want to go through this call for compatibility with// applications that may have overridden the method.startActivityForResult(intent, -1);}
}

而startActivity方法最终又会回到startActivityForResult方法,这里startActivityForResult的方法中code为-1,表示startActivity并不关心是否启动成功。startActivityForResult部分方法如下所示:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) {options = transferSpringboardActivityOptions(options);Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);if (ar != null) {mMainThread.sendActivityResult(mToken, mEmbeddedID, requestCode, ar.getResultCode(),ar.getResultData());}if (requestCode >= 0) {

startActivityForResult方法中又会调用mInstrumentation.execStartActivity方法,我们看到其中有个参数是

mMainThread.getApplicationThread()

关于ActivityThread曾在 深入理解Android消息机制黄林晴的博客_CSDN博客-写作技巧领域博主文章中提到过,ActivityThread是在启动APP的时候创建的,ActivityThread代表应用程序,而我们开发中常用的Application其实是ActivityThread的上下文,在开发中我们经常使用,但在Android系统中其实地位很低的。

                                                         

Android的main函数就在ActivityThread中

public static void main(String[] args) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");SamplingProfilerIntegration.start();// CloseGuard defaults to true and can be quite spammy.  We// disable it here, but selectively enable it later (via// StrictMode) on debug builds, but using DropBox, not logs.CloseGuard.setEnabled(false);Environment.initForCurrentUser();

再回到上面方法

mMainThread.getApplicationThread()

得到的是一个Binder对象,代表Launcher所在的App的进程,mToken实际也是一个Binder对象,代表Launcher所在的Activity通过Instrumentation传给AMS,这样AMS就知道是谁发起的请求。

2.mInstrumentation.execStartActivity

instrumentation在测试的时候经常用到,instrumentation的官方文档:http://developer.android.com/intl/zh-cn/reference/android/app/Instrumentation.html这里不对instrumentation进行详细介绍了,我们主要接着看mInstrumentation.execStartActivity方法

public Instrumentation.ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {IApplicationThread whoThread = (IApplicationThread)contextThread;if(this.mActivityMonitors != null) {Object e = this.mSync;synchronized(this.mSync) {int N = this.mActivityMonitors.size();for(int i = 0; i < N; ++i) {Instrumentation.ActivityMonitor am = (Instrumentation.ActivityMonitor)this.mActivityMonitors.get(i);if(am.match(who, (Activity)null, intent)) {++am.mHits;if(am.isBlocking()) {return requestCode >= 0?am.getResult():null;}break;}}}}try {intent.setAllowFds(false);intent.migrateExtraStreamToClipData();int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);checkStartActivityResult(var16, intent);} catch (RemoteException var14) {;}return null;
}

其实这个类是一个Binder通信类,相当于IPowerManager.java就是实现了IPowerManager.aidl,我们再来看看getDefault这个函数

public static IActivityManager getDefault() {return (IActivityManager)gDefault.get();
}

getDefault方法得到一个IActivityManager,它是一个实现了IInterface的接口,里面定义了四大组件的生命周期,

public static IActivityManager asInterface(IBinder obj) {if(obj == null) {return null;} else {IActivityManager in = (IActivityManager)obj.queryLocalInterface("android.app.IActivityManager");return (IActivityManager)(in != null?in:new ActivityManagerProxy(obj));}
}

最终返回一个ActivityManagerProxy对象也就是AMP,AMP就是AMS的代理对象,说到代理其实就是代理模式,关于什么是代理模式以及动态代理和静态代理的使用可以持续关注我,后面会单独写篇文章进行介绍。

            

AMP的startActivity方法

public int startActivity(IApplicationThread caller, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile, ParcelFileDescriptor profileFd, Bundle options) throws RemoteException {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeInterfaceToken("android.app.IActivityManager");data.writeStrongBinder(caller != null?caller.asBinder():null);intent.writeToParcel(data, 0);data.writeString(resolvedType);data.writeStrongBinder(resultTo);data.writeString(resultWho);data.writeInt(requestCode);data.writeInt(startFlags);data.writeString(profileFile);if(profileFd != null) {data.writeInt(1);profileFd.writeToParcel(data, 1);} else {data.writeInt(0);}

主要就是将数据写入AMS进程,等待AMS的返回结果,这个过程是比较枯燥的,因为我们做插件化的时候只能对客户端Hook,而不能对服务端操作,所以我们只能静静的看着。(温馨提示:如果文章到这儿你已经有点头晕了,那就对了,研究源码主要就是梳理整个流程,千万不要纠结源码细节,那样会无法自拔)。

3.AMS处理Launcher的信息

AMS告诉Launcher我知道了,那么AMS如何告诉Launcher呢?

Binder的通信是平等的,谁发消息谁就是客户端,接收的一方就是服务端,前面已经将Launcher所在的进程传过来了,AMS将其保存为一个ActivityRecord对象,这个对象中有一个ApplicationThreadProxy即Binder的代理对象,AMS通ApplicationTreadProxy发送消息,App通过ApplicationThread来接收这个消息。

Launcher收到消息后,再告诉AMS,好的我知道了,那我走了,ApplicationThread调用ActivityThread的sendMessage方法给Launcher主线程发送一个消息。这个时候AMS去启动一个新的进程,并且创建ActivityThread,指定main函数入口。

启动新进程的时候为进程创建了ActivityThread对象,这个就是UI线程,进入main函数后,创建一个Looper,也就是mainLooper,并且创建Application,所以说Application只是对开发人员来说重要而已。创建好后告诉AMS微信启动好了,AMS就记录了这个APP的登记信息,以后AMS通过这个ActivityThread向APP发送消息。

这个时候AMS根据之前的记录告诉微信应该启动哪个Activity,微信就可以启动了。

public void handleMessage(Message msg) {ActivityThread.ActivityClientRecord data;switch(msg.what) {case 100:Trace.traceBegin(64L, "activityStart");data = (ActivityThread.ActivityClientRecord)msg.obj;data.packageInfo = ActivityThread.this.getPackageInfoNoCheck(data.activityInfo.applicationInfo, data.compatInfo);ActivityThread.this.handleLaunchActivity(data, (Intent)null);Trace.traceEnd(64L);
ActivityThread.ActivityClientRecord

就是AMS传过来的Activity

data.activityInfo.applicationInfo

所得到的属性我们称之为LoadedApk,可以提取到apk中的所有资源,那么APP内部是如何页面跳转的呢,比如我们从ActivityA跳转到ActivityB,我们可以将Activity看作Launcher,唯一不同的就是,在正常情况下ActivityB和ActivityA所在同一进程,所以不会去创建新的进程。

APP的启动流程就是这样了,欢迎留言探讨,记得持续关注哦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/254901.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp实现app跳转app

需要H5唤醒App看这篇文章&#xff1a;H5唤醒App 需求&#xff1a;公司的app跳转公司的另一个app 注意看注释&#xff01;&#xff01;&#xff01; 注意看注释&#xff01;&#xff01;&#xff01; 注意看注释&#xff01;&#xff01;&#xff01; 如何实现呢&#xff0c;…

【Spring】— 动态SQL :<if>元素

动态SQL &#xff1a;元素 在MyBatis中&#xff0c;<if>元素是常用的判断语句&#xff0c;主要用于实现某些简单的条件选择。在实际应用中&#xff0c;我们可能会通过多个条件来精确地查询某个数据。 【示例8-1】下面通过一个具体的案例来演示元素的使用。 &#xff0…

数组及详解冒泡排序

数组及详解冒泡排序 一维数组的创建和初始化一维数组的创建一维数组的初始化一维数组的应用一维数组在内存中的存储 二维数组的创建和初始化二维数组的创建二维数组的初始化二维数组的应用二维数组在内存中的存储 数组越界问题数组作为函数参数数组名的含义及特殊两个例子 冒泡…

chatgpt赋能python:Python中累乘的作用和用法

Python中累乘的作用和用法 在Python编程语言中&#xff0c;累乘指的是连续乘法&#xff0c;或者说是一系列数字的乘积。累乘的概念非常简单&#xff0c;但是实际上它在编程中有着广泛的应用。 累乘在Python中的实现方式 Python中&#xff0c;累乘可以通过多种方式来实现&…

25条关于人生感悟的经典句子

1.活得糊涂的人&#xff0c;容易幸福&#xff1b;活得清醒的人&#xff0c;容易烦恼。这是因为&#xff0c;清醒的人看得太真切&#xff0c;一较真&#xff0c;生活中便烦恼遍地&#xff1b;而糊涂的人&#xff0c;计较得少&#xff0c;虽然活得简单粗糙&#xff0c;却因此觅得…

佛家经典禅语语录句子

【1】&#xff1a;在天地之间觅得一方安详&#xff0c;听风雨&#xff0c;听山语&#xff0c;听禅语。 【2】&#xff1a;心是一方砚&#xff0c;不空亦不满。眼是一片天&#xff0c;不奢亦不贪。字是一盘餐&#xff0c;不腻亦不淡。深邃梅婷花向晚&#xff0c;零落幻影墨里寒。…

知足常乐--每日十问

如果你想走出常规&#xff0c;放松心情&#xff0c;以积极的的心态开始新的一天&#xff0c;那就很有必要以自问的方式开始一天&#xff0c;这些问题会给我们带来力量和好心情. 1、我拥有什么&#xff1f; 通常我们会为自己没有的东西而苦恼&#xff0c;却看不到自己拥有的&…

chatgpt赋能python:Python中的“5“+“5“:了解运算符重载和字符串拼接

Python中的 “5”“5”: 了解运算符重载和字符串拼接 Python中的运算符重载允许我们自定义类型的操作符行为。当我们使用加号运算符将两个对象相加时&#xff0c;Python会动态地确定该使用哪种类型的操作符行为。在使用字符串时&#xff0c;加号可以用于字符串的连接&#xff…

chatgpt赋能python:Python中的提取函数——数据清洗中必不可少的利器

Python中的提取函数——数据清洗中必不可少的利器 数据清洗是数据分析过程中不可或缺的一步&#xff0c;而Python中的提取函数则是数据清洗中必不可少的利器。本文将重点介绍一些Python中常用的提取函数&#xff0c;以帮助数据分析师更好地应对实际问题。 什么是提取函数&…

chatgpt赋能python:Python中的“或”语句:使用方法和示例

Python中的“或”语句&#xff1a;使用方法和示例 在Python编程中&#xff0c;“或"语句表示为"or”&#xff0c;它是逻辑运算符的一种形式。"或"语句可以用于组合两个或多个条件&#xff0c;只要其中一个条件成立&#xff0c;整个语句就会返回True。在本…

css3和h5的新特性

H5的新特性 1. 用于绘画 canvas 元素。 2. 用于媒介回放的 video 和 audio 元素。 3. 本地离线存储 localStorage 长期存储数据&#xff0c;浏览器关闭后数据不丢失&#xff1b; sessionStorage 的数据在浏览器关闭后自动删除。 4. 语意化更好的内容元素&#xf…

六、H5新特性

文章目录 一、H5的兼容二、H5新增特性2.1 语义化标签2.2 增强表单2.3 音频、视频 一、H5的兼容 支持 HTML5的浏览器包括Firefox(火狐浏览器)&#xff0c;IE9及其更高版本&#xff0c;Chrome(谷歌浏览器)&#xff0c;Safari,Opera等&#xff0c;国内的遨游浏览器&#xff0c;以…

h5简介和新特性

h5简介和新特性 语义化标签表单新增的type属性表单元素的其他属性新增的表单元素&#xff0c;datalisth5新增表单事件meter标签fieldset标签和legend标签自定义属性规范全屏接口上传图片实时预览进度条参考手册 学习资源推荐 https://blog.csdn.net/qq_42813491/article/detai…

最详细H5新特性

1. 语义化标签 &#xff08;1&#xff09;比如&#xff1a;header、conent、footer、aside、nav、section、article等语义化标签。 &#xff08;2&#xff09;语义化标签的好处&#xff1a;结构清楚&#xff0c;易于阅读&#xff0c;可维护性更高&#xff0c;有利于SEO的优化…

H5新增了哪些新特性

目录 前言 1.语义化标签 2.form表单增强 3.视频和音频 4.Canvas绘图 5.SVG绘图 6.地理位置定位&#xff08;Geolocation API &#xff09; 7.拖放API 8.Web Worker 9.Web Storage 10.Web Socket 总结 前言 本期为大家总结面试时常被问到的一个问题&#xff0c;那就…

HTML 5 的十大新特性

HTML5 十大新特性总结 一、语义标签 二、增强型表单 三、视频和音频 四、Canvas绘图 五、SVG绘图 六、拖拉API 七、WebWorker 八、WebStorage 九、WebSocket 十、地理定位 一、语义化标签 1.1 什么是语义化标签? 语义化标签既是使标签有自己的含义 1.2 语义化标签…

【靶场实战】Vulnhub - JANGOW: 1.0.1 靶标实战

靶场地址&#xff1a;https://www.vulnhub.com/entry/jangow-101,754/ 靶场IP&#xff1a;192.168.160.215 信息收集 使用Nmap对目标进行扫描 Nmap -sV -O -p- 192.168.160.215 经过漫长的等待扫描完成&#xff0c;该靶标开启了21、80两个端口&#xff0c;21端口运行服务为f…

什么样的打码网站算正规的打码网站

自动打码平台是通过计算机语言对图片图片验证码的一种识别&#xff0c;将图片验证码通过系统自动的识别出来&#xff0c;并且通过一定的途径自动输入需要填写图片验证码的框。这样的平台就成为打码平台平台。实际上就是对于图片验证码的识别破解。答题吧打码平台平台就是利用的…

自动打码神器的准确率你担心么?

现在干什么都需要验证码&#xff0c;比如注册账号、登录账号等一些日常上网操作经常要输入验证码&#xff0c;而很多网站的验证码越来越难看懂。这样对于需要批量操作的人来说真的是折磨啊&#xff0c;但是有了这款验证码自动识别软件之后&#xff0c;大家就可以轻松识别复杂的…

JAVA 实现对图片打码,打马赛克

一. 图片区域类 package com.example.demo.xxx;/*** 图片区域类* author jlm**/ public class ImageArea {int x; //指定区域左上角横坐标int y; //指定区域左上角纵坐标int width; //指定区域宽度int height; //指定区域高度public ImageArea(int x, int y, int width, int h…