System UI学习笔记(三)
通知视图是如何实现跨进程显示的?
通讯基础:IPC
通知服务(NMS)同样离不开IPC,核心架构还是IPC架构。
消息通道
1、应用作为通知发送端,需要调用通知服务NMS发送通知。例如:
public void onEventMusicChangeEvent(MusicChangeEvent event) { NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); Intent intent = new Intent(Intent.ACTION_MAIN); intent.putExtra("toValue", "href"); //====增加部分 intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setComponent(new ComponentName(getContext(), MainActivity.class)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); PendingIntent pend = PendingIntent.getActivity(getContext(), 201, intent, PendingIntent.FLAG_UPDATE_CURRENT); String id = "channel_1"; String description = "123"; int importance = NotificationManager.IMPORTANCE_HIGH; NotificationChannel mChannel = new NotificationChannel(id, description, importance); manager.createNotificationChannel(mChannel); Notification notification = new Notification.Builder(this, id)//创建Notification对象。 .setContentTitle("QQMusic") //设置通知标题 .setSmallIcon(R.drawable.qq_music)//设置通知小图标 .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))//设置通知大图标 .setContentText(songNoticeChange)//设置通知内容 .setAutoCancel(true)//设置自动删除通知 .setContentIntent(pend) .build();//运行 notification.flags = Notification.FLAG_AUTO_CANCEL; manager.notify(201, notification); //通知栏保留多条通知 //manager.notify((int) System.currentTimeMillis(), notification); //通知栏保留多条通知 }
2、System UI作为通知的接收需要注册监听器,INotificationListener是监听通知的一个AIDL接口,NotificationListenerService是一个监听管理服务,他的内部类NotificationListenerWrapper实现了INotificationListener接口。例如:
这个通知监听器需要向通知服务NMS注册:
上面是Android为我们提供的通知接收管理服务类,SystemUI有个NotificationListenerWithPlugins类继承了NotificationListenerService类。并且在SystemUI进程起来的时候调用registerAsSystemService()方法完成了注册:
这样的消息通道就建立起来了。
消息传递过程,通过这个思路解读源码。
RemoteViews
前面我们了解到应用如何将消息传递给SystemUI,理解IPC通讯。那么还有个问题就是现实的视图布局定义在一个应用中,为何能跨进程显示到SystemUI进程呢?他们是如何实现的?
发送通知,传递的通知实体是Notification的实例,其实现了Parcelable接口。Notification有个RemoteViews的成员变量
RemoteViews remoteViews=new RemoteViews(getPackageName(),R.layout.layout_remoteviews); notification.contentView=remoteViews;
RemoteViews也实现了Parcelable接口,主要是封装了通知栏要展示的试图信息,例如,应用包名、布局ID。Parcelable这个接口的实现能够在IPC通道上进行跨进程传递。RemoteView支持的布局类型有限,8.0上的类型支持如下:
RemoteView携带了视图信息,进程间传递的并不是真实的视图对象,而主要是布局的id,那么显示在通知栏上的视图对象如何创建出来的呢?下面了解一下。
通知视图创建
在通知的接收端创建的,上面也说过NotificationManagerService内部类NotificationListenerWrapper监听通知消息,在收到消息之后就在里面解析消息且创建视图了。
在maybePopulateRemoteViews()这个方法中会去检查布局是否要加载,更让人难以理解的是SystemUI他是如何加载远程进程的布局资源的?
这里我们有两个关键的信息:包名、布局ID。了解包名SystemUI进程是有权限创建对应包名的上下文对象的,进而可以拿到对应应用的资源管理器,然后就可以加载布局资源对象。maybePopulateRemoteViews()方法跟踪下去,会走到RemoteViews的
其中getContextForResources中的context对象就是通过对应包名创建的上下文对象,创建过程如下:
本文来自博客园,作者:include_chen,转载请注明原文链接:https://www.cnblogs.com/include-chen/p/16404363.html