Anroid应用启动流程与ActivityThread深入解析

目录

  • 开机与SystemServer启动
  • Launcher 进程启动过程
  • Launcher 桌面启动过程
  • ActivityThread与四大组件的暧昧
  • 学习ActivityThread价值在哪?
  • 如何在浩瀚源码中取其精华
  • 来自ActivityThread的启发与拓展

从开机到SystemServer启动概述

  • 1.按电源键通电后,引导芯片启动,引导芯片开始从固化在ROM里的预设代码执行,加载引导程序到到RAM,bootloader检查RAM,初始化硬件参数.

  • 2.Kernel层主要加载一些硬件设备驱动,初始化进程管理,内管管理、加载Driver驱动文件等,启动内核核心进程.

  • 3.硬件设备驱动与HAL层进行交互,启动Init进程 ,它启动后会启动adbd,logd等用户守护进程,同时fork出zygote进程。zygote进程是承上启下的存在.同时会反射调用com.android.internal.os.ZygoteInit 进入Java层。
  • 4.zygote进程启动完毕它会加载虚拟机,启动System Server进程(zygote孵化的第一个进程);System Server负责启动和管理整个Android Framework,包含ActivityManager,WindowManager,PackageManager,PowerManager等服务.
  • 5.zygote同时会启动相关的APP进程,它启动的第一个APP进程为Launcher,所有的APP进程都有zygote fork生成.

系统开机

SystemServer系统服务启动过程

  • SystemServer进程是在ZygoteInit.java的main方法中创建的,以下是SystemServer进程关键方法讲解。

  • 问题:为什么Zygote接受进程创建的申请使用Socket,不使用Binder等其他IPC方式?最后有答案

    ZygoteInit.java
    public static void main(String argv[]){
      //1.预加载frameworks/base/preloaded-classes和framework_res.apk资源,linux在fork进程时,只是为子进程创建一个数据结构,使子进程地址空间映射到与父进程相同的物理内存空间。虚拟机的创建也是实例拷贝,共享系统资源,如果每创建一个虚拟机都加载一份系统资源,将会非常耗时和浪费。子进程在刚fork生成时,完全共享父进程的进程物理空间,采用写时复制的方式共享资源。
        preloadClasses();
        preloadResources();
        preloadSharedLibraries();
    
       // 2. 启动system_server进程。该进程是framework的核心。
        if(argv[1].equals("start-system-server")){
                startSystemServer(); 
         } 
    
       //3.创建Socket服务
        registerZygoteSocket();
    
       //4.进入阻塞状态,等待连接,用以处理来自AMS申请进程创建的请求
      runSelectLoopMode(); 
        } 
    }
    
  • SystemServer的main方法主要做了以下三件事情,即启动不同类型的系统服务

    SystemServer.java
    public static void main(String argv[]){
      //创建系统的服务的管理者
      SystemServiceManager mSystemServiceManager = new SystemServiceManager(mSystemContext);
       //启动引导服务
       startBootstrapServices();
       //启动核心
       startCoreServices();
      //启动其他一般服务
       startOtherServices();
    }
    
  • 下图是SystemServer进程中启动的核心系统服务示意图

    system_server

Launcher进程启动过程

1.Launcher进程启动流程示意图

  • AMS启动完成后,在systemReady()中会去调用startHomeOnAllDisplays()来启动Launcher应用。Launcher进程启动流程图。

system_server

2.Launcher进程启动流程关键方法
  • 1.ActivityManagerService#systemReady是Launcher应用启动的入口

    ActivityManagerService.java
    public void systemReady(...){
     //开始拉起launcher应用的
     //ActivityTaskManagerInternal是抽象类,它的实现类是ATMS的内部类LocalService
     mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
    }
    
  • 2.RootActivityContainer#startHomeOnDisplay顾名思义,启动桌面Activity,核心思想是通过PKMS从已安装的所有应用中查询出符合CATEGORY_HOME的Intent的Activity。

    RootActivityContainer.java
    boolean startHomeOnDisplay(...) {
    //构建一个category为CATEGORY_HOME的Intent,表明是Home Activity
     Intent homeIntent = mService.getHomeIntent();
    //通过PKMS从系统所有已安装的应用中,找到一个符合HomeItent的Activity
    ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent); 
    //启动Home Activity。下面就是启动Activity的流程了
    ActivityStartController.startHomeActivity(homeIntent, aInfo, myReason,displayId);
        return true;
    }
    
  • 3.ActivityStackSupervisor#startSpecificActivityLocked在启动Activity时会通过进程名称processName到ATMS查询该进程的控制器对象。如果为空,则说明进程还没创建,则执行创建进程的流程,否则正常启动Activity。

    ActivityStackSupervisor.java
    startSpecificActivityLocked(ActivityRecord r) {
    //通过进程名称和进程uid查询出该进程控制器,如果为空则说明进程不存在,否则执行启动Activity的流程  
    WindowProcessController wpc =  mService.getProcessController(r.processName, r.info.applicationInfo.uid);
    if (wpc != null && wpc.hasThread()) {
      //进程存在,执行启动Activity流程
      realStartActivityLocked(....);        
    } else{
      //进程不存在,执行启动进程流程
      //进程启动完成,则会通过JNI方式调用ZygoteInit.java 进而调用ActivityThread#Main方法
      ActivityManagerService$LocalService.startProcess()
    }
    
  • 4.ProcessList#startProcessLocked方法,开始启动进程,并且显示指定了进程创成功后该进程的入口类为android.app.ActivityThread,入口方法均为Java约定俗成的public static void main

    ProcessList.java
    boolean startProcessLocked(){
    //指定了进程创建成功之后执行的进程的入口类,由此可验证ActivityThread才是Android应用的主入口
    String entryPoint = "android.app.ActivityThread";
    return startProcessLocked(..., entryPoint, ....);
    }
    
  • 进程创建最后在ZygoteProcess#attemptZygoteSendArgsAndGetResult的方法中把需要的参数通过socket的形式发送给Zygote进程执行fork命令

    ZygoteProcess.java
    void attemptUsapSendArgsAndGetResult(...){
    //创建socket,并建立跟Zygote进程的链接
    LocalSocket usapSessionSocket = new LocalSocket();          usapSessionSocket.connect(this.mUsapSocketAddress);
    
    //使用字符输出流把进程创建参数 发送到Socket Server
    BufferedWriter usapWriter =new BufferedWriter(
    new OutputStreamWriter(usapSessionSocket.getOutputStream()),Zygote.SOCKET_BUFFER_SIZE);
    DataInputStream usapReader =new DataInputStream(usapSessionSocket.getInputStream());usapWriter.write(msgStr);
    usapWriter.flush();
    }
    
  • 前面我们说过Zygote进程创建成功之后会进入ZygoteInit.java,在它的入口方法中创建了Socket Server服务接收申请进程创建的请求。

    ZygoteConnection.java
    Runnable processCommand(...){
    //通过Navtive方法fork() 创建新进程  
    int pid = Zygote.forkAndSpecialize(...
    parsedArgs.mNiceName, ...);
    
    //进程创建成功之后,会根据创建进程时指定的新进程的入口类"android.app.ActivityThread"反射调用它的main方法,从而进入到新进程的世界。  
    handleChildProc(parsedArgs, descriptors, childPipeFd,parsedArgs.mStartChildZygote);
    
    }
    

Launcher应用桌面启动过程

1.Launcher HomeActivity启动流程图
  • 上面讲到App应用进程创建完成后,会通过JNI方式调用ZygoteInit.java,进而调用ActivityThread#Main方法。这里是App的主入口,也是MainLooper的发源地,同时它还会拉起Application和HomeActivity。

    system_server

2.Launcher HomeActivity启动流程关键方法
  • 1.ActivityThread#main这是所有Android 应用进程的入口,在这里开启了Looper的循环,从而使得主线程具有消息分发机制。同时也会负责执行来自AMS对四大组件创建和生命周期调度的执行工作。

    ActivityThread.java
    public static void main(String[] args) {
        .....
        //ActivityThread之所以称为主线程,就是因为在他的入口处就开始了MainLooper的初始化和loop工作,
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        //该方法至关重要,
        thread.attach(false, startSeq);
    
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        Looper.loop();
        .....
    }
    
  • 2.ActivityThread#attach是在main方法中触发的,该方法的主要作用是向AMS注册自己,同时传递IApplicationThreadBinder对象以便于AMS能够对本进程四大组件进行调度。

    ActivityThread.java
    private void attach(boolean system, long startSeq) { 
      //IApplicationThread mAppThread是内部类实现了IBinder接口
      //向AMS注册自己,并且传递了mAppThread对象,以便AMS能够调度本进程的四大组件生命周期
       IActivityManager mgr = ActivityManager.getService();
       mgr.attachApplication(mAppThread,startSeq) 
    }
    
  • 3.ActivityManagerService#attachApplicationLocked,一是负责Application的创建工作的调度,这会流转到应用进程的ActivityThread。

    二是负责继续启动之前未完成启动的Activity.即HomeActivity.

    ActivityManagerService.java
    //1是创建Application对象,2启动该进程的第一个Activity
    boolean attachApplicationLocked(IApplicationThread thread,..) {
    
    //该方法回调到ActivityThread的handleBindApplication方法 
    //创建Application,并调用onCreate方法  
    thread.bindApplication(...);
    
    //执行ATMS的attachApplication,开始启动第一个Activity.
    //会执行到上面提到的ActivityStackSupervisor#realStartActivityLocked()
    //把刚才待启动的Activity继续启动  
    mAtmInternal.attachApplication(...);
    }
    
  • 4.ActivityStackSupervisor#realStartActivityLocked负责AMS服务启动Activity最后的流程。在Android 10中把Activity生命周期拆分成几种生命周期状态的Item。LaunchActivityItem,ResumeActivityItem,PauseActivityItem,StopActivityItem,DestroyActivityItem.每个Item负责对Activity生命周期的具体的调度

    ActivityStackSupervisor.java
    boolean realStartActivityLocked(ActivityRecord r)  {
    ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.appToken); 
    
    //这里把启动启动Activity操作的Item添加到集合中  clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
    
    //开始执行生命周期,流程就会再次进入到应用进程中的IApplicationThread的scheduleTransaction方法                                                     
    clientLifecycleManager.scheduleTransaction(clientTransaction);
    }
    
  • ClientTransactionHandler,实际上Android 10中ActivityThread是继承自ClientTransactionHandler的,所以下面这个sendMessage最后会被ActivityThread$H 捕获。进而触发下面的performLaunchActivity

    ClientTransactionHandler.java
    void scheduleTransaction(ClientTransaction transaction) {
    
    sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    
        }
    
  • ActivitityThread#performLaunchActivity主要负责执行来自AMS对Activity创建调度的执行,Activity的创建是通过Instrumentation反射创建出来的。进而执行Activity的attch,onCreate。所以Activity才有了生命周期的能力

    ActivitityThread.java
    public Activity performLaunchActivity(...) {
    //通过反射构建出 Activity对象 
    ClassLoader cl = appContext.getClassLoader();
    Activity activity = mInstrumentation.newActivity(
             cl, component.getClassName(), r.intent);
    
      //调度Activity的attach()方法
      activity.attach()
      //调度Activity的onCreate方法  
      mInstrumentation.callActivityOnCreate()  
    
      //所以我们发现Activity也是反射创建出来的,因为有了ActivityThread的调度才具备了生命周期。  
      //至此Launcher应用的进程创建和HomeActivity的启动我们就分析完了  
    }
    

    ActivityThread与四大组件的暧昧

  • 实际上四大组件的另外三个也是由AMS调度由ActivityThread创建并赋予他们生命周期。

  • startService

    scheduleCreateService()->{
      sendMessage(H.CREATE_SERVICE, s)->{
        handleBindService((BindServiceData)msg.obj)->{
          //反射创建Service
           ClassLoader cl = packageInfo.getClassLoader();
           Service service = appCompentFactory
                        .instantiateService(cl,...)
             //紧接着调用核心方法、赋予生命周期
             service.attach(           
             service.onCreate();
        }
      }
    }
    
  • BroadcastReceiver

    scheduleReceiver()->{
      sendMessage(H.RECEIVER, r)->{
       //反射创建对象,并紧接着调用它的 onReceive方法
       ClassLoader cl = context.getClassLoader();
       BroadcastReceiver receiver =          appCompentFactory.instantiateReceiver(cl,...);
    
        receiver.onReceive(data.intent);
      }
    }
    

    学习Launcher应用启动的价值

  • ActivityThread是Android程序第一入口、主线程开始的发源地,也是负责AMS对四大组件调度的具体执行类。

  • 无论是开发者,还是架构师,都应该对Framework知根知底,才能游刃有余。研读源码也是初中级进阶高专开发者必经之路。应用层无法解决的可以从Framework层级解决。

  • 深入理解四大组件从被创建到具备生命周期的始末,探索黑科技,比如学习插件化的从时候我们需要学习Hook机制,但是学习Hook机制的时候我们需要掌握Activity的启动流程、消息机制等等机制。

  • 学习Android源码有助于我们学习其中的设计模式、思想、架构。

  • 熟悉整个源码的架构,有助于我们更加正确地调用Android提供的SDK,写出高效正确的代码。

  • 学习源码有助于我们面试,因为大公司都喜欢问这些。

    如何在浩瀚源码中取其精华

  • 1.调用堆栈是分析源码的最有效途径,一遍两遍的debug跟进就能摸清调用流程。

  • 2.研究Framework时,由于不方便断点调试,代码量浩瀚,切不可一句一方法的研究,推断。可以先查找相关博客,理解大致流程 和核心方法,从入口方法跟进。Framework中很多面向接口,面向父类的调用,此时可根据类名来推断真正的实现类。比如ActivityTaskManagerInternal,Internal顾名思义内部的意思,所以能够在ActivityTaskManagerService中查找到该类的实现。

  • 3.善用adb shell ,比如权限申请弹出的窗口,它实际是个Activity,可以通过adb shell dumpsys topActivity找到入口。

  • 4.下载源码导入IDea方便查看,更甚者或编译Rom

来自Launcher应用的启发与拓展

  • 1.在分析Zygote进程创建之后,会进入Java世界,ZygoteInit.java。那么是如何从C++转到Java的呢?我们知道,Java调用C++方法可以通过JNI。我们来看下C++如何主动访问Java实例方法和静态方法。

    //1从classpath路径下搜索ZygoteInit这个类,并返回该类的Class对象,进程创建创建完成后会固定加载这个类。
    jClass clazz = (*env)->FindClass(env, "com/android/internal/os/ZygoteInit");
    //2获取类的默认构造方法ID
    jmethodID mid_construct = (*env)->GetMethodID(env,clazz, "<init>","()V"); 
    //3创建该类的实例
    jobject jobj = (*env)->NewObject(env,clazz,mid_construct); 
    //4查找实例方法的ID
    jmethodID mid_instance = (*env)->GetMethodID(env, clazz, "main", "(Ljava/lang/String;I)V"); 
    //5调用对象的实例方法
     jstring str_arg = (*env)->NewStringUTF(env,"我是实例方法");
    (*env)->CallVoidMethod(env,jobj,mid_instance,str_arg,200);
    
  • 2.我们还分析了AMS向Zygote进程申请创建进程的时候,使用管理socket的方式,为什么不适用Binder呢?

    • 因为服务端的Binder必须运行在线程池中,而客户端在连接服务端Binder时会导致本线程挂起,如果服务端Binder意外死亡,会导致客户端本线程死锁.另外Linux的fork()方法 不允许多线程.
  • 3.我们知道是因为在ActivityThread的main方法中开始了mainLooper的循环,所以子线程可以向主线程使用handler发送消息,那么如何让主线程向子线程发消息呢?

    new Thread(new Runnable() {
     @Override
     public void run() {
    //在子线程中手动调用Loop.prepare()和Loop.loop(),并且创建handler时传递本线程的Looper,那么主线程使用handler.sendMessage就可以被该Handler捕获了
     Looper.prepare();
     Handler handler = new Handler(Looper.myLooper()){
      @Override
      public void handleMessage(@NonNull Message msg) {
            //处理来自主线程的消息
                        }
                    };
                }
            });
     Looper.loop();
    });
    
  • 4.ActivityThread中有下面这个方法,因为我们还可以反射该方法,无论是组件化模式还是孵化器模式,在任意位置都可以获取Application对象。无需初始化

    public static Application currentApplication() {
      ActivityThread am = currentActivityThread();
      return am != null ? am.mInitialApplication : null;
     }
    
  • 5.ActivityThread中还有如下字段,存储着本进程所有的Activity的信息,我们知道在高版本上并没有有效的Api获取TopActiivty和前后台切换判断,那么反射该字段便可以轻松解决该问题。

    ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>()
    
  • 6.如何开发一个桌面Launcher应用

    如何自定义开发一个桌面应用
    
    <intent-filter>
     <action android:name=“android.intent.action.MAIN”>
     <category android:name=“android.intent.category.LAUNCHER” /> 
     <category android:name=“android.intent.category.HOME”/>      
     <category android:name=“android.intent.category.DEFAULT”/>   
    </intent-filter>
    

results matching ""

    No results matching ""