Android 通知设置

写在前面,8.0以后的通知写法如下

Android 8.0中各种通知写法汇总

最终通过 NotificationManager.notify()发送通知。

 

1、通知发送流程

相关类

frameworks/base/core/java/android/app/NotificationManager.java

frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

frameworks/base/core/java/android/service/notification/NotificationListenerService.java

packages/apps/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java

packages/apps/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java


NotificationManager notify() --> notifyAsUser  -->
NotificationManagerService enqueueNotificationWithTag --> enqueueNotificationInternal -->

void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
        final int callingPid, final String tag, final int id, final Notification notification,
        int incomingUserId) {
    ... ...
  
    final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);

    checkRestrictedCategories(notification);

    // Fix the notification as best we can.
    try {
        fixNotification(notification, pkg, userId);

    } catch (NameNotFoundException e) {
        Slog.e(TAG, "Cannot create a context for sending app", e);
        return;
    }

    mUsageStats.registerEnqueuedByApp(pkg);

    // setup local book-keeping
    String channelId = notification.getChannelId();
    if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
        channelId = (new Notification.TvExtender(notification)).getChannelId();
    }
    final NotificationChannel channel = mPreferencesHelper.getNotificationChannel(pkg,
            notificationUid, channelId, false /* includeDeleted */);
    if (channel == null) {
        ... ... //channel 为NULL,提示并阻止通知发送,所以应用发送通知必需先创建NotificationChannel
        return;
    }

    final StatusBarNotification n = new StatusBarNotification(
            pkg, opPkg, id, tag, notificationUid, callingPid, notification,
            user, null, System.currentTimeMillis());
    final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
    r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));

    ... ...

    mHandler.post(new EnqueueNotificationRunnable(userId, r));
}

 enqueueNotificationInternal方法内部做一些检查,判断通知是否合法等。

NotificationManagerService.EnqueueNotificationRunnable.run()  -->

NotificationManagerService.PostNotificationRunnable.run()   -->

NotificationManagerService.NotificationListeners.notifyPostedLocked()   -->

NotificationManagerService.NotificationListeners.notifyPosted()   -->

INotificationListener.onNotificationPosted()  -->

    /** @hide */
    protected class NotificationListenerWrapper extends INotificationListener.Stub {
        @Override
        public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
                NotificationRankingUpdate update) {
            StatusBarNotification sbn;
            try {
                sbn = sbnHolder.get();
            } catch (RemoteException e) {
                Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);
                return;
            }
        
            try {
                // convert icon metadata to legacy format for older clients
                createLegacyIconExtras(sbn.getNotification());
                maybePopulateRemoteViews(sbn.getNotification());
                maybePopulatePeople(sbn.getNotification());
            } catch (IllegalArgumentException e) {
                // warn and drop corrupt notification
                Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +
                        sbn.getPackageName());
                sbn = null;
            }
        
            // protect subclass from concurrent modifications of (@link mNotificationKeys}.
            synchronized (mLock) {
                applyUpdateLocked(update);
                if (sbn != null) {
                    SomeArgs args = SomeArgs.obtain();
                    args.arg1 = sbn;
                    args.arg2 = mRankingMap;
                    mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,
                            args).sendToTarget();
                } else {
                    // still pass along the ranking map, it may contain other information
                    mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
                            mRankingMap).sendToTarget();
                }
            }
        
        }
        ... ...
    }
    
    ... ...
    
    case MSG_ON_NOTIFICATION_POSTED: {
        SomeArgs args = (SomeArgs) msg.obj;
        StatusBarNotification sbn = (StatusBarNotification) args.arg1;
        RankingMap rankingMap = (RankingMap) args.arg2;
        args.recycle();
        onNotificationPosted(sbn, rankingMap);
    } break;

通过 MyHandler.MSG_ON_NOTIFICATION_POSTED,调用 NotificationListenerService.onNotificationPosted ()。

NotificationListener 继承 NotificationListenerWithPlugins 继承 NotificationListenerService。最终实现在NotificationListener中,

    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
        if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
            Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
                processForRemoteInput(sbn.getNotification(), mContext);
                String key = sbn.getKey();
                boolean isUpdate =
                        mEntryManager.getNotificationData().get(key) != null;
                // In case we don't allow child notifications, we ignore children of
                // notifications that have a summary, since` we're not going to show them
                // anyway. This is true also when the summary is canceled,
                // because children are automatically canceled by NoMan in that case.
                if (!ENABLE_CHILD_NOTIFICATIONS
                        && mGroupManager.isChildInGroupWithSummary(sbn)) {
                    if (DEBUG) {
                        Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
                    }

                    // Remove existing notification to avoid stale data.
                    if (isUpdate) {
                        mEntryManager.removeNotification(key, rankingMap, UNDEFINED_DISMISS_REASON);
                    } else {
                        mEntryManager.getNotificationData()
                                .updateRanking(rankingMap);
                    }
                    return;
                }
                if (isUpdate) {
                    mEntryManager.updateNotification(sbn, rankingMap);
                } else {
                    mEntryManager.addNotification(sbn, rankingMap);
                }
            });
        }
    }

最后由 NotificationEntryManager.addNotification() --> NotificationEntryManager.addNotificationInternal() 完成通知创建显示。

 

2、设置APP通知开启/关闭接口 

private void disableAppNotification(String packagename) {
    INotificationManager mNotificationManager = INotificationManager.Stub.asInterface(
            ServiceManager.getService(Context.NOTIFICATION_SERVICE));
    try {
        int uid = mPackageManagerService.getPackageUid(packagename, 0, UserHandle.USER_SYSTEM);
        //Slog.i("NotificationsEnabled", "PMS getPackageUid disable app pkg=" + packagename + ",uid=" + uid);
        mNotificationManager.setNotificationsEnabledForPackage(packagename, uid, false);
    } catch (Exception e) {
        Slog.e("NotificationsEnabled", "PMS Error calling NoMan" + e);
    }
}

NotificationManagerService.setNotificationsEnabledForPackage 设置APP默认通知状态,可在系统启动时或者APK安装时(监听 Intent.ACTION_PACKAGE_ADDED 安装结束广播)进行设置。

@Override
public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
    enforceSystemOrSystemUI("setNotificationsEnabledForPackage");

    mPreferencesHelper.setEnabled(pkg, uid, enabled);
    mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
            .setType(MetricsEvent.TYPE_ACTION)
            .setPackageName(pkg)
            .setSubtype(enabled ? 1 : 0));
    // Now, cancel any outstanding notifications that are part of a just-disabled app
    if (!enabled) {
        cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
                UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
    }

    try {
        getContext().sendBroadcastAsUser(
                new Intent(ACTION_APP_BLOCK_STATE_CHANGED)
                        .putExtra(NotificationManager.EXTRA_BLOCKED_STATE, !enabled)
                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
                        .setPackage(pkg),
                UserHandle.of(UserHandle.getUserId(uid)), null);
    } catch (SecurityException e) {
        Slog.w(TAG, "Can't notify app about app block change", e);
    }

    handleSavePolicyFile();
}

通知开启关闭后还会发送一个 ACTION_APP_BLOCK_STATE_CHANGED,应用中可以进行监听判断通知状态变化。

 

3、强制过滤APP通知

packages/apps/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java

packages/apps/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java

protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
        throws InflationException {
    if (CHATTY) {
        Log.d(TAG, "createNotificationViews(notification=" + sbn);
    }

    // 获取包名和flags是否是常驻通知
    String pkg = sbn.getPackageName();
    boolean isClearable = (sbn.getNotification().flags
            & Notification.FLAG_AUTO_CANCEL) == Notification.FLAG_AUTO_CANCEL;

    // 根据通知的包名或者flags来过滤
    if (!isClearable /*"com.xxx.xxx".equals(pkg)*/) {
        return null;
    }

    NotificationData.Entry entry = new NotificationData.Entry(sbn);
    Dependency.get(LeakDetector.class).trackInstance(entry);
    entry.createIcons(mContext, sbn);
    // Construct the expanded view.
    inflateViews(entry, mListContainer.getViewParentForNotification(entry));
    return entry;
}

private void addNotificationInternal(StatusBarNotification notification,
        NotificationListenerService.RankingMap ranking) throws InflationException {
    String key = notification.getKey();
    if (DEBUG) Log.d(TAG, "addNotification key=" + key);

    mNotificationData.updateRanking(ranking);
    NotificationData.Entry shadeEntry = createNotificationViews(notification);

    // 判断是否创建了通知
    if (shadeEntry == null) {
        return;
    }

    boolean isHeadsUped = shouldPeek(shadeEntry);
    
    ... ...
}

调用顺序是从 NotificationListener.onNotificationPosted  --> NotificationEntryManager.addNotification --> addNotificationInternal ,最终创建显示通知布局。通过 StatusBarNotification 对象的属性值获知通知的包名及通知的flags,判断是否需要过滤。

常用的通知的flags如下

public static final int FLAG_SHOW_LIGHTS        = 0x00000001;//设置闪光
public static final int FLAG_ONGOING_EVENT      = 0x00000002;//将flag设置为这个属性那么通知就会常驻
public static final int FLAG_INSISTENT          = 0x00000004;//重复发出声音,直到用户响应此通知 
public static final int FLAG_ONLY_ALERT_ONCE    = 0x00000008;//标记声音或者震动一次
public static final int FLAG_AUTO_CANCEL        = 0x00000010;//在通知栏上点击此通知后自动清除此通知 
public static final int FLAG_NO_CLEAR           = 0x00000020;//将flag设置为该值,则通知栏的清楚按钮不会出现
public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;//前台服务标记
public static final int FLAG_HIGH_PRIORITY =      0x00000080;//高权限,已过时

 

Android中,设置通知类别是为了组织和区分不同类型的通知,提升用户体验。你可以通过Notification渠道(Notification Channel)来实现这一点。以下是创建和管理通知类别的基本步骤: 1. **创建通道**: - 在`AndroidManifest.xml`文件中,添加一个新的`<notification-channel>`元素,定义通道的名称、图标、描述等属性。 ```xml <application> <!-- ... --> <meta-data android:name="android NOTIFY_CHANNEL_ID" android:value="@string/channel_id"/> <receiver android:name=".MyBroadcastReceiver"> <!-- ... --> </receiver> <!-- ... --> </application> ``` 然后在`strings.xml`中定义通道ID。 2. **动态创建通道**: 如果在运行时需要创建通道,可以在代码中这样做: ```java NotificationManager notificationManager = getSystemService(NotificationManager.class); String channelId = getString(R.string.channel_id); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { CharSequence name = "Your Channel Name"; String description = "Description of your channel"; int importance = NotificationManager.IMPORTANCE_DEFAULT; NotificationChannel channel = new NotificationChannel(channelId, name, importance); channel.setDescription(description); notificationManager.createNotificationChannel(channel); } ``` 3. **发送带通道的通知**: 当创建Notification时,指定所属的通道ID: ```java NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId) .setContentTitle("Title") .setContentText("Body"); // 发送通知 notificationManager.notify(notificationId, builder.build()); ``` 4. **处理用户交互**: 用户可以手动调整特定通道的设置,如关闭通知声音或震动等。监听用户的操作并相应地更新通知策略。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值