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进程中启动的核心系统服务示意图
Launcher进程启动过程
1.Launcher进程启动流程示意图
- AMS启动完成后,在systemReady()中会去调用startHomeOnAllDisplays()来启动Launcher应用。Launcher进程启动流程图。
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。
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注册自己,同时传递
IApplicationThread
Binder对象以便于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>