我的编程空间,编程开发者的网络收藏夹
学习永远不晚

详解Android中的ActivityThread和APP启动过程

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

详解Android中的ActivityThread和APP启动过程

ActiviryThread

ActivityThread的初始化

ActivityThread即Android的主线程,也就是UI线程,ActivityThread的main方法是一个APP的真正入口,MainLooper在它的main方法中被创建。


//ActivityThread的main方法
public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    //在attach方法中会完成Application对象的初始化,然后调用Application的onCreate()方法
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    ...
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

接下来从主线程Looper的初始化和ApplicationThread及Activity的创建启动两方面,通过源码了解学习下大致的流程。

主线程Looper的初始化

Looper.prepareMainLooper();相关的代码如下


//主线程Looper的初始化
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

//普通线程Looper的初始化
public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

看过Handler源码就知道,主线程Looper的初始化和普通线程Looper的初始化很相似,但还是有以下几个区别

1.普通线程的Prepare()默认quitAllowed参数为true,表示允许退出,而主线程也就是ActivityThread的Looper参数为false,不允许退出。这里的quitAllowed参数,最终会传递给MessageQueue,当调用MessageQueue的quit方法时,会判断这个参数,如果是主线程,也就是quitAllowed参数为false时,会抛出异常。


//Looper的退时会判断quitAllowed
void quit(boolean safe) {
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    synchronized (this) {
        ...
    }
}

2.我们注意到主线程Looper初始化之后,赋值给了成员变量sMainLooper,这个成员的作用就是向其他线程提供主线程的Looper对象。这下我们就应该知道为什么Looper.getMainLooper()方法能获取主线程的Looper对象了


public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

主线程Handler的初始化

在ActivityThread的main方法中我们注意到一行代码:


ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
    sMainThreadHandler = thread.getHandler();
}

见名知意,这是获取主线程的Handler,那么主线程的Handler是在什么时候初始化的呢?


//与之相关的代码如下:
//ActivityThread的成员变量
final H mH = new H();

final Handler getHandler() {
    return mH;
}

从以上代码中可以看到,主线程的Handler作为ActivityThread的成员变量,是在ActivityThread的main方法被执行,ActivityThread被创建时而初始化,而接下来要说的ApplicationThread中的方法执行以及Activity的创建都依赖于主线程Handler。至此我们也就明白了,主线程(ActivityThread)的初始化是在它的main方法中,主线程的Handler以及MainLooper的初始化时机都是在ActivityThread创建的时候。

ApplicationThread及Activity的创建和启动

以上的代码和流程,就是对 MainLooper 和 ActivityThread 的初始化,我们接下来看一下 ActivityThread 的初始化及其对应的 attach 方法,在thread.attach方法中,ActivityManagerService通过attachApplication方法,将ApplicationThread对象绑定到ActivityManagerService,ApplicationThread是ActivityThread的私有内部类,实现了IBinder接口,用于ActivityThread和ActivityManagerService的所在进程间通信。


//ActivityThread的attach方法:
private void attach(boolean system) {
    ...
    if (!system) {
        final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }else{
            ...
        }
    }
}

//ActivityManagerService中的方法:
public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid);
        Binder.restoreCallingIdentity(origId);
    }
}

这里的个人理解是:在每个ActivityThread(APP)被创建的时候,都需要向ActivityManagerService绑定(或者说是向远程服务AMS注册自己),用于AMS管理ActivityThread中的所有四大组件的生命周期。

上述AMS的代码中attachApplicationLocked方法比较复杂,主要功能有两个,详见注释,这里忽略了很多代码细节,具体的流程可以看源码


//AMS中的方法,主要功能有以下两步
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
    ...
    //主要用于创建Application,用调用onCreate方法
    thread.bindApplication(...);
    ...
    //主要用于创建Activity
    if (mStackSupervisor.attachApplicationLocked(app)) {
        ...
    }
}

1.thread.bindApplication:主要用于创建Application,这里的thread对象是ApplicationThread在AMS中的代理对象,所以这里的bindApplication方法最终会调用ApplicationThread.bindApplication()方法,该方法会向ActivityThread的消息对应发送BIND_APPLICATION的消息,消息的处理最终会调用Application.onCreate()方法,这也说明Application.onCreate()方法的执行时机比任何Activity.onCreate()方法都早。


//ActivityThread中的bindApplication方法
public final void bindApplication(...) {
    ...
    // 该消息的处理,会调用handleBindApplication方法
    sendMessage(H.BIND_APPLICATION, data);
}
//ActivityThread中的handleBindApplication方法
private void handleBindApplication(AppBindData data) {
    ...
    try {
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;
        ...
        try {
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
        }
    } finally {
    }
}
    
//LoadedApk中的方法,用于创建Application
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
    //如果存在Application的实例,则直接返回,这也说明Application是个单例
    if (mApplication != null) {
        return mApplication;
    }

    Application app = null;
    //...这里通过反射初始化Application

    if (instrumentation != null) {
        try {
            //调用Application的onCreate方法
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
        }
    }
    return app;
}

2.mStackSupervisor.attachApplicationLocked(app):用于创建Activity,mStackSupervisor是AMS的成员变量,为Activity堆栈管理辅助类实例,该方法最终会调用ApplicationThread类的scheduleLaunchActivity方法,该方法也是类似于第一步,向ActivityThread的消息队列发送创建Activity的消息,最终在ActivityThread中完成创建Activity的操作。


boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
    ...
    if (realStartActivityLocked(hr, app, true, true)) {
        ...
    }          
    ...
}

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
    boolean andResume, boolean checkConfig) throws RemoteException {
    ...
    try {
        //调用ApplicationThread的scheduleLaunchActivity用于启动一个Activity
        app.thread.scheduleLaunchActivity(...);
    } catch (RemoteException e) {

    }
}

ApplicationThread的scheduleLaunchActivity方法会向ActivityThread发送LAUNCH_ACTIVITY信息,用于启动一个Activity,该消息的处理会调用ActivityThread的handleLaunchActivity方法,最终启动一个Activity

以上就是从ActivityThread的main方法执行到Activity的创建之间的流程,至于ActivityThread的main方法执行时机,以及执行前的流程和Activity的具体创建过程,可以接着看APP的启动过程

APP的启动

系统的启动过程

在学习APP的启动之前先简单了解下系统的启动,有助于我们更好的学习APP的启动。系统的启动过程很复杂,这里简单化,只关心大致流程和涉及到的一些名词以及相关类的作用

APP的启动可以简单总结为一下几个流程:

加载BootLoader --> 初始化内核 --> 启动init进程 --> init进程fork出Zygote进程 --> Zygote进程fork出SystemServer进程

  • 系统中的所有经常进程都是由Zygote进程fork出来的
  • SystemServer进程是系统进程,很多系统服务,例如ActivityManagerService、PackageManagerService、WindowManagerService…都是存在该进程被创建后启动
  • ActivityManagerServices(AMS):是一个服务端对象,负责所有的Activity的生命周期,AMS通过Binder与Activity通信,而AMS与Zygote之间是通过Socket通信
  • ActivityThread:本篇的主角,UI线程/主线程,它的main()方法是APP的真正入口
  • ApplicationThread:一个实现了IBinder接口的ActivityThread内部类,用于ActivityThread和AMS的所在进程间通信
  • Instrumentation:可以理解为ActivityThread的一个工具类,在ActivityThread中初始化,一个进程只存在一个Instrumentation对象,在每个Activity初始化时,会通过Activity的Attach方法,将该引用传递给Activity。Activity所有生命周期的方法都有该类来执行

APP的启动过程

APP的启动,我们使用一张图来说明这个启动过程,顺便也总结下上面所说的ActivityThread的main方法执行到Activity的创建之间的流程。

1.点击桌面APP图标时,Launcher的startActivity()方法,通过Binder通信,调用system_server进程中AMS服务的startActivity方法,发起启动请求

2.system_server进程接收到请求后,向Zygote进程发送创建进程的请求

3.Zygote进程fork出App进程,并执行ActivityThread的main方法,创建ActivityThread线程,初始化MainLooper,主线程Handler,同时初始化ApplicationThread用于和AMS通信交互

4.App进程,通过Binder向sytem_server进程发起attachApplication请求,这里实际上就是APP进程通过Binder调用sytem_server进程中AMS的attachApplication方法,上面我们已经分析过,AMS的attachApplication方法的作用是将ApplicationThread对象与AMS绑定

5.system_server进程在收到attachApplication的请求,进行一些准备工作后,再通过binder IPC向App进程发送handleBindApplication请求(初始化Application并调用onCreate方法)和scheduleLaunchActivity请求(创建启动Activity)

6.App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送BIND_APPLICATION和LAUNCH_ACTIVITY消息,这里注意的是AMS和主线程并不直接通信,而是AMS和主线程的内部类ApplicationThread通过Binder通信,ApplicationThread再和主线程通过Handler消息交互。 ( 这里猜测这样的设计意图可能是为了统一管理主线程与AMS的通信,并且不向AMS暴露主线程中的其他公开方法)

7.主线程在收到Message后,创建Application并调用onCreate方法,再通过反射机制创建目标Activity,并回调Activity.onCreate()等方法

8.到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染后显示APP主界面

APP启动过程的部分代码思考

在上面学习APP的启动过程中,看源码的同时注意到一个代码,就是主线程Handler在接收到LAUNCH_ACTIVITY创建Activity的消息后,创建Activity的部分代码如下:


//主线程Handler接收到创建Activity的消息LAUNCH_ACTIVITY后,最终会调用performLaunchActivity方法
//performLaunchActivity方法会通过反射去创建一个Activity,然后会调用Activity的各个生命周期方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        //这里是反射创建Activity
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    }

    try {
        //这里注意,又调用了一次Application的创建方法,但是前面分析过,Application是个单例,所以这里的实际上是获取Application实例,但是这里为什么会再次调用创建Application的方法呢?
        
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        ...
    } 
    ...
    return activity;
}

在上面的代码中,简单注释了一下在Activity的创建方法中,会再次调用Application的创建方法(第一次调用是在接收到BIND_APPLICATION消息的时候),个人觉得这里再次调用Application的创建方法,除了获取已经存在的Application实例这种情况,另外一种情况还有可能是要创建的这个Activity属于另外一个进程,当去启动这个新进程中的Activity时,会先去创建新进程和Application实例,因为我们知道一个常识:

1.APP中有几个进程,Application会被创建几次

2.新进程中所有变量和单例会失效,因为新进程有一块新的内存区域

那么这两点的关系就是,因为新进程中Application实例会为空,所以会再次去创建Application实例,这也就是第一点中我们所说的常识:APP中有几个进程,Application会被创建几次


//创建Application的方法
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
    //如果存在Application的实例,则直接返回,这也说明Application是个单例
    if (mApplication != null) {
        return mApplication;
    }

    Application app = null;
    //...创建Application
    return app;
}

那么依次类推,Service作为四大组件之一,类似于Activity的创建和启动,创建Service的方法中会不会也调用了创建Application的方法(makeApplication方法),答案是肯定的!和Activity的创建类似,当我们调用startService的时候,也是通过Binder向AMS发送创建Service的请求,AMS准备后再向APP进程发送scheduleCreateService的请求,然后主线程handle收到CREATE_SERVICE的消息,调用handleCreateService创建Service的方法。在创建Service的方法handleCreateService中也调用了创建Application的方法,具体代码看源码吧。所以我们也彻底明白了为什么APP中有几个进程,Application会被创建几次,以及Application为什么是个单例。

总结

APP的启动过程很复杂,代码错综交横,这里分析了大概流程,学习这部分的过程中还是有很多收获,例如知道了AMS与主线程的关系,主线程main方法中就是APP的入口,Binder通信机制和handler消息机制在这个过程中的重要作用,Application的创建时机以及Application为什么是单例,为什么有几个进程就创建几个Application…等等 。

以上就是详解Android中的ActivityThread和APP启动过程的详细内容,更多关于Android ActivityThread APP启动过程的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

详解Android中的ActivityThread和APP启动过程

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android中的ActivityThread和APP启动过程是什么

ActivityThread是Android中负责管理所有Activity的线程,它负责处理Activity的生命周期、事件分发、消息处理等工作。APP启动过程如下:用户点击应用图标,系统会启动应用的进程。系统会创建一个Activity
Android中的ActivityThread和APP启动过程是什么
2024-03-08

Android中APP的启动过程是什么

在Android中,APP的启动过程主要包括以下几个步骤:1. 用户点击APP图标或通过其他方式触发APP启动的事件。2. 系统根据APP的包名和启动模式,查找并启动对应的Activity组件。3. 系统会依次调用Activity的生命周期
2023-08-08

Android中通过外部程序启动App的三种方法

第一种:直接通过包名:代码如下: Intent LaunchIntent = getPackageManager().getLaunchIntentForPackage("com.joyodream.jiji");
2022-06-06

详解Android中App的启动界面Splash的编写方法

一、Splash界面的作用 用来展现产品的Logo 应用程序初始化的操作 检查应用程序的版本 检查当前应用程序是否合法注册 二、界面的xml定义 写一个布局背景设置为产品的logo图片,再添加一个textview显示版本号。
2022-06-06

详解Android冷启动实现APP秒开的方法

一、前言 在阅读这篇文章之前,首先需要理解几个东西: 1、什么是Android的冷启动时间? 冷启动时间是指用户从手机桌面点击APP的那一刻起到启动页面的Activity调用onCreate()方法之间的这个时间段。 2、在冷启
2022-06-06

Android应用框架之应用启动过程详解

在Android的应用框架中,ActivityManagerService是非常重要的一个组件,尽管名字叫做ActivityManagerService,但通过之前的博客介绍,我们知道,四大组件的创建都是有AMS来完成的,其实不仅是应用程序
2022-06-06

Android中 Activity的启动过程有哪些

Android中 Activity的启动过程有哪些,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。分析Android Activity的启动过程
2023-05-30

详解Android广播Broadcast的启动流程

Android中的广播是一种用于应用程序之间通信的机制。它允许应用程序发送和接收系统级或应用程序级的广播消息。当一个广播被发送时,系统会自动启动广播接收器来处理该广播。下面是Android广播的启动流程:1. 广播发送:应用程序发送一个广播
2023-08-11

详解Android更改APP语言模式的实现过程

一、效果图二、描述更改Android项目中的语言,这个作用于只用于此APP,不会作用于整个系统 三、解决方案(一)布局文件2022-06-06

[图解]Android源码分析——Activity的启动过程

Activity的启动过程一.Launcher进程请求AMSLauncher.java的startActivitySafely方法的执行过程:Activity.java中startActivity方法的执行过程:startActivityF
2022-06-06

Android 避免APP启动闪黑屏的解决办法(Theme和Style)

前几天Boss就反应说,机器每次启动程序都会闪一下黑屏,这个客户不接受。没办法,只能想想怎么解决,最后找到了下面的方法。闪黑屏的原因主要是我们启动Activity的时候,需要跑完onCreate和onResume才会显示界面。也就是说需要处
2022-06-06

Android中Activity生命周期和启动模式详解

Activity生命周期经典图解:按键对生命周期的影响:BACK键:当我们按BACK键时,我们这个应用程序将结束,这时候我们将先后调用onPause()->onStop()->onDestory()三个方法。 再次启动App时,会执行onC
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录