【FrameWork】②四大组件相关

Activity相关

1. Activity启动流程

考察点

  • 启动Activity会经历哪些生命周期回调
  • 冷启动大致流程,涉及哪些组件,通信过程是怎么样的?
  • Activity启动过程中,生命周期回调的原理?

答题要点

  • 启动Activity要向AMS发起binder调用 60分
  • Activity所在进程是怎么启动的?80分
  • 应用Activity的生命周期回调原理 100分

整体流程,在应用端Activity启动的步骤

  • 首先通过ClassLoader加载apk中的Activity类,生成Activity对象
  • 然后准备好Applicaiton,实际上返回的就是应用进程启动时已经创建好的Applicaiton
  • 创建Context,具体实现为ContextImpl
  • 附加上下文,不仅仅包括context,还包括所有跟Activity运行有关的重要的系统变量
  • 最后执行生命周期回调

总结:

  • 首先应用向AMS发起startActivity请求,
  • 如果应用没有启动,AMS会向Zygote发起启动进程请求
  • zygote收到请求之后就会启动应用进程
  • 应用进程启动之后,就会向AMS发起attchApplication的IPC调用,主要使用与注册Application Thread
  • 接下来AMS会向应用发起bindApplication IPC调用,用来给应用初始化Application
  • 之后AMS又向应用发起scheduleLaunchActivity IPC调用,这个调用就是给应用创建和加载Activity并且执行Activity生命周期

2. Activity显示原理

相关问题

  • Activity的显示原理(Window/DecorView/ViewRoot)
  • Activity的U刷新机制(Vsync/Choreographer)
  • U|的绘制原理(Measure/Layout/Draw)
  • Surface原理(Surface/SurfaceFlinger)

答题要点

  • PhoneWindow是什么,怎么创建的?
  • setContentView原理是什么?
  • Activity在onResume之后才会显示的原因是什么?
  • ViewRoot是干嘛的,是View Tree的rootView么?
  • View的显示原理是什么?WMS发挥了什么作用?

总结
代码细节如图32

  • Activity启动时会创建一个PhoneWindow
  • phoneWindow里有一个DecorView,DecorView是整个Activity的View Tree的Root view
  • ContentView是DecorView的一部分
  • Decorview会对应一个ViewRootImpl对象,ViewRootImpl对象可以与WMS双向通信
    • ViewRootImpl可以通过IwindowSession向WMS发起binder调用
    • WMS通过IWindow向应用端发起binder调用
  • Activity之所以能显示出来,最重要的一步就是ViewRootImpl的创建,并由ViewRootImpl全权负责Decorview的绘制
  • ViewRootImpl会在WMS里注册一个窗口,WMS会统一管理所有窗口的大小,位置以及层级,在第一次绘制的时候,ViewRootImpl会向WMS申请一个Service用于绘制
  • 绘制完成之后,SurfaceFlinger就会按照WMS中提供的Window层级、大小、位置来对surface进行合成,合成完毕之后就可以写到屏幕的帧缓冲区显示出来
  • 如图33

3. 应用的UI线程是怎么启动的

答题要点

  • 什么是UI线程?
    • UI线程也就是刷新UI所在的线程
    • UI是单线程刷新的(如果是多线程导出上锁容易导致问题)
  • UI线程的启动流程,消息循环是怎么创建的
  • 了解Android的UI显示原理,UI线程和U|体系之间是怎么关联的?

Activity.runOnUiThread(Runnable)与view.post(Runnable)

  • Activity.runOnUiThread(Runnable)

    • 结论1:对于Activity来说,UI线程就是主线程
    • 图34
  • view.post(Runnable)

    • 结论2:对View来说,它的U线程就是ViewRootlmpl创建的时候所在的线程!
    • 图35
  • 子线程刷新UI的问题

    • 结论:只有创建了view树的线程,才能访问它的子view
    • 如图36image
  • 结论3:Activity的DecorVie对应的ViewRootlmpl是在主线程创建的

    • 如图37

关于UI线程的三个结论

  • 对Activity来说,UI线程就是主线程
    • activity.runOnUiThread()
  • 对View来说,UI线程就是ViewRootlmpl创建的时候所在的线程
    • View.post(Runnable r)
  • Activity的DecorView对应的ViewRootlmpl是在主线程创建的
    • checkThread

子线程刷新UI可以么?
可以,代码如下

new Thread(){
    @Override
    public void run(){
        //因为添加window是IPC操作,回调回来时,需要handler切换线程,所以需要Looper
        Looper.prepare();
        ...
        getWindowManager().addView(view,params);
        //开启looper,循环取消息。
        Looper.loop()
    }
}.start();

Looper.prepare()与Looper.prepareMainLooper()的区别

  • Looper.prepareMainLooper(),应用进程启动之后MainLooper就已经设置好了
  • prepare(false)表示looper不能退出,对于mainLooper来说looper是不能退出的,其他线程是可以退出的,调用主线程的looper.quit是会抛出异常的

总结

  • UI线程指的是刷新UI的线程,由于ViewRootImpl是在主线程中创建的,所以实际上指的就是主线程
  • UI线程的启动实际上就是主线程的启动,分三步
    • ①Zygote fork进程
    • ②启动binder线程
    • ③ 执行入口函数 ActivityThread.main()
  • 消息循环时通过主线程的Looper开启的

Service相关

1. 说说Service的启动原理

考察点

  • service启动有哪几种方式?
    • startService
    • bindService
    • 区别:bindServcie不会触发onStartCommand
  • service启动过程中主要流程有哪些?
  • service启动过程涉及哪些参与者,通信过程是怎样的?

代码分析

主要的启动流程

  • 如果Service启动了,直接调用onStartCommand
  • 如果Service没有启动
    • 进程启动了
      • 启动Service
      • 调用onStartCommand
    • 进程没有启动
      • 启动进程
      • 启动Service
      • 调用onStartCommand

启动过程涉及哪些参与者,以及通信过程

  • 进程A要开启一个Service,向AMS发起了startService调用

  • 如果AMS发现Service进程没有启动,就会通过socket向zygote进程发起启动进程的请求

  • zygote进程会启动应用进程

  • 应用进程启动之后,就会执行ActivityThread main函数

  • 在入口函数中向AMS发起attchApplication的binder调用,AMS就知道了应用进程已经就绪了

  • 然后AMS会向应用发起bindApplication的binder调用,让应用创建自己的Application

  • 接下来就开始处理这个应用进程里pending的一些应用组件,比如Service相关,这里连发了两条请求,这两个请求都是在主线程中执行的

  • scheduleCreateServices用于在应用端create Service

  • cheduleServiceArgs用于在应用端调用onStartCommand

  • 如图41

总结

  • 首先回答出启动方式,答案如上
  • 然后回答出启动过程中的主要流程
  • 最后回答出service启动过程涉及哪些参与者,通信过程是怎样的?

2.说说Service的绑定原理

考察点

  • bindService用法
  • 了解bindServcie的大致流程
  • bindService涉及哪些参与者,通信过程是怎样的?

用法: 图43

总体流程

  • 首先应用向AMS发起bindService调用
  • AMS检查是否有binder句柄
  • 如果有,就会将binder句柄回调给应用
  • 如果没有,AMS就会向Service请求句柄,Service会将句柄发布到AMS,然后AMS会将binder句柄回调给应用
  • 应用在拿到binder句柄之后,就可以向Service发起binder调用了

  • 应用端bindService的时候会带一个IserviceConnection对象
  • 这个对象是一个Bindder对象,把这个对象传到AMS之后,AMS就会将它保存起来
  • 等到Service绑定成功了,AMS就会调用IserviceConnection的connected函数来主动通知应用端
  • 然后IServiceConnection持有了ServiceConnection的引用,这意味着可以调用ServiceConnection的函数
  • 值得注意的是,IServiceConnection和ServiceConnection不一定是一对一的关系
    • 一个context和一个ServiceConnection构成一个二元组,这个二元组跟IserviceConnection是一对一的关系
    • 这意味着同一个ServiceConnection用不同的Context去bind这个Service的时候,就会对应不同的IserviceConnection
    • 同样,同一个Context用不同的ServiceConnection去bind这个Service的话,那么也会对应不同的IServiceConnection
    • 对于AMS来说,他只认IServiceConnection,只要IServiceConnection不同,他就认为这是不同的Connection

bindService实现原理 bindService从应用端发到了AMS, 在AMS中处理函数就是bindServiceLocked

首先来看一组数据结构

  • 应用端一个Service在AMS中对应一个ServiceRecord
  • 一个ServiceRecord又可以包含一个或多个IntentBindRecord
  • 一个IntentBindRecord又可以包含一个或多个AppBindRecord
  • 一个AppBindRecord又可以包含一个或多个ConnectionRecord

下面看具体流程

  • 启动Service会最终调用到AMS中的bringUpServiceLocked函数,之后的流程如下图
  • 图46

onRebind什么时候调用

  • 图47

unbindService实现

  • 图48

3. 说说动态广播的注册和收发原理

考察点

  • 动态广播的注册原理
  • 广播的发送原理
  • 广播的接收原理

动态广播的注册原理

  • 在应用端

    • 图49
  • 在AMS端

    • AMS收到了应用端注册广播的请求
    • 图50

广播的发送

  • 图51

广播的接收

  • 图52image

  • AMS要给intent分发到应用的5个receiver,普通的动态广播会并行分发出去,但是广播到应用进程之后,分发就是同步的,即串行处理

    • 如图53
  • 下面来看IIntentReceiver的performReceive实现

    • 如图54

总结

  • 首先应用A向AMS注册广播,生成一个binder对象,将binder和intent filter对象注册到AMS
  • 应用B发送一个广播,广播中携带一个intent
  • AMS就会在所有注册的Receiver中根据intent找到匹配的Receiver,然后开始分发
  • 对于普通的动态广播来说,AMS是并行分发,到了应用端是串行分发的
  • 通过binder对象找到对应的broadcast Receiver,然后执行它的receive函数
  • 如图55

简单总结下就是:

  • 注册广播封装了一个binder对象到AMS
  • 通过广播intent找到匹配的receiver,然后分发
  • 普通动态广播在系统端(AMS)是并行分发,应用端串行分发

4. 说说静态广播的注册和收发原理

广播的注册
与动态广播不同,静态广播是在清单文件中注册,安卓在启动的时候,就会启动PMS服务,这个服务会扫描已经安装的apk,解析mainfest文件,当检测到receiver时,会把它加到receiver列表中,静态广播就是在这个时候注册到了PMS中,当需要的时候,会从PMS中查询,如下图, 图56

广播的发送 静态广播是串行分发的,如果进程没有启动,需要启动进程,如果分发超时了,这个广播就废弃了 图57

广播的接收 分发到应用之后,应用会在主线程创建一个广播对象,执行onReceive,但是没有创建上下文,onReceive中的context不是application的context,而是以application为mBase的contextWrapper 图58

5. 说说Provider的启动原理

考察点

  • 了解ContentProvider的生命周期
  • 熟悉ContentProvider的启动流程
  • 熟悉Provider启动过程中各方通信原理

  • 先看上面的图,三个进程,应用进程,AMS进程 Provider进程
  • 应用用Provider进行增删改查,就需要先得到Provider的binder对象
    • Provider启动之后会先把binder发布到AMS
    • 应用向AMS请求时,AMS就把Provider的binder返回给应用
  • 在看下面的图,没有使用到Provider的binder对象,而是直接在应用进程里创建了一个Provider实例,这样应用使用Provider进行增删改查时就不需要进行跨进程通信了
  • 那么该如何在应用进程中创建一个Provider实例呢,首先需要Provider所在应用的uid和调用者的uid相同,另外需要满足下面两个条件之一
    • Provider的进程名称和调用者的进程名称相同
    • 或者Provider指定了multiProcess标志

总结

先说Provider进程没启动的情况如图60

  • 首先应用A向Provider发起增删改查的调用,由于应用A和Provider不是一个进程,所以需要binder跨进程通信,那么就需要拿到需要请求AMS的binder对象
  • 应用A向AMS请求Provider的binder对象
  • AMS发现应用进程没有启动,就会通过socket通信向zygote发起创建Provider进程请求,然后zygote创建出Provider进程
  • Provider启动的第一件事就是调用attachApplication向AMS报告,AMS就知道进程已经就绪了
  • AMS调用bindApplication命令下到Provider,bindApplication有两个重要工作,一个是创建Application对象,另外一个就是把provider全部初始化一遍(安装provider,创建provider对象,调用生命周期)
  • 然后Provider调用publishContentProvider把binder对象发布到AMS
  • 现在AMS有了Provider的binder对象,就会把binder对象返回给应用A
  • 于是应用端就可以向Provider发起增删改查的调用了

再来讨论Provider进程已经启动的情况 如图61

  • 应用A向Provider发起增删改查调用
  • 应用A向AMS请求Provider的binder对象
  • AMS中如果没有binder对象,AMS就调用schedulelnstallProvider向Provider进程请求binder对象
  • 然后Provider就会安装及初始化Provider,调用生命周期,最后通过publishContentProvider将binder对象发布给AMS
  • 现在AMS有了Provider的binder对象,就会把binder对象返回给应用A
  • 于是应用端就可以向Provider发起增删改查的调用了

大家如果还想了解更多Android 相关的更多知识点,可以点进我的GitHub项目中:https://github.com/733gh/GH-Android-Review-master自行查看,里面记录了许多的Android 知识点。最后还请大家点点赞支持下!!!

猜你喜欢

转载自blog.csdn.net/dongrimaomaoyu/article/details/114592527