小部件加载
小部件加载的流程,三方交互,Provider数据方,system_server中转站以及宿主方Host方(AppWidgetHost)
Provider 进程
provider这里面就是继承了provider,就具体来写这个RemoteView的地方,然后它这里为什么用的是remoteView呢?因为你这里是provider进程,你到时候还要跨进程传输到system_server,然后传送到这个Host,两次IPC binder调用,然后再渲染,然后你这个具体我不太懂,但是这个应该是不同的进程,导致这个View,它实际上拿不到具体的属性,然后只能用这个remoteView这种传输layoutId+action来做
具体来说 RemoteViews 不是 View。 它是一个 指令包 — layoutId + Action 列表
Parcel 序列化过去的:layoutId + [SetText(“hello”), SetImage(R.drawable.xx), …]
我这里其实很迷惑,你这里是R.这个不能跨进程吗?那那个context我记得都能loadApk,这个相当于也能跨进程拿资源了吧?那我用这个context加载不也行吗?
Answer:R 资源 ID 确实能跨进程用,context 也确实能 loadApk 拿资源。但问题不在资源,在 View 对象本身
为什么不能直接传 View?View 对象不能跨进程传输。原因很简单:
View 持有的东西:
├── mContext ← 绑定了当前进程的 Context
├── mParent ← 指向父 View 的引用
├── mAttachInfo ← 绑定了当前 Window 的渲染管线
├── RenderNode ← native 层渲染节点
├── 各种 Listener ← onClick 等回调,是代码逻辑
└── Canvas/Surface ← 绘制目标
这些东西全是进程内的内存引用,没法序列化,也没法 Parcel。你不可能把一个 TextView 对象通过 Binder 发到另一个进程。
所以 Android 的设计是:不传 View,传”怎么造 View”的指令。也就是这个RemoteViews: 指令包
总之,这里provider就提供这个remoteView 资源
system_server
这里我感觉就像是中转站+调度中心/大脑 来协调事务的
AppWidgetServiceImpl 维护一张映射表
widgetId → RemoteViews ← 存最新的 remoteView
widgetId → [hostCallbacks] ← 谁在听
Host
startListening(mCallbacks) 注册接口,就往hostCallbacks里面塞东西了,它就能拿到对应的这个remoteView更新了
具体更新流程
Provider: AppWidgetManager.updateAppWidget(42, remoteView) IPC 序列化传输
system_server: table[42] = rv, hostCallbacks.updateAppWidget IPC 序列化传输 & binder线程post message到主线程
Host: updateAppWidgetView(42, remoteView) applyRemoteViews
- reapply (布局没变,复用 View) performApply: 重放 Action
- apply (首次或布局变了) prepareContext createPackageContext
inflate(layoutId) 创建 View 树 performApply 重放 Action
addView 上屏
这边context应该是跨进程拿资源的,有个loadApk




