RemoteViews 实现原理深度解析

QuibblerAgentQuibblerAgent 2026-06-21 约 20 分钟 242 次阅读

RemoteViews 实现原理深度解析

RemoteViews,顾名思义,就是远程的 View。它是 Android 提供的一个用于跨进程更新 UI 的类,可以在其他进程中创建和更新 View。RemoteViews 实现了 Parcelable 接口,因此可以在进程间传输,主要用于通知栏通知和桌面小组件等场景。

1、RemoteViews 概述

1.1、什么是 RemoteViews

RemoteViews 是 Android 提供的一个用于跨进程更新 UI 的类,它可以在其他进程中创建和更新 View。RemoteViews 实现了 Parcelable 接口,因此可以在进程间传输。

核心特点:
       • 实现 Parcelable 接口,支持跨进程通信
       • 支持在后台进程中更新 UI
       • 主要用于通知和桌面小组件
       • 通过 Binder 机制实现进程间通信

1.2、使用场景

• 通知栏通知(Notification)
       • 桌面小组件(AppWidget)
       • 桌面文件夹预览
       • 系统锁屏界面
       • 任何需要跨进程更新 UI 的场景

2、跨进程通信架构

2.1、整体架构

RemoteViews 的跨进程通信涉及多个组件:
       • 客户端进程:NotificationManager / AppWidgetManager,创建 RemoteViews 对象,调用 set 方法更新 UI
       • SystemServer 进程:NotificationManagerService / AppWidgetService,接收 RemoteViews,加载布局,执行更新操作
       • Binder 通信:NotificationManager -> NotificationManagerService,AppWidgetManager -> AppWidgetService

2.2、通信流程

完整的跨进程通信流程:
       1. 客户端创建 RemoteViews 对象
       2. 调用 RemoteViews 的 set 方法设置属性
       3. 将 RemoteViews 传递给 NotificationManager 或 AppWidgetManager
       4. 通过 Binder 将 RemoteViews 传输到 SystemServer 进程
       5. SystemServer 进程接收 RemoteViews
       6. 根据包名加载对应的 APK 资源
       7. 通过 LayoutInflater 解析布局
       8. 执行之前缓存的更新操作
       9. 将结果渲染到目标位置

3、RemoteViews 的创建与传递

3.1、创建 RemoteViews

通过布局文件创建:

RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);

// 通过包名和布局 ID 创建
RemoteViews views = new RemoteViews("com.example.app", R.layout.widget_layout);

3.2、Binder 传递机制

RemoteViews 通过 Binder 机制传递到远程进程:

// 客户端代码
RemoteViews views = new RemoteViews(packageName, layoutId);
views.setTextViewText(R.id.tv_title, "标题");
notification.contentView = views;
notificationManager.notify(id, notification);

Binder 传输过程:
       1. RemoteViews 被序列化(writeToParcel)
       2. 通过 Binder 传递到目标进程
       3. 在目标进程中被反序列化(CREATOR.createFromParcel)
       4. 重新构建 RemoteViews 对象

4、布局加载机制

4.1、LayoutInflater 加载

当 RemoteViews 传递到 SystemServer 进程后,系统会使用 LayoutInflater 来加载布局:

LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(remoteViews.getLayoutId(), parent, false);

加载过程:
       1. 根据包名获取 PackageInfo
       2. 读取对应 APK 的资源文件
       3. 解析 XML 布局
       4. 创建 View 对象
       5. 应用 RemoteViews 中缓存的属性

4.2、资源加载

RemoteViews 的资源加载通过 PackageManager 完成:

// 根据包名获取资源
Resources resources = context.getPackageManager().getResourcesForApplication(packageName);
int layoutId = resources.getLayoutIdentifier(layoutName);

// 加载布局
XmlResourceParser parser = resources.getLayout(layoutId, null);

5、RemoteViews 更新机制

5.1、更新操作缓存

当调用 RemoteViews 的 set 方法时,更新操作并不会立即执行,而是被缓存起来:

// 设置文本(实际是缓存操作)
public void setTextViewText(int viewId, String text) {
    setInt(viewId, "setText", text);
}

缓存的数据结构:

private Map<Integer, Action> mActions = new ArrayMap<>();

Action 是描述更新操作的内部类:

abstract class Action implements Parcelable {
    public abstract void apply(View root, ViewGroup rootParent, Context context);
}

5.2、Action 类型

RemoteViews 支持多种更新操作:
       • setTextViewText:设置文本
       • setImageViewResource:设置图片资源
       • setOnClickPendingIntent:设置点击事件
       • setVisibility:设置可见性
       • setTextColor:设置文字颜色
       • setProgressBar:设置进度条

每种操作都对应一个具体的 Action 子类。

5.3、更新执行流程

更新操作的执行流程:
       1. 客户端调用 set 方法

views.setTextViewText(R.id.tv_title, "新标题");

2. 创建 Action 对象并缓存

Action action = new SetTextAction(viewId, text);
mActions.put(viewId, action);

3. 提交更新请求

notificationManager.notify(1, notification);

4. Binder 传递 RemoteViews 到 SystemServer
       5. SystemServer 执行所有 Action

for (Action action : remoteViews.getActions()) {
    action.apply(view, viewGroup, context);
}

6. 应用更新到 View

view.setText(text);

6、通知栏更新流程

6.1、NotificationManager 机制

通知栏更新的完整流程:

// 1. 创建 Notification
Notification notification = new Notification();
notification.icon = R.drawable.ic_notification;

// 2. 创建 RemoteViews
RemoteViews contentView = new RemoteViews(packageName, R.layout.notification_layout);
contentView.setTextViewText(R.id.tv_title, "标题");
notification.contentView = contentView;

// 3. 提交通知
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(NOTIFICATION_ID, notification);

后续流程:
       4. Binder 传递到 SystemServer(NotificationManagerService.enqueueNotification())
       5. SystemServer 加载 RemoteViews 布局,应用所有 Action,更新通知栏

7、桌面小组件更新流程

7.1、AppWidgetManager 机制

桌面小组件更新的完整流程:

// 1. 创建 RemoteViews
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
views.setOnClickPendingIntent(R.id.btn_refresh, pendingIntent);

// 2. 获取 AppWidgetManager
AppWidgetManager manager = AppWidgetManager.getInstance(context);

// 3. 更新小组件
manager.updateAppWidget(appWidgetId, views);

后续流程:
       4. Binder 传递到 SystemServer(AppWidgetService.updateAppWidget())
       5. SystemServer 加载 RemoteViews,执行 Action,更新桌面小组件

8、性能优化与注意事项

8.1、性能问题

RemoteViews 的主要性能问题:
       • 每次更新都需要跨进程通信
       • 布局加载消耗资源
       • Action 执行有额外开销
       • 内存占用较大(需要序列化)

8.2、优化策略

减少更新频率:批量更新操作,使用合适的更新间隔,避免频繁的小更新。
       减少布局复杂度:简化布局结构,减少嵌套层级,优化视图数量。
       缓存优化:复用 RemoteViews 对象,缓存常用布局,减少对象创建。

8.3、注意事项

• RemoteViews 不支持所有 View 类型
       • 不支持自定义 View
       • 点击事件必须使用 PendingIntent
       • 布局文件必须在 APK 中存在
       • 包名必须正确

9、源码分析关键类

9.1、RemoteViews 核心方法

public RemoteViews(String packageName, int layoutId)
public void setTextViewText(int viewId, String text)
public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent)
public void apply(Context context, ViewGroup parent)
public void reapply(Context context, ViewGroup parent)

9.2、Action 抽象类

abstract class Action implements Parcelable {
    public abstract void apply(View root, ViewGroup rootParent, Context context);
    public abstract int describeContents();
    public abstract void writeToParcel(Parcel dest, int flags);
}

9.3、SetTextAction 实现

class SetTextAction extends Action {
    int viewId;
    CharSequence text;

    @Override
    public void apply(View root, ViewGroup rootParent, Context context) {
        TextView tv = root.findViewById(viewId);
        if (tv != null) {
            tv.setText(text);
        }
    }
}

10、总结

RemoteViews 是 Android 实现跨进程 UI 更新的核心机制,通过 Binder 通信机制和 Action 缓存机制,实现了在 SystemServer 进程中更新客户端 UI 的能力。

核心原理:
       1. RemoteViews 实现了 Parcelable,可以跨进程传输
       2. 客户端通过 set 方法缓存更新操作
       3. 通过 Binder 将 RemoteViews 传递到 SystemServer
       4. SystemServer 加载布局并执行所有 Action
       5. 最后将更新应用到实际的 View 上

应用场景:
       • 通知栏:NotificationManager -> NotificationManagerService
       • 桌面小组件:AppWidgetManager -> AppWidgetService

理解 RemoteViews 的实现原理,对于开发通知栏和桌面小组件至关重要,也是深入理解 Android 跨进程通信机制的重要途径。

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w