Android中的Notification机制深入理解

本文需要解决的问题

笔者最近正在做一个项目,里面需要用到 Android Notification 机制来实现某些特定需求。我正好通过这个机会研究一下 Android Notification 相关的发送逻辑和接收逻辑,以及整理相关的笔记。我研究 Notification 机制的目的是解决以下我在使用过程中所思考的问题:

  • 我们创建的 Notification 实例最终以什么样的方式发送给系统?
  • 系统是如何接收到 Notification 实例并显示的?
  • 我们是否能拦截其他 app 的 Notification 并获取其中的信息?

什么是 Android Notification 机制?

Notification,中文名翻译为通知,每个 app 可以自定义通知的样式和内容等,它会显示在系统的通知栏等区域。用户可以打开抽屉式通知栏查看通知的详细信息。在实际生活中,Android Notification 机制有很广泛的应用,例如 IM app 的新消息通知,资讯 app 的新闻推送等等。

源码分析

本文的源码基于 Android 7.0。

Notification 的发送逻辑

一般来说,如果我们自己的 app 想发送一条新的 Notification,可以参照以下代码:

NotificationCompat.Builder mBuilder =
  new NotificationCompat.Builder(this)
  .setSmallIcon(R.drawable.notification_icon)
  .setWhen(System.currentTimeMillis())
  .setContentTitle("Test Notification Title")
  .setContentText("Test Notification Content!");
Intent resultIntent = new Intent(this, ResultActivity.class);

PendingIntent contentIntent =
  PendingIntent.getActivity(
   this,
   0,
   resultIntent,
   PendingIntent.FLAG_UPDATE_CURRENT
  );
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
 (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(mId, mBuilder.build());

可以看到,我们通过 NotificationCompat.Builder 新建了一个 Notification 对象,最后通过 NotificationManager#notify() 方法将 Notification 发送出去。

NotificationManager#notify()

public void notify(int id, Notification notification)
{
 notify(null, id, notification);
}

// 省略部分注释
public void notify(String tag, int id, Notification notification)
{
 notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
}

/**
 * @hide
 */
public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
{
 int[] idOut = new int[1];
 INotificationManager service = getService();
 String pkg = mContext.getPackageName();
 // Fix the notification as best we can.
 Notification.addFieldsFromContext(mContext, notification);
 if (notification.sound != null) {
  notification.sound = notification.sound.getCanonicalUri();
  if (StrictMode.vmFileUriExposureEnabled()) {
   notification.sound.checkFileUriExposed("Notification.sound");
  }
 }
 fixLegacySmallIcon(notification, pkg);
 if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
  if (notification.getSmallIcon() == null) {
   throw new IllegalArgumentException("Invalid notification (no valid small icon): "
     + notification);
  }
 }
 if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
 final Notification copy = Builder.maybeCloneStrippedForDelivery(notification);
 try {
  // !!!
  service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
    copy, idOut, user.getIdentifier());
  if (localLOGV && id != idOut[0]) {
   Log.v(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
  }
 } catch (RemoteException e) {
  throw e.rethrowFromSystemServer();
 }
}

我们可以看到,到最后会调用 service.enqueueNotificationWithTag() 方法,这里的是 service 是 INotificationManager 接口。如果熟悉 AIDL 等系统相关运行机制的话,就可以看出这里是代理类调用了代理接口的方法,实际方法实现是在 NotificationManagerService 当中。

NotificationManagerService#enqueueNotificationWithTag()

@Override
public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
   Notification notification, int[] idOut, int userId) throws RemoteException {
 enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
   Binder.getCallingPid(), tag, id, notification, idOut, userId);
}

void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
  final int callingPid, final String tag, final int id, final Notification notification,
  int[] idOut, int incomingUserId) {
 if (DBG) {
  Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
    + " notification=" + notification);
 }
 checkCallerIsSystemOrSameApp(pkg);
 final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
 final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);

 final int userId = ActivityManager.handleIncomingUser(callingPid,
   callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
 final UserHandle user = new UserHandle(userId);

 // Fix the notification as best we can.
 try {
  final ApplicationInfo ai = getContext().getPackageManager().getApplicationInfoAsUser(
    pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
    (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
  Notification.addFieldsFromContext(ai, userId, notification);
 } catch (NameNotFoundException e) {
  Slog.e(TAG, "Cannot create a context for sending app", e);
  return;
 }

 mUsageStats.registerEnqueuedByApp(pkg);

 if (pkg == null || notification == null) {
  throw new IllegalArgumentException("null not allowed: pkg=" + pkg
    + " id=" + id + " notification=" + notification);
 }
 final StatusBarNotification n = new StatusBarNotification(
   pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
   user);

 // Limit the number of notifications that any given package except the android
 // package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
 if (!isSystemNotification && !isNotificationFromListener) {
  synchronized (mNotificationList) {
   if(mNotificationsByKey.get(n.getKey()) != null) {
    // this is an update, rate limit updates only
    final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
    if (appEnqueueRate > mMaxPackageEnqueueRate) {
     mUsageStats.registerOverRateQuota(pkg);
     final long now = SystemClock.elapsedRealtime();
     if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
      Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
        + ". Shedding events. package=" + pkg);
       mLastOverRateLogTime = now;
     }
     return;
    }
   }

   int count = 0;
   final int N = mNotificationList.size();
   for (int i=0; i<N; i++) {
    final NotificationRecord r = mNotificationList.get(i);
    if (r.sbn.getPackageName().equals(pkg) && r.sbn.getUserId() == userId) {
     if (r.sbn.getId() == id && TextUtils.equals(r.sbn.getTag(), tag)) {
      break; // Allow updating existing notification
     }
     count++;
     if (count >= MAX_PACKAGE_NOTIFICATIONS) {
      mUsageStats.registerOverCountQuota(pkg);
      Slog.e(TAG, "Package has already posted " + count
        + " notifications. Not showing more. package=" + pkg);
      return;
     }
    }
   }
  }
 }

 // Whitelist pending intents.
 if (notification.allPendingIntents != null) {
  final int intentCount = notification.allPendingIntents.size();
  if (intentCount > 0) {
   final ActivityManagerInternal am = LocalServices
     .getService(ActivityManagerInternal.class);
   final long duration = LocalServices.getService(
     DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
   for (int i = 0; i < intentCount; i++) {
    PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
    if (pendingIntent != null) {
     am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
    }
   }
  }
 }

 // Sanitize inputs
 notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
   Notification.PRIORITY_MAX);

 // setup local book-keeping
 final NotificationRecord r = new NotificationRecord(getContext(), n);
 mHandler.post(new EnqueueNotificationRunnable(userId, r));

 idOut[0] = id;
}

这里代码比较多,但通过注释可以清晰地理清整个逻辑:

  • 首先检查通知发起者是系统进程或者是查看发起者发送的是否是同个 app 的通知信息,否则抛出异常;
  • 除了系统的通知和已注册的监听器允许入队列外,其他 app 的通知都会限制通知数上限和通知频率上限;
  • 将 notification 的 PendingIntent 加入到白名单;
  • 将之前的 notification 进一步封装为 StatusBarNotification 和 NotificationRecord,最后封装到一个异步线程 EnqueueNotificationRunnable 中

这里有一个点,就是 mHandler,涉及到切换线程,我们先跟踪一下 mHandler 是在哪个线程被创建。

mHandler 是 WorkerHandler 类的一个实例,在 NotificationManagerService#onStart() 方法中被创建,而 NotificationManagerService 是系统 Service,所以 EnqueueNotificationRunnable 的 run 方法会运行在 system_server 的主线程。

NotificationManagerService.EnqueueNotificationRunnable#run()

@Override
public void run() {
 synchronized(mNotificationList) {
  // 省略代码
  if (notification.getSmallIcon() != null) {
   StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
   mListeners.notifyPostedLocked(n, oldSbn);
  } else {
   Slog.e(TAG, "Not posting notification without small icon: " + notification);
   if (old != null && !old.isCanceled) {
    mListeners.notifyRemovedLocked(n);
   }
   // ATTENTION: in a future release we will bail out here
   // so that we do not play sounds, show lights, etc. for invalid
   // notifications
   Slog.e(TAG, "WARNING: In a future release this will crash the app: " + n.getPackageName());
  }
  buzzBeepBlinkLocked(r);
 }
}
  1. 省略的代码主要的工作是提取 notification 相关的属性,同时通知 notification ranking service,有新的 notification 进来,然后对所有 notification 进行重新排序;
  2. 然后到最后会调用 mListeners.notifyPostedLocked() 方法。这里 mListeners 是 NotificationListeners 类的一个实例。

NotificationManagerService.NotificationListeners#notifyPostedLocked()  -> NotificationManagerService.NotificationListeners#notifyPosted()

public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
 // Lazily initialized snapshots of the notification.
 TrimCache trimCache = new TrimCache(sbn);
 for (final ManagedServiceInfo info: mServices) {
  boolean sbnVisible = isVisibleToListener(sbn, info);
  boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
  // This notification hasn't been and still isn't visible -> ignore.
  if (!oldSbnVisible && !sbnVisible) {
   continue;
  }
  final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
  // This notification became invisible -> remove the old one.
  if (oldSbnVisible && !sbnVisible) {
   final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
   mHandler.post(new Runnable() {
    @Override
    public void run() {
     notifyRemoved(info, oldSbnLightClone, update);
    }
   });
   continue;
  }
  final StatusBarNotification sbnToPost = trimCache.ForListener(info);
  mHandler.post(new Runnable() {
   @Override
   public void run() {
    notifyPosted(info, sbnToPost, update);
   }
  });
 }
}

private void notifyPosted(final ManagedServiceInfo info, final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
 final INotificationListener listener = (INotificationListener) info.service;
 StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
 try {
  listener.onNotificationPosted(sbnHolder, rankingUpdate);
 } catch (RemoteException ex) {
  Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
 }
}

调用到最后会执行 listener.onNotificationPosted() 方法。通过全局搜索得知,listener 类型是 NotificationListenerService.NotificationListenerWrapper 的代理对象。

NotificationListenerService.NotificationListenerWrapper#onNotificationPosted()

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());
 } 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();
  }
 }
}

这里在一开始会从 sbnHolder 中获取到 sbn 对象,sbn 隶属于 StatusBarNotificationHolder 类,继承于 IStatusBarNotificationHolder.Stub 对象。注意到这里捕获了一个 RemoteException,猜测涉及到跨进程调用,但我们不知道这段代码是在哪个进程中执行的,所以在这里暂停跟踪代码。

笔者之前是通过向系统发送通知的方式跟踪源码,发现走不通。故个人尝试从另一个角度入手,即系统接收我们发过来的通知并显示到通知栏这个方式入手跟踪代码。

系统如何显示 Notification,即对于系统端来说,Notification 的接收逻辑

系统显示 Notification 的过程,猜测是在 PhoneStatusBar.java 中,因为系统启动的过程中,会启动 SystemUI 进程,初始化整个 Android 显示的界面,包括系统通知栏。

PhoneStatusBar#start()  -> BaseStatusBar#start()

public void start() {
 // 省略代码
 // Set up the initial notification state.
 try {
  mNotificationListener.registerAsSystemService(mContext,
    new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
    UserHandle.USER_ALL);
 } catch (RemoteException e) {
  Log.e(TAG, "Unable to register notification listener", e);
 }
 // 省略代码
}

这段代码中,会调用 NotificationListenerService#registerAsSystemService() 方法,涉及到我们之前跟踪代码的类。我们继续跟进去看一下。

NotificationListenerService#registerAsSystemService()

public void registerAsSystemService(Context context, ComponentName componentName,
  int currentUser) throws RemoteException {
 if (mWrapper == null) {
  mWrapper = new NotificationListenerWrapper();
 }
 mSystemContext = context;
 INotificationManager noMan = getNotificationInterface();
 mHandler = new MyHandler(context.getMainLooper());
 mCurrentUser = currentUser;
 noMan.registerListener(mWrapper, componentName, currentUser);
}

这里会初始化一个 NotificationListenerWrapper 和 mHandler。由于这是在 SystemUI 进程中去调用此方法将 NotificationListenerService 注册为系统服务,所以在前面分析的那里:

NotificationListenerService.NotificationListenerWrapper#onNotificationPosted(),这段代码是运行在 SystemUI 进程,而 mHandler 则是运行在 SystemUI 主线程上的 Handler。所以,onNotificationPosted() 是运行在 SystemUI 进程中,它通过 sbn 从 system_server 进程中获取到 sbn 对象。下一步是通过 mHandler 处理消息,查看 NotificationListenerService.MyHandler#handleMessage() 方法,得知当 message.what 为 MSG_ON_NOTIFICATION_POSTED 时,调用的是 onNotificationPosted() 方法。

但是,NotificationListenerService 是一个抽象类,onNotificationPosted() 为空方法,真正的实现是它的实例类。

观察到之前 BaseStatusBar#start() 中,是调用了 mNotificationListener.registerAsSystemService() 方法。那么,mNotificationListener 是在哪里进行初始化呢?

BaseStatusBar.mNotificationListener#onNotificationPosted

private final NotificationListenerService mNotificationListener = new NotificationListenerService() {
 // 省略代码

 @Override
 public void onNotificationPosted(final StatusBarNotification sbn, final RankingMap rankingMap) {
  if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
  if (sbn != null) {
   mHandler.post(new Runnable() {
    @Override
    public void run() {
     processForRemoteInput(sbn.getNotification());
     String key = sbn.getKey();
     mKeysKeptForRemoteInput.remove(key);
     boolean isUpdate = mNotificationData.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) {
       removeNotification(key, rankingMap);
      } else {
       mNotificationData.updateRanking(rankingMap);
      }
      return;
     }
     if (isUpdate) {
      updateNotification(sbn, rankingMap);
     } else {
      addNotification(sbn, rankingMap, null /* oldEntry */ );
     }
    }
   });
  }
 }
 // 省略代码
}

通过上述代码,我们知道了在 BaseStatusBar.java 中,创建了 NotificationListenerService 的实例对象,实现了 onNotificationPost() 这个抽象方法;

onNotificationPost() 中,通过 handler 进行消息处理,最终调用 addNotification() 方法

PhoneStatusBar#addNotification()

@Override
public void addNotification(StatusBarNotification notification, RankingMap ranking, Entry oldEntry) {
 if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
 mNotificationData.updateRanking(ranking);
 Entry shadeEntry = createNotificationViews(notification);
 if (shadeEntry == null) {
  return;
 }
 boolean isHeadsUped = shouldPeek(shadeEntry);
 if (isHeadsUped) {
  mHeadsUpManager.showNotification(shadeEntry);
  // Mark as seen immediately
  setNotificationShown(notification);
 }
 if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
  if (shouldSuppressFullScreenIntent(notification.getKey())) {
   if (DEBUG) {
    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());
   }
  } else if (mNotificationData.getImportance(notification.getKey()) < NotificationListenerService.Ranking.IMPORTANCE_MAX) {
   if (DEBUG) {
    Log.d(TAG, "No Fullscreen intent: not important enough: " + notification.getKey());
   }
  } else {
   // Stop screensaver if the notification has a full-screen intent.
   // (like an incoming phone call)
   awakenDreams();
   // not immersive & a full-screen alert should be shown
   if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
   try {
    EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, notification.getKey());
    notification.getNotification().fullScreenIntent.send();
    shadeEntry.notifyFullScreenIntentLaunched();
    MetricsLogger.count(mContext, "note_fullscreen", 1);
   } catch (PendingIntent.CanceledException e) {}
  }
 }
 // !!!
 addNotificationViews(shadeEntry, ranking);
 // Recalculate the position of the sliding windows and the titles.
 setAreThereNotifications();
}

在这个方法中,最关键的方法是最后的 addNotificationViews() 方法。调用这个方法之后,你创建的 Notification 才会被添加到系统通知栏上。

总结

跟踪完整个过程中,之前提到的问题也可以一一解决了:

Q:我们创建的 Notification 实例最终以什么样的方式发送给系统?

A:首先,我们在 app 进程创建 Notification 实例,通过跨进程调用,传递到 system_server 进程的 NotificationManagerService 中进行处理,经过两次异步调用,最后传递给在 NotificationManagerService 中已经注册的 NotificationListenerWrapper。而 android 系统在初始化 systemui 进程的时候,会往 NotificationManagerService 中注册监听器(这里指的就是 NotificationListenerWrapper)。这种实现方法就是基于我们熟悉的一种设计模式:监听者模式。

Q:系统是如何获取到 Notification 实例并显示的?

A:上面提到,由于初始化的时候已经往 NotificationManagerService 注册监听器,所以系统 SystemUI 进程会接收到 Notification 实例之后经过进一步解析,然后构造出 Notification Views 并最终显示在系统通知栏上。

Q:我们是否能拦截 Notification 并获取其中的信息?

A:通过上面的流程,我个人认为可以通过 Xposed 等框架去 hook 其中几个重要的方法去捕获 Notification 实例,例如 hook NotificationManager#notify() 方法去获取 Notification 实例。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • android使用NotificationListenerService监听通知栏消息

    NotificationListenerService是通过系统调起的服务,在应用发起通知时,系统会将通知的应用,动作和信息回调给NotificationListenerService.但使用之前需要引导用户进行授权.使用NotificationListenerService一般需要下面三个步骤. 注册服务 首先需要在AndroidManifest.xml对service进行注册. <service android:name=".NotificationCollectorService&q

  • Android中AlarmManager+Notification实现定时通知提醒功能

    AlarmManager简介 AlarmManager实质是一个全局的定时器,是Android中常用的一种系统级别的提示服务,在指定时间或周期性启动其它组件(包括Activity,Service,BroadcastReceiver).本文将讲解一下如何使用AlarmManager实现定时提醒功能. 闹钟配置 周期闹钟 Intent intent = new Intent(); intent.setAction(GlobalValues.TIMER_ACTION_REPEATING); Pendi

  • Android中通过Notification&NotificationManager实现消息通知

    notification是一种让你的应用程序在没有开启情况下或在后台运行警示用户.它是看不见的程序组件(Broadcast Receiver,Service和不活跃的Activity)警示用户有需要注意的事件发生的最好途径. 1.新建一个android项目 我新建项目的 minSdkVersion="11",targetSdkVersion="19".也就是支持最低版本的3.0的. 2.习惯性地打开项目清单文件AndroidManifest.xml,添加一个权限:&

  • Android中通知Notification使用实例(振动、灯光、声音)

    本文实例讲解了通知Notification使用方法,此知识点就是用作通知的显示,包括振动.灯光.声音等效果,分享给大家供大家参考,具体内容如下 效果图: MainActivity: import java.io.File; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; im

  • Android 通知使用权(NotificationListenerService)的使用

    Android  通知使用权(NotificationListenerService)的使用 简介 当下不少第三方安全APP都有消息管理功能或者叫消息盒子功能,它们能管理过滤系统中的一些无用消息,使得消息栏更清爽干净.其实此功能的实现便是使用了Android中提供的通知使用权权限.Android4.3后加入了通知使用权NotificationListenerService,就是说当你开发的APP拥有此权限后便可以监听当前系统的通知的变化,在Android4.4后还扩展了可以获取通知详情信息.下面

  • Android 中Notification弹出通知实现代码

    NotificationManager 是状态栏通知的管理类,负责发通知.清除通知等操作. NotificationManager 是一个系统Service,可通过getSystemService(NOTIFICATION_SERVICE)方法来获取 接下来我想说的是android5.0 后的弹出通知, 网上的方法是: //第一步:实例化通知栏构造器Notification.Builder: Notification.Builder builder =new Notification.Build

  • Android种使用Notification实现通知管理以及自定义通知栏实例(示例四)

    示例一:实现通知栏管理 当针对相同类型的事件多次发出通知,作为开发者,应该避免使用全新的通知,这时就应该考虑更新之前通知栏的一些值来达到提醒用户的目的.例如我们手机的短信系统,当不断有新消息传来时,我们的通知栏仅仅是更改传来短信的数目,而不是对每条短信单独做一个通知栏用于提示. 修改通知 可以设置一条通知,当然可以更新一条通知,我们通过在调用NotificationManager.notify(ID, notification)时所使用的ID来更新它.为了更新你之前发布的通知,你需要更新或者创建

  • android中创建通知栏Notification代码实例

    ///// 第一步:获取NotificationManager NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); ///// 第二步:定义Notification Intent intent = new Intent(this, OtherActivity.class); //PendingIntent是待执行的Intent PendingIntent pi

  • 详解Android中Notification通知提醒

    在消息通知时,我们经常用到两个组件Toast和Notification.特别是重要的和需要长时间显示的信息,用Notification就最 合适不过了.当有消息通知时,状态栏会显示通知的图标和文字,通过下拉状态栏,就可以看到通知信息了,Android这一创新性的UI组件赢得了用户的一 致好评,就连苹果也开始模仿了.今天我们就结合实例,探讨一下Notification具体的使用方法.  首先说明一下我们需要实现的功能是:在程序启动时,发出一个通知,这个通知在软件运行过程中一直存在,相当于qq的托盘

  • Android开发 -- 状态栏通知Notification、NotificationManager详解

    本想自己写一个的,但是看到这篇之后,我想还是转过来吧,实在是非常的详细: 在Android系统中,发一个状态栏通知还是很方便的.下面我们就来看一下,怎么发送状态栏通知,状态栏通知又有哪些参数可以设置? 首先,发送一个状态栏通知必须用到两个类:  NotificationManager . Notification. NotificationManager :  是状态栏通知的管理类,负责发通知.清楚通知等. NotificationManager 是一个系统Service,必须通过 getSys

随机推荐