Android 系统启动流程

Android 系统启动流程

本文基于android8.0系统源码来分析

系统启动大致流程图分析,一图以蔽之:

system-start- analysis

其中,启动电源、引导程序Bootloaderlinux内核启动 部分读者自行了解,重点关注:init 进程启动过程,zygote进程启动过程,SystemServer进程启动过程,Launcher启动过程

init 进程启动过程

init 进程是Android系统创建的第一个进程,其职责之一:创建zygote进程和属性服务等。

  1. init 入口函数

init进程的入口函数是main system/core/init/init.cpp 源码

int main(int argc, char** argv){
   ...
   if (!strcmp(basename(argv[0]), "watchdogd")) {
      return watchdogd_main(argc, argv);
   }
   ...
   // 1. 创建文件夹并挂载到 initramdisk
   mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
   mkdir("/dev/pts", 0755);
   mkdir("/dev/socket", 0755);
   mount("devpts", "/dev/pts", "devpts", 0, NULL);
   #define MAKE_STR(x) __STRING(x)
   mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
   ...
 
   // 2. 初始化属性相关属性
   property_init();
   process_kernel_dt();
   process_kernel_cmdline();
   ...
   // 3. 启动属性服务
   start_property_service();
   set_usb_controller();
   ...
   Parser& parser = Parser::GetInstance();
   ...
   std::string bootscript = GetProperty("ro.boot.init_rc", "");
   if (bootscript.empty()) {
      // 4. 解析init.rc 文件
      parser.ParseConfig("/init.rc");
      parser.set_is_system_etc_init_loaded(
              parser.ParseConfig("/system/etc/init"));
                                                              parser.set_is_vendor_etc_init_loaded(parser.ParseConfig("/vendor/etc/init"));
      parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
   }
   ...
   property_load_boot_defaults();
   export_oem_lock_status();
}

init main 函数做了很多事情,需要重点关注代码中的注释部分。注释4处解析init.rc

  1. init.rc

    它是由Android Init Language介绍脚本编写的,它主要包含五种类型语句:
    ActionCommandsServicesOptionsImport,较为重要的语法介绍:

    on <trigger> [&& <trigger>]* //设置触发器  
       <command>  
       <command> //动作触发之后要执行的命令
    

    截取init.rc 部分Action类型语句代码,如下所示:

    on init
        sysclktz 0
        copy /proc/cmdline /dev/urandom
        copy /default.prop /dev/urandom
        ...
        mkdir /dev/stune/foreground
        mkdir /dev/stune/background
        mkdir /dev/stune/top-app
    

    不难发现,init.rc 组合了许多系统执行命令。为了分析如何创建zygote,主要查看下Service 类型语句,格式如下

    service <name> <pathname> [ <argument> ]*   //<service的名字><执行程序路径><传递参数>  
       <option>  //option是service的修饰词,影响什么时候、如何启动services  
       <option> 
    

    每一个服务对应一个rc文件,相应的zygote服务的启动脚本则在init.zygoteXX.rc中定义,切换到system/core/rootdir/源码目录,存在多个形似zygote.rc文件,这里,选取64位处理器为例,也即为system/core/rootdir/init.zygote64.rc

    service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
       class main //1. zygote 进程的类名
       priority -20
       user root
       group root readproc
       socket zygote stream 660 root system
       onrestart write /sys/android_power/request_state wake
       onrestart write /sys/power/state on
       onrestart restart audioserver
       onrestart restart cameraserver
       onrestart restart media
       onrestart restart netd
       onrestart restart wificond
       writepid /dev/cpuset/foreground/tasks
    

    结合Init 脚本语言的Service类型规范,可以得出init进程要启动的进程名称为zygote, zygote进程执行程序的路径为/system/bin/app_process64注释1 后文会用到

  2. init进程fork zygote进程

    zygote 启动脚本init.zygote64.rc中得出zygote的class name 为main。 在init.rc配置中

    ...
    on nonencrypted
       class_start main
       class_start late_start
    ...   
    

    结合Android Init Language脚本Action 类型语句,class_start 是一个COMMAND ,用于启动还没有运行的指定服务,对应的函数是system/core/init/builtins.cpp#do_class_start

    static int do_class_start(const std::vector<std::string>& args) {
         /* Starting a class does not start services
          * which are explicitly disabled.  They must
          * be started individually.
          */
         ServiceManager::GetInstance().
         ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); }); // 调用了Service.StartIfNotDisabled
         return 0;
    }
    

    system/core/init/service.cpp 的StartIfNotDisabled函数

    bool Service::StartIfNotDisabled() {
        if (!(flags_ & SVC_DISABLED)) {
            return Start(); // 执行Start函数
        } else {
            flags_ |= SVC_DISABLED_START;
        }
        return true;
    }
    

    执行Start函数:

    bool Service::Start() {
         flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
         if (flags_ & SVC_RUNNING) {
              //1. 如果Service已经启动,则不启动
              return false;
         }
         ...
         struct stat sb;
         // 2. 判断启动的Service对应的执行文件是否存在,不存在则不启动该Service
         if (stat(args_[0].c_str(), &sb) == -1) {
              PLOG(ERROR) << "cannot find '" << args_[0] << "', disabling '" << name_ << "'";
              flags_ |= SVC_DISABLED;
              return false;
         }
         ...
         pid_t pid = -1;
         if (namespace_flags_) {
              pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
         } else {
              // 3. fork 函数创建子进程 
              pid = fork();
         }
         
         if (pid == 0) {
              ...
              std::vector<char*> strs;
              ExpandArgs(args_, &strs);
              // 4. 通过execve 函数执行程序
              if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
                 PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
              }
         }
    }
    

    注释3处将zygote进程启动; 注释4处,在子进程中调用execve函数来执行/system/bin/app_process64 ,这样就会进入framework/cmds/app_process/app_main.cpp源码main 的函数

    int main(int argc, char* const argv[]){
        ...
        if (zygote) {
           // 1. 执行Zygote 进程Java框架层代码
           runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
        }  else if (className) {
           runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
        } else {
           fprintf(stderr, "Error: no class name or --zygote supplied.\n");
           app_usage();
           LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
        }
    }
    

    从注释1:调用AppRuntime#start 执行Zygote 进程Java框架层代码

  3. 属性服务初始化与启动

    init入口函数 system/core/init/init.cpp源码注释2,property_init()函数具体实现代码system/core/init/property_service.cpp

    void property_init() {
         if (__system_property_area_init()) {
            LOG(ERROR) << "Failed to initialize property area";
            exit(1);
         }
    }
    

    其中__system_property_area_init 函数用来初始化属性内存区域。init入口函数 system/core/init/init.cpp源码注释3处start_property_service 函数具体实现代码

    void start_property_service() {
         property_set("ro.property_service.version", "2");
         // 1. 创建非阻塞的socket
         property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                     0666, 0, 0, NULL);
         if (property_set_fd == -1) {
            PLOG(ERROR) << "start_property_service socket creation failed";
            exit(1);
         }
         // 2. 对property_set_fd 进行监听,第二个参数代表属性服务最多可以同时为8个视图设置属性的用户提供服务
         listen(property_set_fd, 8);
         // 3. 用epoll 监听property_set_fd属性,当property_set_fd属性设置时,init进程将用handle_property_set_fd函数来处理
         register_epoll_handler(property_set_fd, handle_property_set_fd);
     }
    

    在新linux内核中,epoll用来替换selectepoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为内核中的select实现是采用轮询来处理的,轮询的fd数目越多,自然耗时越多
    当属性服务接受到客户端的请求,关注下handle_property_set_fd函数

    static void handle_property_set_fd(){
          ...
          switch (cmd) {
              case PROP_MSG_SETPROP:{
                   char prop_name[PROP_NAME_MAX];
                   char prop_value[PROP_VALUE_MAX];
                   if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
             !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
                          PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
                          return;
                     }
                   prop_name[PROP_NAME_MAX-1] = 0;
                   prop_value[PROP_VALUE_MAX-1] = 0;
                   // 1. 处理属性设置
                   handle_property_set(socket, prop_value, prop_value, true);
              }
          }
          ...
    }
    
    static void handle_property_set(SocketConnection& socket,
                                 const std::string& name,
                                 const std::string& value,
                                 bool legacy_protocol){
          ...
          // 2. 检查权限
          if (check_mac_perms(name, source_ctx, &cr)) {
              // 3. 设置属性
              uint32_t result = property_set(name, value);
              if (!legacy_protocol) {
                 socket.SendUint32(result);
              }
          } else {
              LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
              if (!legacy_protocol) {
                 socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
              }
          }
          ...                                
    }
    
    uint32_t property_set(const std::string& name, const std::string& value){
         size_t valuelen = value.size();
         //1. 校验属性键是否合法
         if (!is_legal_property_name(name)) {
             LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
             return PROP_ERROR_INVALID_NAME;
         }
         //2. 校验值是否合法
         if (valuelen >= PROP_VALUE_MAX) {
             LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                    << "value too long";
             return PROP_ERROR_INVALID_VALUE;
         }
         //3. 加载属性元信息,
         prop_info* pi = (prop_info*) __system_property_find(name.c_str());
         if (pi != nullptr) {
             // ro.* properties are actually "write-once".
            if (android::base::StartsWith(name, "ro.")) {
                // 属性ro.前缀开头,表示只读,不能修改
                LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
                        << "property already set";
                return PROP_ERROR_READ_ONLY_PROPERTY;
            }
            // 更新属性值
            __system_property_update(pi, value.c_str(), valuelen);
         }
         ...
    }
    

    上述源码注释,介绍属性修改的过程

  4. init进程启动过程总结

    init-conclusion.png

    大致做了三件事:

    1. 创建文件目录并挂载设备;
    2. 初始化和启动属性服务;
    3. 解析init.rc 文件,fork zygote进程

zygote 进程启动过程

zygote 译为“孵化器”,是一个进程名字,DVM、应用程序进程以及运行系统关键服务的SystemServer进程都是由它创建并启动,其他应用所在的进程都是zygote进程的子进程

  1. AppRuntime 分析
    从上文得知init启动zygote时主要是调用app_main.cppmain函数中的AppRuntimestart函数来启动zygote服务的:
    framework/cmds/app_process/app_main.cpp

    int main(int argc, char* const argv[]){
        ...
        AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
        ...
        while (i < argc) {
          const char* arg = argv[i++];
          if (strcmp(arg, "--zygote") == 0) {
              zygote = true;
              niceName = ZYGOTE_NICE_NAME;
          } else if (strcmp(arg, "--start-system-server") == 0){
              // 1.启动SystemServer命令
              startSystemServer = true;
          } else if (strcmp(arg, "--application") == 0) {
              application = true;
          } else if (strncmp(arg, "--nice-name=", 12) == 0) {
              niceName.setTo(arg + 12);
          } else if (strncmp(arg, "--", 2) != 0) {
              className.setTo(arg);
              break;
          } else {
              --i;
              break;
          }
        }
        if (zygote) {
           //2. 将args(启动SystemServer命令)作为形参传入
           runtime.start("com.android.internal.os.ZygoteInit", args, zygote); 
        } else if (className) {
           runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
        } 
    }
    
    

    main 函数第二个形参中包含--start-system-server,因为上一节init进程启动分析中提到过init.zygote64.rcInit脚本:

    service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server 
       ... 
    

    其中 --start-system-server 为携带参数,该参数传递到app_main.cpp#main()函数 ,AppRuntime#start启动zygote进程,同时也会将SystemServer进程启动

    #include <android_runtime/AndroidRuntime.h>
    class AppRuntime : public AndroidRuntime{
         ...
    }
    

    AppRuntime 继承AndroidRuntime,即调用AndroidRuntime#start函数
    frameworks/base/core/jni/AndroidRuntime.cpp

    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote){
         ...
         JniInvocation jni_invocation;
         jni_invocation.Init(NULL);
         JNIEnv* env;
         // 1. 开启虚拟机
         if (startVm(&mJavaVM, &env, zygote) != 0) { 
            return;
         }
         onVmCreated(env);
         // 2. 注册android native函数
         if (startReg(env) < 0) {
            ALOGE("Unable to register all android natives\n");
            return;
         }
         
         // 3. 将main函数的 形参options 转成 jobjectArray,作为注释5执行className 类的main函数
         jclass stringClass;
         jobjectArray strArray;
         jstring classNameStr;
         
         ...
         stringClass = env->FindClass("java/lang/String"); 
         strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
         classNameStr = env->NewStringUTF(className);
         for (size_t i = 0; i < options.size(); ++i) {
            jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
            assert(optionsStr != NULL);
            env->SetObjectArrayElement(strArray, i + 1, optionsStr);
         }
         
         char* slashClassName = toSlashClassName(className);
         jclass startClass = env->FindClass(slashClassName);// 反射获取类实例
         ...
         // 4. 找到className 类对应的main函数
         jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
             "([Ljava/lang/String;)V");
         //5. 注释3出生成的strArray 参数数组 传递给className 类的main 函数并执行它  
         env->CallStaticVoidMethod(startClass, startMeth, strArray);    
    }
    

    从源码app_main.cpp 得知,源码中提到的classNamecom.android.internal.os.ZygoteInit ,而ZygoteInit是java编写的,所以需要通过JNI的方式完成调用(c++ ->Java 反射实现)

  2. Zygote的java 框架层

    从上一节得知,zygote通过jni调用ZygoteInit.java
    com.android.internal.os.ZygoteInit

    public static void main(String argv[]) {
         ...
         boolean startSystemServer = false;
         String socketName = "zygote";
         ...
         for (int i = 1; i < argv.length; i++) {
              if ("start-system-server".equals(argv[i])) {
                  // 1. 参数中携带start-system-server
                  startSystemServer = true;
              } else if{...}
         }
         // 2. 注册zygote 用的Socket
         zygoteServer.registerServerSocketFromEnv(socketName);
         ...
         if (startSystemServer) {
              // 3. fork SystemServer进程(用于启动系统关键服务)
              Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
              // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
              // child (system_server) process.
              if (r != null) {
                  //4. 执行SystemServer#run 方法
                  r.run();
                  return;
              }
         } 
    }
    

    注释2出的registerServerSocketFromEnv 用来等待ActivityManagerService来请求Zygote 创建应用程序进程,注释3、4用于fork SystemServer进程,和执行run方法。
    com.android.internal.os.ZygoteServer

    void registerServerSocketFromEnv(String socketName) {
         if (mServerSocket == null) {
             int fileDesc;
             final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
             try {
                 String env = System.getenv(fullSocketName);
                 fileDesc = Integer.parseInt(env);
             } catch (RuntimeException ex) {
                 throw new RuntimeException(fullSocketName + " unset or invalid", ex);
             }
    
             try {
                 FileDescriptor fd = new FileDescriptor();
                 fd.setInt$(fileDesc);
                 // 1.
                 mServerSocket = new LocalServerSocket(fd);
                 mCloseSocketFd = true;
             } catch (IOException ex) {
                 throw new RuntimeException(
                         "Error binding to local socket '" + fileDesc + "'", ex);
             }
         }
     }
    

    注释1创建LocalServerSocket,也就是服务端的Socket, 当Zygote进程将SystemServer进程启动后,就会等待ActivityManagerService请求Zygote进程来启动新的应用程序进程。接着继续看forkSystemServer方法

    private static Runnable forkSystemServer(String abiList, String socketName,ZygoteServer zygoteServer) {
          ...
          //1.
          String args[] = {
             "--setuid=1000",
             "--setgid=1000",
             "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1024,1032,1065,3001,3002,3003,3006,3007,3009,3010",
             "--capabilities=" + capabilities + "," + capabilities,
             "--nice-name=system_server",
             "--runtime-args",
             "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
             "com.android.server.SystemServer",
         };
         ...
         //2.
         pid = Zygote.forkSystemServer(
                     parsedArgs.uid, parsedArgs.gid,
                     parsedArgs.gids,
                     parsedArgs.runtimeFlags,
                     null,
                     parsedArgs.permittedCapabilities,
                     parsedArgs.effectiveCapabilities);
         //3.
         if (pid == 0) {
             if (hasSecondZygote(abiList)) {
                 waitForSecondaryZygote(socketName);
             }
    
             zygoteServer.closeServerSocket();
             //4.
             return handleSystemServerProcess(parsedArgs);
         }            
    }
    

    注释1处用于为启动SystemServer进程参数命令,可以得知启动的类名为com.android.server.SystemServer,注释2处fork SystemServer进程,注释3若pid为0 表示在新创建的子进程中执行,则执行注释4的handleSystemServerProcess 并返回Runnable对象。由于forkSystemServer 方法调用链较长,这里用UML时序图简略表示:

    forkSystemServer-Call-chain

  3. Zygote进程启动流程总结


    zygote-start-analyze.png

SystemServer进程启动过程

SystemServer 也是一个进程,从上文得知,它是zygote fork 出来的。ActivityMangerService PackageManagerService WindowManagerService 等这些重要的服务都是通过SystemServer进程启动的

  1. 启动服务过程
    结合上一节Zygote.forkSystemServer 方法 绘制的UML时序图,最终通过反射的方式调用SystemServer#main方法
    com.android.server.SystemServer

    public static void main(String[] args) {
         // 1. 创建`SystemServer`实例并执行`run`方法,注意,它并不是`Runnable`对象
         new SystemServer().run();
    }
    ...
    private void run() {
         ...
         //2.注释2用于初始化系统配置(时区、语言...)
         SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
         SystemProperties.set("persist.sys.timezone", "GMT");
         SystemProperties.set("persist.sys.language", ""); 
         ...
         // 3. 设置进程优先级并创建主线程`Looper`
         android.os.Process.setThreadPriority(
         android.os.Process.THREAD_PRIORITY_FOREGROUND);
         android.os.Process.setCanSelfBackground(false);
         Looper.prepareMainLooper();
         Looper.getMainLooper().setSlowLogThresholdMs(
                     SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS); 
         // 4. 加载了`libandroid_servers.so`             
         // Initialize native services.
         System.loadLibrary("android_servers");            
         ...
         // 5. 创建系统上下文
         createSystemContext();
         ...
         // 6. 创建SystemServiceManager:创建系统服务、管理服务生命周期,存取系统服务
         // Create the system service manager.
         mSystemServiceManager = new SystemServiceManager(mSystemContext);
         mSystemServiceManager.setStartInfo(mRuntimeRestart,
                     mRuntimeStartElapsedTime, mRuntimeStartUptime);
         LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
         // 为可以并行化的任务准备线程池
         SystemServerInitThreadPool.get(); 
         
         try {
             // 7. 启动核心关键服务
             startBootstrapServices();
             startCoreServices();
             startOtherServices();
             SystemServerInitThreadPool.shutdown();
         } catch (Throwable ex) {
             ...
         }
         
         ...
         // 8. 
         Looper.loop();
    }
    

    注释5创建系统上下文:

    private void createSystemContext() {
         ActivityThread activityThread = ActivityThread.systemMain();
         mSystemContext = activityThread.getSystemContext();
         mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);
    
         final Context systemUiContext = activityThread.getSystemUiContext();
         systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
     }
    

    得知systemContext是从ActivityThread#getSystemContext ;并设置了系统主题。

    重点关注注释7:启动系统服务,官方把系统服务分成三种类型引导服务、和兴服务、其他服务,这里简单列举一下:

    start-service.png

大致统计了下SystemServer 在启动过程中会启动96个左右的服务

查看调用启动服务源码 e.g
SystemServer#startBootstrapServices

mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);

启动服务是委托给com.android.server.SystemServiceManager

public SystemService startService(String className) {
     ...
     // 1. 反射构造服务实例
     Constructor<T> constructor = serviceClass.getConstructor(Context.class);
     service = constructor.newInstance(mContext);
     ...
     // Register it.
     //2. 添加到动态数组中
     mServices.add(service);
     try {
         //3. 执行服务的onStart方法
         service.onStart();
     } catch (RuntimeException ex) {
         throw new RuntimeException("Failed to start service " + service.getClass().getName()
                 + ": onStart threw an exception", ex);
     }
}

以上代码表述了一个服务启动的过程。其中SystemServerManager用来管理(生死存亡、开启|关闭服务)系统各种服务,这对于后期学习系统C/S架构中的Binder机制通信有极大的作用。

  1. 总结


    SystemServer-start-analyze.png

Launcher启动过程过程

作为Android系统启动流程的最后一步:Home应用程序启动,Home也即Launcher。应用程序在启动过程中会请求PMS 返回系统中已经安装的应用程序信息,并将这些信息转换成一个快捷启动图标显示在桌面上,这样用户就可以点击快捷图标启动程序了。源码地址

  1. Launcher 程序是如何启动的?
    由上一节得知SystemServer进程会启动AMS PMS 等服务。其中Launcher 程序是通过AMS 启动的
    frameworks/base/services/java/com/android/server/SystemServer.java

    private void startOtherServices() {
       mActivityManagerService.systemReady(() -> {
          ... 
          // MakeXXXServiceReady
       }
    }
    

    调用了AMS systemReady方法

    ...
    String mTopAction = Intent.ACTION_MAIN;
    ...
    public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
         ...
         // Start up initial activity.
         mBooting = true;
         // Enable home activity for system user, so that the system can always boot. We don't
         // do this when the system user is not setup since the setup wizard should be the one
         // to handle home activity in this case.
         // 1.  启动HomeActivity
         startHomeActivityLocked(currentUserId, "systemReady"); 
       
    }
    
    boolean startHomeActivityLocked(int userId, String reason){
         if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
                 && mTopAction == null) {
             // We are running in factory test mode, but unable to find
             // the factory test app, so just sit around displaying the
             // error message and don't try to start anything.
             return false;
         }
         // 2. 创建Home程序的启动Activity Intent
         Intent intent = getHomeIntent();
         ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
         if (aInfo != null) {
             intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
             // Don't do this if the home app is currently being
             // instrumented.
             aInfo = new ActivityInfo(aInfo);
             aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
             ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                     aInfo.applicationInfo.uid, true);
             if (app == null || app.instr == null) {
                 //3. 设置启动模式为FLAG_ACTIVITY_NEW_TASK
                 intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
                 final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
                 // For ANR debugging to verify if the user activity is the one that actually
                 // launched.
                 final String myReason = reason + ":" + userId + ":" + resolvedUserId;
                 // 4. 启动Activity
                 mActivityStartController.startHomeActivity(intent, aInfo, myReason);
             }
         } else {
             Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
         }
    
         return true;
    }
    
    Intent getHomeIntent() {
         Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
         intent.setComponent(mTopComponent);
         intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
         if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
             // 2.1 设置Category为CATEGORY_HOME
             intent.addCategory(Intent.CATEGORY_HOME);
         }
         return intent;
     }
    

    要启动的actionIntent.ACTION_MAINcategoryIntent.CATEGORY_HOME。 Launcher应用程序Manifest

    <application
        android:name="LauncherApplication"
        android:process="android.process.acore"
        android:label="@string/application_name"
        android:icon="@drawable/ic_launcher_home">
        <activity
             android:name="Launcher"
             android:launchMode="singleTask"
             android:clearTaskOnLaunch="true"
             android:stateNotNeeded="true"
             android:theme="@style/Theme"
             android:screenOrientation="nosensor"
             android:windowSoftInputMode="stateUnspecified|adjustPan">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.HOME"/>
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.MONKEY" />
             </intent-filter>
         </activity>
    </application>    
    

    这样 桌面应用程序Launcher Activity#onCreate 就会执行

    Launcher-start.png

  1. Launcher中应用图标启动分析

    紧接着分析下点击应用图标到底发生了什么?
    Launcher.java

    public final class Launcher extends Activity implements View.OnClickListener, OnLongClickListener{
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ...
            // 1. 
            setContentView(R.layout.launcher);
            ...   
        }
    }
    

    launcher.xml

    <com.android.launcher.DragLayer
       xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
       android:id="@+id/drag_layer"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent">  
    
       <!-- The workspace contains 3 screens of cells -->
       <com.android.launcher.Workspace
           android:id="@+id/workspace"
           android:layout_width="fill_parent"
           android:layout_height="fill_parent"
           launcher:defaultScreen="1">
           <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
           <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
           <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
       </com.android.launcher.Workspace>
       
       ...
       
       <com.android.launcher.DeleteZone
         android:id="@+id/delete_zone"
         android:layout_width="wrap_content"
         android:layout_height="49dip"
         android:scaleType="center"
         android:src="@drawable/ic_delete"
         android:background="@drawable/delete_zone_selector"
         android:layout_gravity="bottom|center_horizontal"
         android:visibility="invisible"
         
         launcher:direction="horizontal" />
    </com.android.launcher.DragLayer>   
    

    重点关注cell,每一个cell对应一个application
    workspace_screen.xml

    <com.android.launcher.CellLayout
       xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       launcher:cellWidth="80dip"
       launcher:cellHeight="100dip"
       launcher:longAxisStartPadding="0dip"
       launcher:longAxisEndPadding="55dip"
       launcher:shortAxisStartPadding="0dip"
       launcher:shortAxisEndPadding="0dip"
       launcher:shortAxisCells="4"
       launcher:longAxisCells="4" />
    

    CellLayout 是一个自定义View,继承ViewGroup,父控件为Workspace。回到Launcher.java:

    private static class DesktopBinder extends Handler implements MessageQueue.IdleHandler{
          ...
          @Override
          public void handleMessage(Message msg) {
               Launcher launcher = mLauncher.get();
               if (launcher == null || mTerminate) {
                   return;
               }
               switch (msg.what) {
                 case MESSAGE_BIND_ITEMS: {
                     // 1.执行了bindAppWidgets
                     launcher.bindItems(this, mShortcuts, msg.arg1, msg.arg2);
                     break;
                 }
                 case MESSAGE_BIND_DRAWER: {
                     launcher.bindDrawer(this, mDrawerAdapter);
                     break;
                 }
                 case MESSAGE_BIND_APPWIDGETS: {
             
                     launcher.bindAppWidgets(this, mAppWidgets);
                     break;
                 }
             }
          }
    }
    private void bindItems(Launcher.DesktopBinder binder,
             ArrayList<ItemInfo> shortcuts, int start, int count){
         final Workspace workspace = mWorkspace;
         ...
         for ( ; i < end; i++) {
              final ItemInfo item = shortcuts.get(i);
              switch (item.itemType) {
                  case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                  case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                     // 2. 创建快捷方式View 并添加到 workspace子控件的cellLayout中
                     final View shortcut = createShortcut((ApplicationInfo) item);
                     workspace.addInScreen(shortcut, item.screen, item.cellX, item.cellY, 1, 1,
                             !desktopLocked);
                     break;
              }
         }
    }
    
    

    当从PMS获取到已经安装的应用程序后,将这些appinfo 绑定到Launcher的界面中。注释2调用createShortcut

    View createShortcut(int layoutResId, ViewGroup parent, ApplicationInfo info) {
         TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);
         if (!info.filtered) {
             info.icon = Utilities.createIconThumbnail(info.icon, this);
             info.filtered = true;
         }
         favorite.setCompoundDrawablesWithIntrinsicBounds(null, info.icon, null, null);
         favorite.setText(info.title);
         favorite.setTag(info);
         // 1. 
         favorite.setOnClickListener(this);
         return favorite;
     }
    

    注释1 很关键,快捷方式的点击事件委托给LauncherActivity ,接着看Launcher#onClick方法

    /**
      * Launches the intent referred by the clicked shortcut.
      *
      * @param v The view representing the clicked shortcut.
      */
     public void onClick(View v) {
         Object tag = v.getTag();
         if (tag instanceof ApplicationInfo) {
             // Open shortcut
             final Intent intent = ((ApplicationInfo) tag).intent;
             //1. 
             startActivitySafely(intent);
         } else if (tag instanceof FolderInfo) {
             handleFolderClick((FolderInfo) tag);
         }
     }
    void startActivitySafely(Intent intent) {
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         try {
             // 2. 
             startActivity(intent);
         } catch (ActivityNotFoundException e) {
             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
         } catch (SecurityException e) {
             ...
         }
     } 
    

    注释1 & 2 调用了startActivitySafely也即调用了startActivity , 这样目标程序就会被启动。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容

  • 做梦梦见一位故人,于是开始心烦意乱起来,人果然是骗不了自己的心,你喜欢什么,就算埋藏的再深,它也会在某一时刻突然跳...
    西瓜瓤和地瓜子阅读 464评论 0 1
  • 你的潜意识指引着你的人生,而你称其为命运。——荣格 我们做任何选择都是为了追求好处。
    张小米_2580阅读 182评论 0 0
  • 古有采诗之官
    MarcieLamm阅读 324评论 0 0
  • 接过,风雨的打赏 芳香,漫过星辰 停下,追逐的脚步 眉梢,锁定日月 把过往 ,装进抽屉 上锁心慰,任 秒摆轻唱 织...
    草根如玉阅读 169评论 0 1