ServiceManager

ServiceManager的启动

ServiceManager进程service_manager.c并没有使用libbinder框架代码,而是自行编写了binder.c直接和Binder驱动来通信,ServiceManager是单线程的进程, 不断地循环在binder_loop()过程来读取和处理事务,从而对外提供查询和注册服务的功能,这样的好处是简单而高效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// service_manager.c
int main(int argc, char **argv) {
struct binder_state *bs;
char *driver;
if (argc > 1) {
driver = argv[1];
} else {
driver = "/dev/binder"; // 默认的Binder设备节点
}

//Step 1: 打开binder驱动,申请128k字节内存
bs = binder_open(driver, 128*1024);
...

//Step 2: 成为上下文管理者
if (binder_become_context_manager(bs)) {
return -1;
}
...

//Step 3: 进入无限循环,处理client端发来的请求
binder_loop(bs, svcmgr_handler);
return 0;
}

启动过程主要划分为以下几个阶段:(每个阶段具体见5.2.1 启动ServiceManager服务)

create_servicemanager

  • 首先,打开设备驱动:调用binder_open()方法来打开binder驱动,默认地采用/dev/binder设备节点,申请地内存空间大小为128KB;

  • 其次,注册成为大管家:调用binder_become_context_manager()方法,将自己注册成为binder服务的唯一管家;

    通过ioctl系统调用向Binder驱动发送命令BINDER_SET_CONTEXT_MGR,成为上下文的管理者,由于servicemanager进程启动非常早(先于Zygote),可以确定在Binder整体机制正式投入产线之前,就能完成向Binder驱动注册成为大管家的工作。 关于驱动层处理BINDER_SET_CONTEXT_MGR命令的主要任务:

    • 保证每个Binder上下文有且仅有一个binder管家实体,如果已存在则不再创建
    • 创建binder管家实体,初始化异步事务和binder工作两个队列,并分别增加其强弱引用计数
    • 初始化当前binder_context的管家实体(binder_context_mgr_node)和管家uid(binder_context_mgr_uid)信息
    • handle等于0的服务实体都是指servicemanager管家实体
  • 最后,等待客户请求:调用binder_loop()方法进入无限循环,作为守护进程,随时待命等待处理client端发来的请求。

servicemanager先向Binder驱动发送BC_ENTER_LOOPER协议,让ServiceManager进入循环。然后再向驱动发送BINDER_WRITE_READ命令, 进入内核态,等待客户端的请求数据。若没有数据,则进入等待状态,直到收到数据后返回用户态,解析并处理,周而复始地不断循环该过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func) {
int res;
struct binder_write_read bwr;
uint32_t readbuf[32];

bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;

readbuf[0] = BC_ENTER_LOOPER;
//向binder驱动发送BC_ENTER_LOOPER协议
binder_write(bs, readbuf, sizeof(uint32_t));

for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
//等待客户的数据
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
//解析binder信息
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
}
}

ServiceManager提供的服务

ServiceManager对外提供查询/注册功能,通过接收到客户端进程发送过来的BR_TRANSACTION协议。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// service_manager.c
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
struct svcinfo *si;
uint16_t *s;
size_t len;
uint32_t handle;
uint32_t strict_policy;
int allow_isolated;
...
strict_policy = bio_get_uint32(msg);
s = bio_get_string16(msg, &len);
...

switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
s = bio_get_string16(msg, &len); //服务名
//根据名称查找相应服务
handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);
bio_put_ref(reply, handle);
return 0;

case SVC_MGR_ADD_SERVICE:
s = bio_get_string16(msg, &len); //服务名
handle = bio_get_ref(msg); //服务实体在servicemanager中的handle
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
//注册指定服务
if (do_add_service(bs, s, len, handle, txn->sender_euid,
allow_isolated, txn->sender_pid))
return -1;
break;

case SVC_MGR_LIST_SERVICES: {
uint32_t n = bio_get_uint32(msg);
if (!svc_can_list(txn->sender_pid)) {
return -1;
}
si = svclist;
while ((n-- > 0) && si)
si = si->next;
if (si) {
bio_put_string16(reply, si->name);
return 0;
}
return -1;
}
}

bio_put_uint32(reply, 0);
return 0;
}

该方法的功能:查询服务,注册服务,以及列举所有服务。不同的code对应不同的工作,定义在IBinder.h文件,跟IServiceManager.h中定义的code具有一一对应关系,具体关系如下所示。

code IBinder.h IServiceManager.h
1 SVC_MGR_GET_SERVICE GET_SERVICE_TRANSACTION
2 SVC_MGR_CHECK_SERVICE CHECK_SERVICE_TRANSACTION
3 SVC_MGR_ADD_SERVICE ADD_SERVICE_TRANSACTION
4 SVC_MGR_LIST_SERVICES LIST_SERVICES_TRANSACTION

ServiceManager

servicemanager进程里面有一个链表svclist,记录着所有注册的服务svcinfo,每一个服务用svcinfo结构体来表示,该handle值是在注册服务的过程中,由服务所在进程那一端所确定的。svcinfo结构体如下所示。

ServiceManager进程
1
2
3
4
5
6
7
8
9
struct svcinfo
{
struct svcinfo *next;
uint32_t handle; //服务的handle值
struct binder_death death;
int allow_isolated;
size_t len; //服务名的长度
uint16_t name[0]; //服务名
};

整个ServiceManager启动过程的完整流程,这里整个过程都的都离不开Binder驱动层的实现。

ServiceManager启动过程

Other

vndservicemanager

以前,Binder 服务通过 servicemanager 注册,其他进程可从中检索这些服务。在 Android 8 中,servicemanager 现在专供框架使用,而应用进程和供应商进程无法再对其进行访问。

不过,供应商服务现在可以使用 vndservicemanager,这是一个使用 /dev/vndbinder(作为构建基础的源代码与框架 servicemanager 的相同)而非 /dev/binderservicemanager 的新实例。供应商进程无需更改即可与 vndservicemanager 通信;当供应商进程打开 /dev/vndbinder 时,服务查询会自动转至 vndservicemanager

vndservicemanager 二进制文件包含在 Android 的默认设备 Makefile 中。

Binder

为什么是Binder

img

具体见QA。

一次完整的 Binder IPC 通信过程通常是这样:

其内存流向是这样的:

  1. 首先 Binder 驱动在内核空间创建一个数据接收缓存区;
  2. 接着在内核空间开辟一块内核缓存区,建立内核缓存区内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区接收进程用户空间地址的映射关系;
  3. 发送方进程通过系统调用 copy_from_user() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。

如下图:

nzNJUA.png

Read more

PackageManager

img

PackageManger的核心作用:

  1. 在系统启动过程中,通过**PackageManagerService(PKMS)*对特定系统文件(如package.list, package.xml)进行扫描解析后,将所有app的信息整合存储,通过IPackageManger*对外暴露
  2. 通过PackageInstallerService提供Apk/Apex的安装、更新、卸载等操作(IPackageInstaller)
  3. 应用运行过程中的权限检查
Read more

sharePreference

SharedPreferences原理-xmind

SharedPreferences原理-xmind

SharedPreferences是系统提供的一种简易数据持久化的手段,适合单进程、小批量的数据存储与访问。以键值对的形式存储在xml文件中。
文件存储路径为data/data/package_name/shared_prefs/目录。

源码解析

源码解析

源码解析

Read more

empty

  • PackageManagerService

  • WindowManagerService(done?)

  • ActivityManagerService(done?)

  • ServiceManager(done?)

  • Binder
  • ANR

1、review

kernel bright spots

  • Apm监控
  • fresco深入优化
  • 性能优化工作/工具

skills pool

  • recyclerview缓存和优化

  • app、android启动流程

  • 编译流程

  • 编舞者以及屏幕刷新原理(与耗时方法监听之)

  • 插件化(代理和hook两种方式)

  • handler

  • https&http(http version)

  • hashmap

  • Matrix为主,Dokit、blockcanary等APM框架

  • Jmm与GC算法(深入理解java虚拟机-标记清除之类)

  • tcp滑动窗口之类(就找一篇文章like腾讯之前的那篇tcp ip问题彻底弄懂TCP协议:从三次握手说起 (qq.com)

  • leakCanary、retrofit、okhttp、glide、

  • threadLocal、Rv四级缓存

2、TODO

  • kotlin协程
  • 算法(1~2)
  • View测绘,事件分发(事件系列只能被整一个处理)
  • SurfaceView, TextureView及View的区别。SurfaceView怎么控制它处于的层级
  • SMTP了解下

3、Optimize Experience

  • 图片内存优化工作:fresco关于gif缓存问题修复,dokit图片闪烁缓存失效问题的修复、基于dokit框架(ASM)下尺寸过大图片识别实现。(基于业务做的一些图片加载的优化,如cdn链接统一域名,regex(String.replace)耗时200us)

  • 耗时方法,两种常见的监听方式:looper.printer 以及 编舞者回调监听

  • recyclerview优化

4、Other

  • h5图片缓存共用的优化思路
  • 阿里patron和Metrix做的native hook 减少32bit模式下app内存占用

Project Experience

从购物车重构到购物车预加载

ps:我并不想抽象的去将购物车的架构之类的东西,而是更具体的讲遇到的问题及解决方案。不过还是得简单讲一下设计

购物车代码十分屎山,难以维护。在23年时做了一次历经一个月的重构,边开车边换轮子。使用多数业务在用的协议架构奥创重构:先说收益:奥创加购以组件为单位,职责清晰,易于复用维护,端侧组件使用MVVM架构进一步解耦逻辑:

举个例子,请求在model层(共用的Repository,维护于Engine层),每个组件通过注册组件的ViewHolder获得独立的View,注册组件的Parser获得独立的ViewModel解析数据及进行业务逻辑处理。view与vm之间使用liveData进行数据更新后的通知view状态渲染。

遇到的问题:

  1. 奥创冗余数据及后端接口慢 -> 购物车本地数据缓存 + (detail/购物车进下单页)的数据预加载 + 购物车预加载的根据abtest下做的精细运营。
  2. LiveData的使用:购物车提供给MainActiivty的其他Tab由于时机问题导致的刷新问题
  3. 多国家配置不同组件 -> 问题:一些组件不同国家需要配置不同的样式和逻辑,但是下发的组件名是一致的。那么当时有两种做法:一种是直接在解析是篡改掉网络数据中的组件名,本地正常映射。但是这个评估完认为直接修改了数据,风险更不好控。于是我在注册组件名时根据依靠 同名Parser后注册的Parser优先匹配的机制 实现 的特性,在注册Parser时提供将多国家的parser后置接口,于是当解析完多国家组件parser后即不会再映射通用组件了,即不影响原数据,也实现了多国家不新增后端组件。

RTL adapte

  1. What: Ltr下不同币种导致的显示顺序错乱;
  2. How: 服务端配置下发特殊符号
  3. More:
  1. 做过印象比较深的事情 ,有挑战的事情

  2. 弱网环境优化:业务和技术的手段,有什么很技术的手段来做吗

  3. 当前团队的优劣点

  4. 遇到不合理需求你会怎么拒绝

  5. 有用过新的东西吗

  6. 线上问题怎么解决,因为不能用动态字节码技术

  7. 遇到啥有意思的问题吗,crash。window

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    java.lang.IllegalArgumentException: View=android.widget.PopupWindow$PopupDecorView{2a10009 V.E...... R.....I. 0,0-0,0} not attached to window manager
    at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:544)
    at android.view.WindowManagerGlobal.updateViewLayout(WindowManagerGlobal.java:433)
    at android.view.WindowManagerImpl.updateViewLayout(WindowManagerImpl.java:162)
    at android.widget.PopupWindow.update(PopupWindow.java:2226)
    at android.widget.PopupWindow.update(PopupWindow.java:2347)
    at android.widget.PopupWindow.alignToAnchor(PopupWindow.java:2517)
    at android.widget.PopupWindow.-$$Nest$malignToAnchor(Unknown Source:0)
    at android.widget.PopupWindow$1.onViewAttachedToWindow(PopupWindow.java:243)
    at android.view.View.dispatchAttachedToWindow(View.java:21423)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3502)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3509)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3011)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2518)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9389)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1451)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1459)
    at android.view.Choreographer.doCallbacks(Choreographer.java:1089)
    at android.view.Choreographer.doFrame(Choreographer.java:1003)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1431)
    at android.os.Handler.handleCallback(Handler.java:942)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:210)
    at android.os.Looper.loop(Looper.java:299)
    at android.app.ActivityThread.main(ActivityThread.java:8261)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:559)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:954)

OOM的治理:crash率在一次架构组的升级后由万4涨到万6,其中新增了很多的OOM。1:修复了EventCenter的内存泄露;2:搜推服务造成的泄露;

内存兜底措施:2g设备直接改为RGB565,其他才开ARGB8888;同时降低起设备图片内存池大小;

Surface

  • SurfaceView: View 的子类,但不与宿主 Window 共享 Surface, 而是有自己独立的 Surface, 且可以在一个独立的线程中进行绘制,因此 SurfaceView 一般用来实现比较复杂的图像或动画/视频的显示。可以参考 Android双缓存与SurfaceView。由于其内容是绘制在一个独立的 Surface 上,因此无法用 scrollTo/By 等方法去移动操作 Canvas 里的内容,但是可对整个 View 进行平移,缩放,旋转等变换操作。
  • GLSurfaceView: 基于 SurfaceView 再次进行扩展,在 SurfaceView 基础上封装了 EGL 环境管理以及 Render 线程,专门为 OpenGl 显示渲染使用。参考 Android-OpenGL-ES笔记
  • TextrueView: Android 4.0 后引入 TextureView, 它将 SurfaceTexture 和 View 结合到了一起。与 SurfaceView 相比,它并没有创建一个单独的 Surface 来绘制,解决了 SurfaceView 无法在 Canvas 内容上做动画的问题。另外 TextureView 必须在硬件加速开启的窗口中使用。
Read more

SystemStartProcess

image-20210730142407367

简述:

开机后系统将Rom文件加载进Ram内存中,loader检查Ram,kernel启动Swapper进程和kthreadd进程(创建内核守护进程)。

NativeFramework中init.cpp运行后启动init进程,init进程解析init.rc文件后,孵化如installd、logd、adbd等用户守护进程、启动servicemanager、surfaceflinger、bootanim等服务、孵化出Zygote虚拟机进程(java进程)。

JavaFramework中Zygote注册ZygoteSocket加载虚拟机、预加载通用类、资源;之后孵化system_server进程,启动如AMS(startService可监听RootPhase)、WMSPKMS、PMS等服务。

App:由Zygote孵化的第一个App——Launcher(桌面),用户点击Launcher上的app图片,通过JNI调用AMS从Zygote进程中fork出新的App。

Read more

EventDispatch

image-20220127160157749

Core

事件分发中有一个重要的规则:一个触控点的一个事件序列只能给一个view处理

分析:以DOWN事件为序列分发判定,ViewGroup为消费DOWN事件的View生成一个TouchTarget(这个TouchTarget就包含了该view的实例与触控id,id可以是多个以应对多指触控),后续MOVE、UP都会交给这个TouchTarget。如果TouchTarget为空则ViewGroup自己处理。如果viewGroup消费了down事件,那么子view将无法收到任何事件。

Read more

ScreenDraw

从onCreate到页面绘制

当手指点击了桌面的App图标时发生了什么 - ProcessOn

Android 屏幕刷新机制

主要参考 https://juejin.cn/post/6863756420380196877#heading-12

省流版:

双缓存:为了解决画面撕裂;画面撕裂来自于只有一个buffer时,正在display的那一帧数据被后一帧的数据覆盖了

Vsync:系统在收到VSync pulse(Vsync脉冲)后,将马上开始下一帧的渲染,(CPU开始计算数据)。

三缓冲:当显示器正在写入FrameBuffer同时GPU也正在写入BackBuffer时,下一次渲染开始了,此时CPU可以使用新增的GraphicBuffer进行计算。减少了Jank。(更多缓冲需要耗费更大的内存)

ChoreoGrapher机制:规定了数据计算开始(measure、layout、draw)的时机(vsync信号),使计算到渲染图像数据能有一个完整的16.6ms:更新ui(request()/invalidate())后编舞者注册vsync信号回调,在下一个vsync信号到时候立刻进行view的测量布局绘制

Read more