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对象就是通过对应包名创建的上下文对象,创建过程如下:

 

posted @ 2022-06-23 10:47  include_chen  阅读(137)  评论(0编辑  收藏  举报