1. RemoteViews 的应用场景
- 传统的应用场景:通知栏和桌面小部件
- 实际开发需求:现在有两个应用,一个应用需要能够更新另一个应用中的某个界面
- AIDL: 如果对界面的更新比较频繁,此时会有效率问题,同时 AIDL 接口可能会很复杂
- RemoteViews: 没有 AIDL 的性能问题,但缺点是仅支持常见的 View,不支持自定义 View
2. Demo: 实现一个模拟的通知栏效果并实现跨进程的 UI 更新
需求
- 有 2 个 Activity 分别运行在不同的进程中,一个名字叫 A,一个名字叫 B。其中 A 扮演着模拟通知栏的角色,而 B 则可以不停地发送通知栏消息
- 修改 A 的 process 属性使其运行在单独的进程中,这样 A 和 B 就构成了多进程通信。在 B 中创建 RemoteViews 对象,然后通知 A 显示这个 RemoteViews 对象
- 可以像系统一样采用 Binder 来实现,这里简单起见采用广播。B 每发送一次模拟通知,就会发送一个特定的广播,然后 A 接收到广播后就开始显示 B 中定义的 RemoteViews 对象
实现
1
2
3
4
5
6
7
8
9
10
11// B 的实现
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_simulated_notification);
remoteViews.setTextViewText(R.id.msg, "msg from process: " + Process.myPid());
remoteViews.setImageViewResource(R.id.icon, R.drawable.icon1);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, DemoActivity_1.class), PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent openActivity2PendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, DemoActivity_2.class), PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.item_holder, pendingIntent);
remoteViews.setOnClickPendingIntent(R.id.open_activity2, openActivity2PendingIntent);
Intent intent = new Intent(MyConstants.REMOTE_ACTION);
intent.putExtra(MyConstants.EXTRA_REMOTE_VIEWS, remoteViews);
sendBroadcast(intent);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// A 的实现
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private LinearLayout mRemoteViewsContent;
private BroadcastReceiver mRemoteViewsReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
RemoteViews remoteViews = intent.getParcelableExtra(MyConstatns.EXTRA_REMOTE_VIEWS);
if (remoteViews != null) {
updateUI(remoteViews);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mRemoteViewsContent = (LinearLayout) findViewById(R.id.remote_views_content);
IntentFilter filter = new IntentFilter(MyConstants.REMOTE_ACTION);
registerReceiver(mRemoteViewsReceiver, filter);
}
private void updateUI(RemoteViews remoteViews) {
// 会有资源 id 不一致的问题
// View view = remoteViews.apply(this, mRemoteViewsContent); // 加载布局文件并执行更新操作
// 解决 id 不一致问题:通过资源名称来加载布局文件
int layoutId = getResources().getIdentifier("layout_simulated_notification", "layout", getPackagename());
View view = getLayoutInflater().inflate(layoutId, mRemoteViewsContent, false);
remoteViews.reapply(this, view);
mRemoteViewContent.addView(view); // 将得到的 View 添加到 A 的布局中
}
@Override
protected void onDestroy() {
unregisterReceiver(mRemoteViewsReceiver);
super.onDestroy();
}
}