Android 通知之RankingHelper

Android的通知系统比较复杂,会分开多个章节介绍,本文介绍RankingHelper。

一、RankingHelper简介

因为不同厂商不同设备(手机、TV、汽车等)对于通知的处理方式可能会各不相同,为了方便通知行为的灵活配置&分离解耦,Android平台定义了NotificationSignalExtractor接口,并通过xml文件配置添加相关的处理实现类。先看下接口定义。

public interface NotificationSignalExtractor {

    public void initialize(Context context, NotificationUsageStats usageStats);
    /**
     * Called once per notification that is posted or updated.
     *
     * @return null if the work is done, or a future if there is more to do. The
     * {@link RankingReconsideration} will be run on a worker thread, and if notifications
     * are re-ordered by that execution, the {@link NotificationManagerService} may send order
     * update events to the {@link android.service.notification.NotificationListenerService}s.
     */
    public RankingReconsideration process(NotificationRecord notification);
    void setConfig(RankingConfig config);
    void setZenHelper(ZenModeHelper helper);
}

二、NMS 启动

2.1 - NMS.start

NMS是一个系统服务,在启动时会走onStart方法。

[NotificationManagerService.java]

public void onStart() {
         、、、
        final File systemDir = new File(Environment.getDataDirectory(), "system");
        mRankingThread.start();

        WorkerHandler handler = new WorkerHandler(Looper.myLooper());

        init(handler, new RankingHandlerWorker(mRankingThread.getLooper()),
                AppGlobals.getPackageManager(), getContext().getPackageManager(),
                getLocalService(LightsManager.class),
                new NotificationListeners(getContext(), mNotificationLock, mUserProfiles,
                        AppGlobals.getPackageManager()),
                new NotificationAssistants(getContext(), mNotificationLock, mUserProfiles,
                        AppGlobals.getPackageManager()),
                new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
                null, snoozeHelper, new NotificationUsageStats(getContext()),
                new AtomicFile(new File(
                        systemDir, "notification_policy.xml"), "notification-policy"),
                (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
                getGroupHelper(), ActivityManager.getService(),
                LocalServices.getService(ActivityTaskManagerInternal.class),
                LocalServices.getService(UsageStatsManagerInternal.class),
                LocalServices.getService(DevicePolicyManagerInternal.class),
                UriGrantsManager.getService(),
                LocalServices.getService(UriGrantsManagerInternal.class),
                (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE),
                getContext().getSystemService(UserManager.class),
                new NotificationHistoryManager(getContext(), handler),
                mStatsManager = (StatsManager) getContext().getSystemService(
                        Context.STATS_MANAGER),
                getContext().getSystemService(TelephonyManager.class),
                LocalServices.getService(ActivityManagerInternal.class),
                createToastRateLimiter());
        //发布NMS,后面可以通过NContext.NOTIFICATION_SERVICE字符串
        //来获取NMS
        publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
                DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
        publishLocalService(NotificationManagerInternal.class, mInternalService);
    }

在onStart中调用了init方法。

2.2 - NMS.init

[NotificationManagerService.java]

void init(WorkerHandler handler, RankingHandler rankingHandler,
            IPackageManager packageManager, PackageManager packageManagerClient,
            LightsManager lightsManager, NotificationListeners notificationListeners,
            NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
            ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
            NotificationUsageStats usageStats, AtomicFile policyFile,
            ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
            ActivityTaskManagerInternal atm, UsageStatsManagerInternal appUsageStats,
            DevicePolicyManagerInternal dpm, IUriGrantsManager ugm,
            UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager,
            NotificationHistoryManager historyManager, StatsManager statsManager,
            TelephonyManager telephonyManager, ActivityManagerInternal ami,
            MultiRateLimiter toastRateLimiter) {
        mHandler = handler;
        Resources resources = getContext().getResources();
        、、、
        String[] extractorNames;
        try {
            extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
        } catch (Resources.NotFoundException e) {
            extractorNames = new String[0];
        }
        、、、
        mPreferencesHelper = new PreferencesHelper(getContext(),
                mPackageManagerClient,
                mRankingHandler,
                mZenModeHelper,
                new NotificationChannelLoggerImpl(),
                mAppOps,
                new SysUiStatsEvent.BuilderFactory());
        mRankingHelper = new RankingHelper(getContext(),
                mRankingHandler,
                mPreferencesHelper,
                mZenModeHelper,
                mUsageStats,
                extractorNames);
        、、、
    }

我们看到NMS从Resource中获取了一个字符串数组extractorNames并且给到了RankingHelper的构造方法中,该数组定义在frameworks/base/core/res下的config.xml文件中。我们来看一下:

[config.xml]

    <!-- The list of classes that should be added to the notification ranking pipeline.
     See {@link com.android.server.notification.NotificationSignalExtractor}
      If you add a new extractor to this list make sure to update
      NotificationManagerService.handleRankingSort()-->
    <string-array name="config_notificationSignalExtractors">
        <!-- many of the following extractors depend on the notification channel, so this
        extractor must come first -->
        <item>com.android.server.notification.NotificationChannelExtractor</item>
        <item>com.android.server.notification.NotificationAdjustmentExtractor</item>
        <item>com.android.server.notification.BubbleExtractor</item>
        <!-- depends on AdjustmentExtractor-->
        <item>com.android.server.notification.ValidateNotificationPeople</item>
        <item>com.android.server.notification.PriorityExtractor</item>
        <!-- depends on PriorityExtractor -->
        <item>com.android.server.notification.ZenModeExtractor</item>
        <item>com.android.server.notification.ImportanceExtractor</item>
        <!-- depends on ImportanceExtractor-->
        <item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
        <item>com.android.server.notification.VisibilityExtractor</item>
        <!-- Depends on ZenModeExtractor -->
        <item>com.android.server.notification.BadgeExtractor</item>
        <item>com.android.server.notification.CriticalNotificationExtractor</item>

    </string-array>

数组内是一些全类名,这些类都实现了NotificationSignalExtractor接口。

三、RankingHelper 初始化

    public RankingHelper(Context context, RankingHandler rankingHandler, RankingConfig config,
            ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames) {
        mContext = context;
        mRankingHandler = rankingHandler;
        mPreliminaryComparator = new NotificationComparator(mContext);

        final int N = extractorNames.length;
        mSignalExtractors = new NotificationSignalExtractor[N];
        for (int i = 0; i < N; i++) {
            try {
                Class<?> extractorClass = mContext.getClassLoader().loadClass(extractorNames[i]);
                NotificationSignalExtractor extractor =
                        (NotificationSignalExtractor) extractorClass.newInstance();
                extractor.initialize(mContext, usageStats);
                extractor.setConfig(config);
                extractor.setZenHelper(zenHelper);
                mSignalExtractors[i] = extractor;
            } catch (ClassNotFoundException e) {
                Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e);
            } catch (InstantiationException e) {
                Slog.w(TAG, "Couldn't instantiate extractor " + extractorNames[i] + ".", e);
            } catch (IllegalAccessException e) {
                Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
            }
        }
    }

RankingHelper通过反射将存在extractorNames数组中的所有类实例化,并将这些实例初始化,存储到mSignalExtractors数组中。

四、RankingHelper .extractSignals

在前文Android通知之NotificationManagerService 源码分析中的2.4章节讲到NMS通过mRankingHelper调用了extractSignals与sort方法,这里看一下实现。

[RankingHelper .java]

    public void extractSignals(NotificationRecord r) {
        final int N = mSignalExtractors.length;
        for (int i = 0; i < N; i++) {
            NotificationSignalExtractor extractor = mSignalExtractors[i];
            try {
                RankingReconsideration recon = extractor.process(r);
                if (recon != null) {
                    mRankingHandler.requestReconsideration(recon);
                }
            } catch (Throwable t) {
                Slog.w(TAG, "NotificationSignalExtractor failed.", t);
            }
        }
    }

代码很简单就是循环遍历依次调用所有实例的process方法,下面是一些实现者说明。

BadgeExtractor:确定是否应为通知显示徽章

BubbleExtractor:确定是否可以为此通知显示气泡。

CriticalNotificationExtractor:设置通知记录的重要性。这用于允许绕过所有其他排名信号。在汽车用例中需要它来促进将紧急和警告通知置于所有其他通知之上。除非系统设置了汽车功能标志,否则它不会处理通知。

五、RankingHelper.sort

[RankingHelper .java]

    public void sort(ArrayList<NotificationRecord> notificationList) {
        final int N = notificationList.size();
        // clear global sort keys
        for (int i = N - 1; i >= 0; i--) {
            notificationList.get(i).setGlobalSortKey(null);
        }

        // rank each record individually
        Collections.sort(notificationList, mPreliminaryComparator);

        、、、
        // Do a second ranking pass, using group proxies
        Collections.sort(notificationList, mFinalComparator);
    }

排序就是用的集合排序,规则在mFinalComparator与mPreliminaryComparator两个Comparator实例里面。

mFinalComparator比较简单就是根据key用字符串规则排序,这里看下mPreliminaryComparator。主要看下compare方法。

[NotificationComparator.java]

public int compare(NotificationRecord left, NotificationRecord right) {
        final int leftImportance = left.getImportance();
        final int rightImportance = right.getImportance();
        final boolean isLeftHighImportance = leftImportance >= IMPORTANCE_DEFAULT;
        final boolean isRightHighImportance = rightImportance >= IMPORTANCE_DEFAULT;

        if (isLeftHighImportance != isRightHighImportance) {
            // by importance bucket, high importance higher than low importance
            return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance);
        }

        // If a score has been assigned by notification assistant service, use this service
        // rank results within each bucket instead of this comparator implementation.
        if (left.getRankingScore() != right.getRankingScore()) {
            return -1 * Float.compare(left.getRankingScore(), right.getRankingScore());
        }

        // first all colorized notifications
        boolean leftImportantColorized = isImportantColorized(left);
        boolean rightImportantColorized = isImportantColorized(right);

        if (leftImportantColorized != rightImportantColorized) {
            return -1 * Boolean.compare(leftImportantColorized, rightImportantColorized);
        }

        // sufficiently important ongoing notifications of certain categories
        boolean leftImportantOngoing = isImportantOngoing(left);
        boolean rightImportantOngoing = isImportantOngoing(right);

        if (leftImportantOngoing != rightImportantOngoing) {
            // by ongoing, ongoing higher than non-ongoing
            return -1 * Boolean.compare(leftImportantOngoing, rightImportantOngoing);
        }

        boolean leftMessaging = isImportantMessaging(left);
        boolean rightMessaging = isImportantMessaging(right);
        if (leftMessaging != rightMessaging) {
            return -1 * Boolean.compare(leftMessaging, rightMessaging);
        }

        // Next: sufficiently import person to person communication
        boolean leftPeople = isImportantPeople(left);
        boolean rightPeople = isImportantPeople(right);
        final int contactAffinityComparison =
                Float.compare(left.getContactAffinity(), right.getContactAffinity());

        if (leftPeople && rightPeople){
            // by contact proximity, close to far. if same proximity, check further fields.
            if (contactAffinityComparison != 0) {
                return -1 * contactAffinityComparison;
            }
        } else if (leftPeople != rightPeople) {
            // People, messaging higher than non-messaging
            return -1 * Boolean.compare(leftPeople, rightPeople);
        }

        if (leftImportance != rightImportance) {
            // by importance, high to low
            return -1 * Integer.compare(leftImportance, rightImportance);
        }

        // by contact proximity, close to far. if same proximity, check further fields.
        if (contactAffinityComparison != 0) {
            return -1 * contactAffinityComparison;
        }

        // Whether or not the notification can bypass DND.
        final int leftPackagePriority = left.getPackagePriority();
        final int rightPackagePriority = right.getPackagePriority();
        if (leftPackagePriority != rightPackagePriority) {
            // by priority, high to low
            return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
        }

        final int leftPriority = left.getSbn().getNotification().priority;
        final int rightPriority = right.getSbn().getNotification().priority;
        if (leftPriority != rightPriority) {
            // by priority, high to low
            return -1 * Integer.compare(leftPriority, rightPriority);
        }

        final boolean leftInterruptive = left.isInterruptive();
        final boolean rightInterruptive = right.isInterruptive();
        if (leftInterruptive != rightInterruptive) {
            return -1 * Boolean.compare(leftInterruptive, rightInterruptive);
        }

        // then break ties by time, most recent first
        return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
    }

首先按照重要性高优先级的优于低优先级,其次根据色彩,所有彩色通知优先,再按照持续性等。

本文由 mdnice 多平台发布

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值