APP启动流程解析

APP启动流程解析前言当我们点击手机屏幕上的软件图标时 就可以打开这个软件 看似很简单的过程其实包含了许多的底层交互 看了还不明白 欢迎来打我 一 启动流程简介首先要知道的是 手机屏幕其实就是一个 Activity 我们专业点将其称为 Launcher 相信做过车载设备开发的朋友肯定不会陌生 Launcher 是手机厂商提供的 不同的手机厂商比拼的就是 Launcher 的设计 当然我们自己也可以去编写 Lau

前言

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

一 . 启动流程简介

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

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

 
   
    
    
  

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

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

                    APP启动流程解析       

                                             

                                                             APP启动流程解析

                APP启动流程解析                       

                                                            APP启动流程解析              

            APP启动流程解析    

                                                             APP启动流程解析   

          APP启动流程解析

                                                             APP启动流程解析

         APP启动流程解析

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

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系统中其实地位很低的。

                                                         APP启动流程解析

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的代理对象,说到代理其实就是代理模式,关于什么是代理模式以及动态代理和静态代理的使用可以持续关注我,后面会单独写篇文章进行介绍。

            APP启动流程解析

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的启动流程就是这样了,欢迎留言探讨,记得持续关注哦。

APP启动流程解析

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/210863.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午11:44
下一篇 2026年3月18日 下午11:44


相关推荐

  • 三极管工作原理介绍

    三极管工作原理介绍转载至 电子发烧友 http www elecfans com yuanqijian sanjiguang 201704255108 html 三极管 全称应为半导体三极管 也称双极型晶体管 晶体三极管 是一种电流控制电流的半导体器件 其作用是把微弱信号放大成幅度值较大的电信号 也用作无触点开关 晶体三极管 是半导体基本元器件之一 具有电流放大作用 是电子电路的核心元件 三极管是在一块

    2026年3月26日
    2
  • Linux下安装redis_redis安装部署centos

    Linux下安装redis_redis安装部署centos1、下载源代码http://code.google.com/p/redis/downloads/list下载redis-1.2.6.tar.gz将下载包拷贝到/usr/local/webserver/redis-1.2.6/下2、安装ta

    2022年10月4日
    3
  • goland调试go代码_debug运行

    goland调试go代码_debug运行如何使用dlv结合Goland进行程序debug调试相信很多Golang的初级玩家不会进行程序的Debug定位问题单纯的靠脑子,或者效率很低的不断的添加日志打印,别问我为什么知道的因为我就是这样的,最好最快捷的问题定位方式一定是使用Debug打断点调试,这时就引出了本文的主角dlv。实际上,delve才是全称,dlv只是启动命令,如果VScode,Goland,默认使用的调试器就是基于delve的。安装dlv参考官方的安装方法,把dlv命令安装在go.

    2025年6月14日
    6
  • 素数判断算法(高效率)「建议收藏」

    素数判断算法(高效率)「建议收藏」chuanbindeng的素数判断算法关于素数的算法是信息学竞赛和程序设计竞赛中常考的数论知识,在这里我跟大家讲一下寻找一定范围内素数的几个算法。看了以后相信对大家一定有帮助。   正如大家都知道的那样,一个数n如果是合数,那么它的所有的因子不超过sqrt(n)–n的开方,那么我们可以用这个性质用最直观的方法来求出小于等于n的所有的素数。   num=0;

    2022年6月18日
    33
  • CANoe之CAPL编程(CANoe系列其三)「建议收藏」

    CANoe之CAPL编程(CANoe系列其三)「建议收藏」CANoe之CAPL编程(CANoe系列其三)摘要1、CAPL概述1.1、CAPL语言特性1.2、CAPL的程序结构1.3、CAPL的数据类型1.4、CAPL事件类型概述2、CAPL事件类型2.1、系统事件2.2、CAN控制器事件2.3、CAN消息事件2.4、时间事件2.5、键盘事件2.6、错误帧事件2.7、环境变量事件2.8、系统变量事件摘要    &…

    2025年7月28日
    6
  • 理解mipi协议

    理解mipi协议完成 mipi 信号通道分配后 需要生成与物理层对接的时序 同步信号 MIPI 规定 传输过程中 包内是 200mV 包间以及包启动和包结束时是 1 2V 两种不同的电压摆幅 需要两组不同的 LVDS 驱动电路在轮流切换工作 为了传输过程中各数据包之间的安全可靠过渡 从启动到数据开始传输 MIPI 定义了比较长的可靠过渡时间 加起来最少也有 600 多 ns 而且规定各个时间参数是可调的 所以需要一定等待时间 需要缓

    2026年3月16日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号