Android Framework 面试集合——Framework底层服务

AMS(ActivityManagerService) 在SystemServer的进程中,是SystemServer中的一个对象;

作用:

  • 管理activity的生命周期

  • 启动activity

  • 与PMS进行交互

    Activity->AMS:

  • 调用activity.startActivity()

  • 通过ActivityManage.getService("activity")得到AMS的BpBinder;

  • 通过BpBinder发送请求,调用AMS的startActivity()

    AMS->PMS:

  • AMS和PMS都在SystemServer进程中,都是SystemServer中一个对象

  • 通过包名和PMS里的缓存mPackage查询到App对应的Package

  • 使用activity的类名通过PMS里的内部类PackageManagerInternalImpl查询到activity对应的包装类ResolveInfo; ps:ResolveInfo这个javabean里有activityInfo、ServiceInfo等变量,查询啥就给哪个变量赋值,再返回ResolveInfo;

  • 得到ResolveInfo里的activityInfo;

  • activityInfo返回给App进程的ActivityThread;`

  • ActivityThread中发送事件

  • ActivityThread中的Handler对象mH收到159事件,处理

  • 通过反射创建Activity对象

  • 将Activity对象放到activtes启动记录中

ActivityThread

  • 每个应用有一个ActivityThread;是应用的入口;
  • 在APP进程中
  • 是AMS的缓存中心
  • ActivityThread中的List<ActivityRecord> activtes放了activity的启动记录

ActivityThread中重要的对象:

  • ApplicationThread:AMS回调给ActivityThread数据的桥梁
  • mInstrumentation:管理Application和Activity的生命周期(及创建)
  • mH:Handler,处理ApplicationThread里各种回调函数发送的各种消息

点击桌面App图标发生了什么?

  1. 点击的APP图标是在单独的Luancher进程,是一个系统App进程
  2. Luancher进程请求SystemServer进程中的AMS去创建应用的根Activity(AndroidMnifest.xml中initen-fifter为Luanche的activity)
  3. AMS通过包名让PMS查询到相关应用信息,得到应用的Package;
  4. AMS创建activity栈,根据Package拿到根activity的配置节点信息,放到栈中,此时栈中只有一个根activity的配置节点信息,也就是在栈顶;(此处的栈不是应用层的栈,这个栈只是用来放activity节点信息的)
  5. AMS请求zygote进程创建App进程;zygote进程比较特殊, 使用Socket通信,而不是binder;zygote是所有应用的孵化器,zygote进程挂掉时,手机会自动重启;
  6. zygote进程去fork出App进程;
  7. APP进程中的主线程调用ActivityThread.main()静态函数,main中创建 ActivityThread对象
  8. 接着在ActivityThread.attch()中创建了一个ApplicationThread对象,作为和AMS通信时,返回结果的桥梁;
  9. App进程通过AMS的binder调用attachApplication(thread)请求AMS获取应用对应的Applaction和栈顶中activity节点信息(步骤4),此时给AMS传过去了一个thread,这个thread就是ApplicationThread
  10. AMS将从PMS查到的application节点数据序列化后,调用thread.bindApplaction(data数据...)传给ActivityThread; (此时代码还会继续往下执行,去获取栈顶activity的节点信息)
  11. ActivityThread调用sendMessage发送消息BIND_APPLICATION(110)给Handler,Handler调用handleBindApplication(data)
  12. 通过反射实例化Instrumentation对象:负责application和activity的生命周期的管理
  13. 通过Instrumentation对象反射实例化new Applaction对象app
  14. 调用Instrumentation.callApplactionOnCreate(app)
  15. 执行Applaction.onCreate()
  16. 步骤10中AMS继续向下执行查找activity,AMS将查到的栈顶根Activity(LaunchActivity )信息封装到一个事务ClientTransaction中,提交事务并执行,在执行中,调用thread.scheduleTransaction(事务数据);(thread为ActivityThread中的ApplicationThread
  17. ApplicationThread回调scheduleTransaction函数中,发送`EXECUTE_TRANSACTION(159)消息
  18. Handler处理EXECUTE_TRANSACTION消息,从事务数据中取出LaunchActivity信息,并调用hanldeLaunchActivity(activity数据)
  19. 通过Instrumentation对象反射实例化newActivity()出对象activity
  20. 执行activity.attach(),在attach中创建WMS的桥接代理类;(绘制流程会用到)
  21. 通过Instrumentation调用callActivityOnCreate(activity)
  22. 执行Activty.onCreate();
  23. 至此启动页根Activity启动完成;

下图中4-5中少了上面7-23的步骤:

7-15创建并启动了Application;

16-22创建并启动了Activity;

应用内activity与activity的跳转是跨进程通信,还是同一个进程内通信?

是跨进程通信;

跳转流程参考上面的:省去了application的创建过程;

步骤3 +步骤16-23;

2.Android Framework源码-PMS

SystemServer: Android一切服务的启动者;

  1. 开机时,板子引导芯片启动引导程序
  2. 引导程序启动PID为0的linux内核进程
  3. linux系统启动init脚本,启动PID永远为1的init进程
  4. init进程启动SystemManager进程;
  5. SystemManager进程启动完后;
  6. init进程启动zygote进程(native进程)
  7. zygote调用SystemServer.java的main函数,frok出SystemServer进程(java进程)
  8. SystemServer.java的main函数里执行SystemServer的run方法,main函数里只有一句代码:new SystemServer().run();
  9. run方法中启动服务进程,AMS、PMS等

ps:SystemManager: 是SystemServer的叔叔,SystemServer把所有服务都交给了SystemManager管理;

  1. AMS、PMS自身创建后,自身对象会添加到SystemManager中,addService("key",AMS/PMS)
  2. getService()时,取的是个binder;
    PMS(PackageManagerService): 在SystemServer的进程中,是SystemServer中的一个对象;

有一个缓存中心:mPackages;是一个Map,key为应用的包名,value为每个应用的Package;

在手机启动的时候,做了三件事,且只做一次:

  1. 遍历所有app文件
  2. 解压每个apk文件
  3. dom解析AndroidMnifest.xml,并缓存;

作用:只解析每个Apk中的AndroidMnifest.xml中的信息,而不是去解析节点中每个xxxActivity.java文件;解析到的信息缓存到mPackages中,相当于“注册表”,方便之后AMS快速定位到相应的APP;

  1. PackageManagerService.java中会去两个目录做扫描scanDirTracedLI:用户安装的所有APP目录sAppInstallDir:data/app/;和系统应用所有APP的目录systemAppDir:System/app/
  2. 6.0-8.0都是单线程扫描,9.0和10.0是用线程池进行扫描,扫描到的apk文件信息,new PackageParse(),赋值给包解析工具类PackageParse;
  3. 解压Apk文件,9.0和10.0解析时会去判断缓存中是否有,有则用缓存,6.0-8.0没有使用缓存;
  4. 使用工具类PackageParse解析AndroidMnifest.xml,xml解析完会返回Package对象,每个APK对应一个Package对象,得到这个Package对象后,缓存到PackageManagerService的mPackages这个ArrayMap里;key为应用的包名,value为应用的Package;
  5. Package对象中有解析出的对应App中的四大组件标签、权限标签等等,放入各自的List中,如:activites、services、revicers、providers、权限list等等;activites这些list存的只是一个javabean,而不是存的具体的应用层的Activity;

解析AndroidMnifest.xml流程:

  1. 打开AndroidMnifest.xml
  2. 获取版本号、版本名称
  3. 判断tagname=="applacation"
  4. 判断tagname=="activity","reciver","service","provide"等等
  5. 走到对应的解析parseActivity,parseActivity(reciver和activity的结构一样,就用同样的javabean接收),parseService,parseProvide
  6. 解析完添加到Package的对应的list中;

3.Android Framework源码-IMS

Linux事件机制:

事件都是储存在文件中;

如触摸屏幕事件:存储在dev/input/event0的文件中,每次触摸都会以 16进制 数据储存;

INotify:监听文件状态,有变化则产生FD值

epoll机制:

epoll_create:注册监听事件类型

epoll_ctl:监听FD值,FD改变则唤醒epoll_wait()

epoll_wait:没事件则阻塞,有事件则分发;

将INotify和epoll封装为一个对象EventHub;

SystemServer进程启动时,创建了InputManagerService服务,这个IMS在native层创建了InputManager对象;

InputManager里有一个对象EventHub;

同时InputManager里面又开启了两个线程:

InputReaderThread:不断去读取EventHub里的事件;有事件时把数据封装后添加到队列,立马从队列里读取交给InputDispatcher进行分发;

InputDispatcherThread:InputDispatcher里面保存了wms中所有的window信息(wms会将window信息实时更新到InputDispatcher中),InputDispatcher就可以将事件分发给对应合适的window;

App进程中的ViewRootImpl和SystemServer中的IMS通过socketpair通信,因为事件产生的非常快且非常多使用binder通信不适合

ViewRootImpl中setView后new了一个监听FD文件的回调,new WindowInputEventReceiver();

在回调中,底层使用epoll_ctl()函数监听FD是否变化,有变化则会回调至java层,dispatchInputEvent();

这里就是Activity-》Viewgroup->View的事件分发前置;

底层事件信号传递总结:

  1. 事件信号都是用物理文件存储数据的,位置在dev/input 文件夹下;touch事件存储在dev/input/event0的文件中;
  2. Linux有提供相关的文件监控api: inotify()和epoll机制
  3. android创建了一个封装了 inotify()和epoll机制的对象EventHub,来监控dev/input文件夹下面的事件信号文件;
  4. android自己启动两个线程来处理dev/input文件夹下面的事件信号文件:InputReaderThreadInputDispatherThread;`
  5. InputReaderThread中开启循环,对EventHub对象进行getEvent();
  6. getEvent()中有epoll_wait;相当于wait-notif机制;唤醒的触发点时dev/input下的文件被改变;
  7. InputReaderThread将dev/input文件夹下面的事件信号文件数据进行 提取、封装,然后交给InputDispatherThread;
  8. InputDispatherThread最终选择到对应的ViewRootImpl(window)进行分发数据;
  9. 这里App进程和SystemServer两个进程通过Socketpair进行通信;两个进程一边一组socketpair;
  10. ViewRootImpl中对于Channel连接的文件进行监控(epoll_ctr),从而是上层接收到touch信号;

4.Android WMS及绘制流程

主角:ViewRootImpl、Choreographer、Surfaceflinfer

WMS扮演了什么角色?

作为协调者,协调view布局,绘制;

  1. 在ActivityThread中创建Actiivty后,调用activity.attach()时,创建一个窗体对象PhoneWindow
  2. PhoneWindow创建了一个WMS的代理桥接类WindowManagerImpl对象,作为WMS在app中的代表;
  3. WindowManagerImpl对象中的(mGlobal)WindowManagerGlobal专门和WMS通信,在mGlobal里面获取了到了WMS的Binder:getWindowSession()->WMS::openSession();

setContentView()

  1. 调用PhoneWindow.setContentView(resouseID)
  2. PhoneWindow中:创建mDector:窗体上的整个View:里面有官方的主题布局+用户自己的布局;
  3. PhoneWindow中:创建mContentParent:官方主题布局中提供给用户装载布局的容器:id为content;
  4. 调用mLayoutInflater.inflater(resouseID,mContentParent):
  5. 解析用户的布局xml
  6. 递归调用:解析根布局,通过反射创建根布局;解析子view,通过反射创建view;
  7. 最后PhoneWindow中的mContentParent加载用户的根布局;
  8. 提交view数据

ps:这里递归调用,若嵌套层级太多,会导致栈溢出;因为递归调用不会释放栈;

ViewRootImpl 单例,管理所有View的绘制策略;

注意onCreate.setContentView后view数据已解析并实例化了;

  1. 在状态机为Resume时:
  2. 调用WindowManagerImpl中的mGlobal.addView(view)
  3. addView中创建ViewRootImpl root=new ViewRootImpl()
  4. root.setView(view);
  5. 在setView总调用requestLayout()
  6. requestLayout()请求绘制,编舞者出场

帧速率: CPU/GPU出图速率;

刷新率: 屏幕刷新速率;

  1. 帧速率>刷新率时,出现丢帧(出图好多张了,但是只显示了开头和结尾两张,中间的丢了)
  2. 帧速率<刷新率,出现卡顿(屏幕刷新好多次了,但是还是显示的第一帧)

Vsync: 垂直同步绘制信号; 因可能硬件帧速率和刷新率不一致,用来同步刷新的问题;

Choreographer编舞者: 负责管理帧率节奏;

  1. 在内部维护了个Haner和Looper,保证绘制发生在UI主线程:Looper.myLooper==mLooper判断是否是主线程,是的话去调同步绘制信号,不是的话发送消息,走主线程去调同步绘制信号
  2. 走native层请求垂直同步信号,实际是找底层驱动要上次绘制的时间
  3. 请求到垂直同步信号后回调onVsync
  4. 走doFrame去逻辑管控, 判断当前时间离上次绘制的时间大于了1帧的时间(16.66毫秒) 就跳帧(卡顿优化有用到),若小于16.66毫秒就再次请求垂直同步信号,防止重叠
  5. 执行callback,让ViewRootImpl去真正绘制,调用ViewRootImpl.performTraversals()

真正的绘制: ViewRootImpl.performTraversals()

  1. 调用relayoutWindow()
  2. 创建用户java层的surface:只有用户提供的画面数据;
  3. 创建native层的surface:包含用户提供的画面数据(java层的surface)+系统的画面数据(状态栏,电池、wifi等等);
  4. 创建完surface后:依次调用:performMeasure(对应view的onMeasure)、performLayout(onLayout)、performDraw(onDraw);

performDraw()中:

  1. 将view的数据传至native层的surface
  2. surface中的canvas记录数据
  3. 生成bitmap图像数据(此时数据是在surface中)
  4. 将surface放入队列中;生产者消费者模式;
  5. 通知surfaceflinfer进程去队列中取surface数据
  6. surfaceflinfer拿到不同的surface,进行融合,生成bitmap数据
  7. 将bitmap数据放入framebuffer中,进行展示

简单版总结: Activity.setContentView(R.layout.resId):

解析xml并实例化;

  1. 调用phoneWindow.setContentView(resId)
  2. 在setContentView中调用installDector():根据不同的主题,找到系统默认的xml,初始化出mDector和mContentParent(反射实例化出对应的ViewGroup)
  3. 初始化完成后,调用mLayoutInflater.inflate(resId,mContentParent):
  4. 解析resId的xml文件,将解析的view反射实例化;递归添加到各节点的viewgroup中;最后将自己定义的xml根布局view添加到mContentParent;

绘制发生时间: 在AMS回调ActivityThread中的handleResumeActivity时,也就是Resume时,而不是onCreate()

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

推荐阅读更多精彩内容