zoukankan      html  css  js  c++  java
  • Android 4.4 KitKat NotificationManagerService使用具体解释与原理分析(二)__原理分析

    前置文章:

    《Android 4.4 KitKat NotificationManagerService使用具体解释与原理分析(一)__使用具体解释》

    转载请务必注明出处:http://blog.csdn.net/yihongyuelan

    概况

            在上一篇文章《Android 4.4 KitKat NotificationManagerService使用具体解释与原理分析(一)__使用具体解释》中具体介绍了NotificationListenerService的用法,以及在使用过程中遇到的问题和规避方案。本文主要分析NotificationListenerService实现原理。以及具体分析在上一篇文章中提到的相关问题和产生的根本原因。

    在原理分析前。先看看NotificationListenerService涉及到的类以及基本作用,如图1所看到的:


    图 1 NLS注冊及回调过程

    通过图1能够看到,整个通知状态获取分为三部分:

    ①. 监听器注冊。新建一个类NotificationMonitor继承自NotificationListenerService。

    ②. 系统通知管理。系统通知管理由NotificationManagerService负责。

    ③. 通知状态回调;当系统通知状态改变之后,NotificationManagerService会通知NotificationListenerService。最后再由NotificationListenerService通知其全部子类。

    在整个系统中,通知管理是由NotificationManagerService完毕的。NotificationListenerService仅仅是在通知改变时,会获得对应的通知消息。这些消息终于会回调到NotificationListenerService的全部子类中。

    NotificationListenerService启动

            NotificationListenerService尽管继承自Service,但系统中实际上启动的是其子类,为了表述方便,后文统一使用NotificationListenerService启动来指代。其子类的启动有三个途径。各自是:开机启动、接收PACKAGE相关广播(安装、卸载等)启动、SettingsProvider数据变更启动。

            既然NotificationListenerService是一个service,那其子类启动方式自然就是bindService或者startService。在SourceCode/frameworks/base/services/java/com/android/server/NotificationManagerService.java中能够找到。实际上NotificationListenerService的启动是通过bindServiceAsUser来实现的,而bindServiceAsUser与bindService作用一致。

    开机启动

            由于NotificationListenerService终于是在NotificationManagerService中启动的,因此当系统在开机第一次启动时。会进行NotificationManagerService初始化,之后会调用其SystemReady方法,继而调用rebindListenerServices以及registerListenerService(),最后使用bindServiceAsUser实现NotificationListenerService的启动。rebindListenerServices代码例如以下:

    void rebindListenerServices() {
        final int currentUser = ActivityManager.getCurrentUser();
        //获取系统中哪些应用开启了Notification access
        String flat = Settings.Secure.getStringForUser(
                mContext.getContentResolver(),
                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
                currentUser);
    
        NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()];
        final ArrayList<ComponentName> toAdd;
    
        synchronized (mNotificationList) {
            // unbind and remove all existing listeners
            toRemove = mListeners.toArray(toRemove);
    
            toAdd = new ArrayList<ComponentName>();
            final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>();
            final HashSet<String> newPackages = new HashSet<String>();
    
            // decode the list of components
            if (flat != null) {
                String[] components = flat.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
                for (int i=0; i<components.length; i++) {
                    final ComponentName component
                            = ComponentName.unflattenFromString(components[i]);
                    if (component != null) {
                        newEnabled.add(component);
                        toAdd.add(component);
                        newPackages.add(component.getPackageName());
                    }
                }
    
                mEnabledListenersForCurrentUser = newEnabled;
                mEnabledListenerPackageNames = newPackages;
            }
        }
    
        //对全部NotificationListenerService全部unbindService操作
        for (NotificationListenerInfo info : toRemove) {
            final ComponentName component = info.component;
            final int oldUser = info.userid;
            Slog.v(TAG, "disabling notification listener for user " + oldUser + ": " + component);
            unregisterListenerService(component, info.userid);
        }
        //对全部NotificationListenerService进行bindService操作
        final int N = toAdd.size();
        for (int i=0; i<N; i++) {
            final ComponentName component = toAdd.get(i);
            Slog.v(TAG, "enabling notification listener for user " + currentUser + ": "
                    + component);
            registerListenerService(component, currentUser);
        }
    }
    在该方法中将获取系统中全部NotificationListenerService,并进行registerListenerService操作,代码例如以下:
    private void registerListenerService(final ComponentName name, final int userid) {
    //... ...省略
    
    Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
    intent.setComponent(name);
    
    intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
            R.string.notification_listener_binding_label);
    intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
            mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0));
    
    try {
        if (DBG) Slog.v(TAG, "binding: " + intent);
        //使用bindService启动NotificationListenerService
        if (!mContext.bindServiceAsUser(intent,
                new ServiceConnection() {
                    INotificationListener mListener;
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                        synchronized (mNotificationList) {
                            mServicesBinding.remove(servicesBindingTag);
                            try {
                                mListener = INotificationListener.Stub.asInterface(service);
                                NotificationListenerInfo info = new NotificationListenerInfo(
                                        mListener, name, userid, this);
                                service.linkToDeath(info, 0);
                                //service启动成功之后将相关信息加入到mListeners列表中,兴许通过该列表触发回调
                                mListeners.add(info);
                            } catch (RemoteException e) {
                                // already dead
                            }
                        }
                    }
    
                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                        Slog.v(TAG, "notification listener connection lost: " + name);
                    }
                },
                Context.BIND_AUTO_CREATE,
                new UserHandle(userid)))
        //... ...省略
        }
    }
    整个启动流程如图2所看到的:


    图 2 NLS开机启动时序图

    广播启动

            当系统安装或者卸载应用的时候。也会触发NotificationListenerService的启动。当一个使用NotificationListenerService的应用被卸载掉后,须要在Notification access界面清除对应的选项,或者当多用户切换时,也会更新NotificationListenerService的状态。在NotificationManagerService中监听了下面广播:

    Intent.ACTION_PACKAGE_ADDED
    Intent.ACTION_PACKAGE_REMOVED
    Intent.ACTION_PACKAGE_RESTARTED
    Intent.ACTION_PACKAGE_CHANGED
    Intent.ACTION_QUERY_PACKAGE_RESTART
    Intent.ACTION_USER_SWITCHED
            这些广播在应用变更时由系统发出。比方安装、卸载、覆盖安装应用等等。当NotificationManagerService接收这些广播后编会调用rebindListenerServices,之后的流程就与前面一样。

    启动流程例如以下:


    图 3 NLS广播启动

    数据库变更启动

            在NotificationManagerService中使用了ContentObserver监听SettingsProvider数据库变化,当Notification access有更新时,会更新NotificationListenerService的状态。比如,当用户进入Notification access界面,手动开启或关闭相关应用的Notification access权限时便会触发这样的启动方式。当数据库中NotificationListenerService关联的信息改变后。会触发ContentObserver的onChange方法,继而调用update方法更新系统中NotificationListenerService的服务状态,最后调用到rebindListenerServices中。

    整个流程例如以下:


    图 4 NLS数据库变更启动

    NotificationListenerService启动小结

            在系统中实际上运行的是NotificationListenerService的子类,这些子类的启动方式分为三种:开机启动时NotificationManagerService初始化回调。接收相关广播后运行;数据库变更后运行。

    这些启动方式归根究竟还是bindService的操作。

    NotificationListenerService调用流程

    前面提到了NotificationListenerService的启动流程,当启动完毕之后就是调用。整个调用流程分为两种情况。即:新增通知和删除通知。

    新增通知

    当系统收到新的通知消息时,会调用NotificationManager的notify方法用以发起系统通知,在notify方法中则调用关键方法enqueueNotificationWithTag:

    service.enqueueNotificationWithTag(......)
    这里的service是INotificationManager的对象,而NotificationManagerService继承自INotificationManager.Stub。

    也就是说NotificationManager与NotificationManagerService实际上就是client与server的关系,这里的service终于是NotificationManagerService的对象。这里便会跳转到NotificationManagerService的enqueueNotificationWithTag方法中,实际调用的是enqueueNotificationInternal方法。在该方法中就涉及到Notification的组装。之后调用关键方法notifyPostedLocked():

    private void notifyPostedLocked(NotificationRecord n) {
        final StatusBarNotification sbn = n.sbn.clone();
        //这里触发mListeners中全部的NotificationListenerInfo回调
        for (final NotificationListenerInfo info : mListeners) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    info.notifyPostedIfUserMatch(sbn);
                }});
        }
    }
    到这里就開始准备回调了,由于前面通知已经组装完成准备显示到状态栏了,之后就须要将相关的通知消息告诉全部监听者。

    继续看到notifyPostedIfUserMatch方法:

    public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
        //... ...省略
        try {
            listener.onNotificationPosted(sbn);
        } catch (RemoteException ex) {
            Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
        }
    }

    上面的listener对象是NotificationListenerInfo类的全局变量,那是在哪里赋值的呢?还记得前面注冊NotificationListenerService的时候bindServiceAsUser,当中new了一个ServiceConnection对象,并在其onServiceConnected方法中有例如以下代码:

    public void onServiceConnected(ComponentName name, IBinder service) {
    synchronized (mNotificationList) {
        mServicesBinding.remove(servicesBindingTag);
        try {
            //mListener就是NotificationListenerService子类的对象
            //service是INotificationListenerWrapper的对象。INotificationListenerWrapper
            //继承自INotificationListener.Stub,是NotificationListenerService的内部类
            mListener = INotificationListener.Stub.asInterface(service);
            //使用mListener对象生成相应的NotificationListenerInfo对象
            NotificationListenerInfo info = new NotificationListenerInfo(
                    mListener, name, userid, this);
            service.linkToDeath(info, 0);
            mListeners.add(info);
        } catch (RemoteException e) {
            // already dead
        }
    }
    }
    也就是说在NotificationListenerService启动并连接的时候,将binder对象保存到了NotificationListenerInfo中。

    这里就得看看NotificationListenerService的onBind方法返回了,代码例如以下:

    @Override
    public IBinder onBind(Intent intent) {
        if (mWrapper == null) {
            mWrapper = new INotificationListenerWrapper();
        }
        //这里返回的是INotificationListenerWrapper对象
        return mWrapper;
    }
    
    private class INotificationListenerWrapper extends INotificationListener.Stub {
        @Override
        public void onNotificationPosted(StatusBarNotification sbn) {
            try {
                //onNotificationPosted是抽象方法之中的一个
                NotificationListenerService.this.onNotificationPosted(sbn);
            } catch (Throwable t) {
                Log.w(TAG, "Error running onNotificationPosted", t);
            }
        }
        @Override
        public void onNotificationRemoved(StatusBarNotification sbn) {
            try {
                //onNotificationRemoved是还有一个抽象方法
                NotificationListenerService.this.onNotificationRemoved(sbn);
            } catch (Throwable t) {
                Log.w(TAG, "Error running onNotificationRemoved", t);
            }
        }
    }

            通过以上代码能够知道,当在notifyPostedIfUserMatch运行listener.onNotificationPosted方法时。实际上会调用到NotificationListenerService.INotificationListenerWrapper的onNotificationPosted方法。

            NotificationListenerService是一个Abstract类。当中的Abstract方法是onNotificationPosted和onNotificationRemoved。当触发NotificationListenerService.INotificationListenerWrapper的onNotificationPosted方法时,继续调用了NotificationListenerService.this.onNotificationPosted(sbn)。这样会继续调用全部NotificationListenerService子类中的onNotificationPosted方法,系统通知新增的消息便传到了全部NotificationListenerService中。

            从整个流程来看。新增通知的发起点是NotificationManager,处理通知则是由NotificationManagerService完毕,传输过程是通过NotificationListenerService。最后回调方法是各个继承自NotificationListenerService的子类。整个过程的调用时序图例如以下:


    图 5 onNotificationPosted触发流程

    删除通知

            与"新增通知"类似的流程是"删除通知",发起点在NotificationManager,之后经由NotificationManagerService处理和NotificationListenerService传递,最后到达各个继承自NotificationListenerService的子类中,仅仅只是最后的处理方法变成了onNotificationRemoved。调用时序图下:


    图 6 onNotificationRemoved触发流程

    NotificationListenerService调用流程小结

            简单来看,NotificationListenerService在系统通知的消息传递过程中,起到了代理的作用。

    继承自NotificationListenerService的类作为client端。真正的server端则是NotificationManagerService,由它负责整个Notification的控制与管理。NotificationManagerService将处理之后的结果通过NotificationListenerService返回给client端,终于各个client端通过onNotificationPosted和onNotificationRemoved方法拿到系统通知状态变更的相关信息。

    NotificationListenerService重点分析

            前文分析了整个NotificationListenerService的启动和调用。通过以上分析能够非常清楚的了解NotificationListenerService的工作流程。在上一篇文章《Android 4.4 KitKat NotificationManagerService使用具体解释与原理分析(一)__使用具体解释》中,文末分析了在NotificationListenerService在使用过程中的遇到的一些问题。但并没有深究出现这些问题的根本原因,下文会对这些问题进行具体分析。

    Notification access页面不存在

            当手机上没有安装不论什么使用NotificationListenerService的应用时,系统默认不会显示"Notification access"选项。

    仅仅有手机中安装了使用NotificationListenerService的应用,才干够在"Settings > Security > Notification access" 找到相应的设置页面。

    在SourceCode/packages/apps/Settings/src/com/android/settings/SecuritySettings.java中能够看到例如以下初始化代码:

    //... ...省略
    mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
    if (mNotificationAccess != null) {
        final int total = NotificationAccessSettings.getListenersCount(mPM);
        if (total == 0) {
            if (deviceAdminCategory != null) {
                //假设系统中没有安装使用NLS的应用则删除显示
                deviceAdminCategory.removePreference(mNotificationAccess);
            }
        } else {
            //获取系统中有多少启动了Notification access的应用
            final int n = getNumEnabledNotificationListeners();
            //依据启用的数量显示不同的Summary
            if (n == 0) {
                mNotificationAccess.setSummary(getResources().getString(
                        R.string.manage_notification_access_summary_zero));
            } else {
                mNotificationAccess.setSummary(String.format(getResources().getQuantityString(
                        R.plurals.manage_notification_access_summary_nonzero,
                        n, n)));
            }
        }
    }
    //... ...省略

    getActiveNotifications()方法返回为null

            有非常多人在使用getActiveNotifications方法时返回为null。多数情况下是由于在onCreate或者在onBind中调用了getActiveNotifications方法。比方NotificaionMonitor extends NotificationListenerService:

    public class NotificationMonitor extends NotificationListenerService {
    @Override    
    public void onCreate() {
        //getActiveNotifications();
        super.onCreate();
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        getActiveNotifications();
        return super.onBind(intent);
    }
    
    }

    找到NotificationListenerService中的getActiveNotifications方法实现,代码例如以下:

    public StatusBarNotification[] getActiveNotifications() {
        try {
            //getActiveNotifications成功运行的两个关键点:
            //1.getNotificationInterface方法返回正常
            //2.mWrapper对象不为null
            return getNotificationInterface().getActiveNotificationsFromListener(mWrapper);
        } catch (android.os.RemoteException ex) {
            Log.v(TAG, "Unable to contact notification manager", ex);
        }
        return null;
    }
    
    //通过查看能够知道。getNotificationInterface没有问题。假设为null会进行初始化
    private final INotificationManager getNotificationInterface() {
        if (mNoMan == null) {
            mNoMan = INotificationManager.Stub.asInterface(
                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
        }
        return mNoMan;
    }
    
    //假设mWrapper为null则进行初始化
    @Override
    public IBinder onBind(Intent intent) {
        if (mWrapper == null) {
            mWrapper = new INotificationListenerWrapper();
        }
        return mWrapper;
    }
    通过上面的代码能够知道getActiveNotifications方法调用失败的原因:

    1. service的生命周期会先从onCraete->onBind逐步运行;

    2. 此时调用getActiveNotifications方法会使用NotificationListenerService中的mWrapper对象;

    3. mWrapper对象必须在NotificationMonitor完毕super.onBind方法之后才会初始化;

    综上所述。当在onCreate或者onBind方法中使用getActiveNotifications方法时。会导致mWrapper没有初始化,即mWrapper == null。解决方式能够在onCreate或者onBind方法中使用handler异步调用getActiveNotification方法,详细可參考《Android 4.4 KitKat NotificationManagerService使用具体解释与原理分析(一)__使用具体解释》

    NotificationListenerService失效

            假设NotificationMonitor在onCreate或onBind方法中出现crash。则该NotificationMonitor已经失效。就算改动了NotificationMonitor的代码不会再crash,但NotificationMonitor还是不能收到onNotificationPosted和onNotificationRemoved回调。除非重新启动手机。

            这个问题是google设计上的缺陷导致,出现NotificationListenerService失效的必要条件: 在NotificationMonitor的onCreate或者onBind中出现异常,导致service crash。也就是说service还没有全然启动的情况下出现了异常导致退出。

            这里须要回到NotificationManagerService中,NotificationListenerService的注冊方法registerListenerService中:

    private void registerListenerService(final ComponentName name, final int userid) {
        //servicesBindingTag能够理解为须要启动的service的标签
        final String servicesBindingTag = name.toString() + "/" + userid;
        //假设mServicesBinding中已经包括正在处理的service则直接return退出
        if (mServicesBinding.contains(servicesBindingTag)) {
        // stop registering this thing already! we're working on it
           return;
        }
        //将准备启动的service标签加入到mServicesBinding中
        mServicesBinding.add(servicesBindingTag);
    
        //... ...省略
        //使用bindServiceAsUser启动service
        Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
        intent.setComponent(name);
    
        intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                R.string.notification_listener_binding_label);
        intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
                mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0));
    
        try {
            if (DBG) Slog.v(TAG, "binding: " + intent);
            if (!mContext.bindServiceAsUser(intent,
                    new ServiceConnection() {
                        INotificationListener mListener;
                        @Override
                        public void onServiceConnected(ComponentName name, IBinder service) {
                            synchronized (mNotificationList) {
                                //服务成功启动之后删除标签
                                mServicesBinding.remove(servicesBindingTag);
                                try {
                                    mListener = INotificationListener.Stub.asInterface(service);
                                    NotificationListenerInfo info = new NotificationListenerInfo(
                                            mListener, name, userid, this);
                                    service.linkToDeath(info, 0);
                                    mListeners.add(info);
                                } catch (RemoteException e) {
                                    // already dead
                                }
                            }
                        }
    
                        @Override
                        public void onServiceDisconnected(ComponentName name) {
                            Slog.v(TAG, "notification listener connection lost: " + name);
                        }
                    },
                    Context.BIND_AUTO_CREATE,
                    new UserHandle(userid)))
            {
                //绑定服务失败后删除标签
                mServicesBinding.remove(servicesBindingTag);
                Slog.w(TAG, "Unable to bind listener service: " + intent);
                return;
            }
        } catch (SecurityException ex) {
            Slog.e(TAG, "Unable to bind listener service: " + intent, ex);
            return;
        }
    }
    }

            当调用registerListenerService方法时,使用了一个mServicesBinding的ArrayList<String>用来记录当前正在启动的服务。

    在启动之前会推断当前service是否在mServicesBinding之中。假设是则表明正在运行bindServiceAsUser操作。直接退出,否则就继续运行bindServiceAsUser流程。

    调用bindServiceAsUser之前会在mServicesBinding中会加入标签,当连接成功之后也就是onServiceConnected返回后,以及绑定失败后会在mServicesBinding中删除标签。

            google这样设计的目的可能是为了避免同一个service多次启动。因此在运行bindServiceAsUser之前就打上标签,当处理完毕之后(onServiceConnected回调)就删掉这个标签,表明这个service 绑定完毕。

    可是,假设运行bindServiceAsUser之后,NotificationMonitor在onCreate或者onBind的时候crash了。也就是NotificationMonitor还没有完毕启动,因此就不会去调用onServiceConnected方法,并终于导致不会调用 mServicesBinding.remove(servicesBindingTag)方法,从而使得NotificationMonitor的标签被一致记录在mServicesBinding中。那么当下一次想再次注冊该服务的时候,系统发现该服务已经在mServicesBinding中了,所以直接return,后面的bindServiceAsUser就不会被调用了。

            尽管代码已经更新。但service无法正常启动,那么onNotificationPosted和onNotificationRemoved的回调自然就无法使用。此时的解决的方法就仅仅能重新启动手机,清空mServicesBinding的值。

    总结

            NotificationListenerService在系统通知获取的流程中,自身并没有启动,而是起到了一个代理的作用。每个继承自NotificationListenerService的类,当系统通知变化后终于都会收到onNotificationPosted和onNotificationRemoved的回调。

            bindService方法的返回与service是否成功启动无关,因此才会导致NotificationListenerService失效。

            最后再看一下整个NotificationListenerService的关系类图:


    图 7 NLS关系类图

    文中图片资源,免积分下载:戳这里

  • 相关阅读:
    MySQL系列(二)--数据类型
    并发和多线程(十)--锁状态概念
    并发和多线程(九)--并发容器J.U.C和lock简介
    并发和多线程(八)--线程安全、synchronized、CAS简介
    Nuxt 2.3.X 配置babel
    Nuxt 2.3.X 配置sass
    vscode写vue模板--代码片段
    ES6和ES5中的this指向问题
    TypeScript -- JavaScript的救赎
    Pycharm 查看一个类的继承关系图
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/6937808.html
Copyright © 2011-2022 走看看