`

Linux Notification chains

 
阅读更多
Notifier是Linux 中提供一种在内核子系统 中共享事件信息的方法。
基于版本2.6.22,notifier有四种types:
1.Atomic notifier chains: 
    Chain callbacks run in interrupt/atomic context. Callouts are not allowed to block.
2.Blocking notifier chains: 
    Chain callbacks run in process context.Callouts are allowed to block.
3.Raw notifier chains: 
    There are no restrictions on callbacks,registration, or unregistration. All locking     and protection must be provided by the caller.
4.SRCU notifier chains: 
    A variant of blocking notifier chains, with the same restrictions.

  atomic_notifier_chain_register() may be called from an atomic context,but blocking_notifier_chain_register() and srcu_notifier_chain_register()must be called from a process context.  Ditto for the corresponding _unregister() routines.

  atomic_notifier_chain_unregister(), blocking_notifier_chain_unregister(),and srcu_notifier_chain_unregister() _must not_ be called from within the call chain.

  SRCU notifier chains are an alternative form of blocking notifier chains. They use SRCU (Sleepable Read-Copy Update) instead of rw-semaphores for protection of the chain links.  This means there is _very_ low overhead in srcu_notifier_call_chain(): no cache bounces and no memory barriers. As compensation, srcu_notifier_chain_unregister() is rather expensive. SRCU notifier chains should be used when the chain will be called very often but notifier_blocks will seldom be removed.  Also, SRCU notifier chains are slightly more difficult to use because they require special runtime initialization.

转:

Notification chain浅析

前 言
本 人正在学习《Understanding Linux Network Internals》一书,读到Notification Chains一章,感觉该机制挺有意思,就对该方面的资料进行了学习并简单整理,算是对前一段学习的小结。文中所用到的资料在参考文献中注明。本文中许多 描述以及用词会与参考资料的描述有较大重合。本人无意侵犯各参考资料的著作者,仅仅是为了知识共享以及交流。若哪位作者感觉本人对您有所冒犯,请通知本 人,本人立即改正。
谢谢!

1 概 述

内 核许多子系统之间关联紧密,因此在一个子系统发生或者检测到的事件信息很可能对其他子系统来说也是有价值的。为了满足其他子系统对这些事件信息的需求,即 在某个子系统内发生或检测到事件时,其他对此感兴趣的子系统也能知道事件的发生,内核提供了notification chain机制。
注意:notification chain适用于内核子系统之间的信息传递,不涉及用户态。
Notification chain使用发布-订阅模型(publish-and-subscribe model):在事件发生时,检测或产生事件的子系统作为主动一方通过通知函数来告知作为被动一方的订阅者(对此事件感兴趣的子系统)。这里有个额外要 求,订阅一方要提供callback函数以供发布方调用,当然,提供什么样的callback函数完全由订阅方决定。
订 阅者必须知道其他子系统提供了哪些事件通知支持,以选择可以订阅的事件通知;当然,订阅者本身也是一个子系统,因此也具有信息发布功能,因此它也要清楚本 系统内哪些事件对其他子系统是有价值的,即有哪些本系统内的事件发生时需要通知订阅者,但是子系统对谁订阅了事件通知以及为什么要订阅一无所知。
从某种意义上来说,notification chain实现了事件信息的共享

2 struct notifier_block结构

Notification chain由notifier block组成,其结构类型如下(include/linux/notifier.h):
struct notifier_block
{
int (*notifier_call)(struct notifier_block *self, unsigned long, void *);
struct notifier_block *next;
int priority;
};
notifier_call回调函数:self参数通常为notifier_block本身;unsigned long型参数表示发生的事件类型,因为一个chain可能支持多个事件,此参数用来对事件进行区分,在include/linux/notifier.h文件 中预订了一些事件常量,例如与netdevice相关的就有多个事件;void *用来存放私有信息,其具体信息取决于特定的事件
next指针:用于同一个chain中的notifier_block的链接
priority: 表示notifier_call函数的优先级,在事件发生时先调用高优先级的回调函数。实际上在chain中notifier block是根据优先级来进行排队的,高优先级的在前面,这样就可以容易地实现根据优先级来调用回调函数。同优先级的根据加入chain的顺序来排队,最 新加入的排在同优先级的最后。通常该字段取缺省值0,这样回调函数就根据加入chain的顺序来调用。该字段的用法可参考函数 notifier_chain_register的实现。
实际上notification chain就是一组函数列表。通常notification chain的名字的格式为xxx_chain、xxx_notifier_chain、 xxx_notifier_list,例如reboot_notifier_list。

2.1 回调函数notifier_call
int (*notifier_call)(struct notifier_block *self, unsigned long, void *);
前面已经讲过函数参数的含义,注意到该函数的返回值为int类型,这里就来看一下可能的返回值(include/linux/notifier.h文件中定义了这些常值):
NOTIFY_DONE 0x0000 对该事件不感兴趣(根据unsigned long参数)
NOTIFY_OK 0x0001 成功响应该事件
NOTIFY_STOP_MASK 0x8000 该回调函数返回后停止处理后续notifier block
NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002)
出错,回调函数返回后停止处理后续notifier block
NOTIFY_STOP (NOTIFY_OK|NOTIFY_STOP_MASK)
成功响应事件,回调函数返回后停止处理后续notifier block
注意,NOTIFY_STOP和NOTIFY_BAD的定义都包含了NOTIFY_STOP_MASK。

2.2 并发访问控制
在 kernel/sys.c文件中定义了对notification chain进行并发访问控制的读写锁notifier_lock。系统中对所有notification chain的并发访问都是由该锁来控制。子系统通常只在boot或者加载module时注册notifier block,即修改notification chain,而大多数时间仅以只读方式来访问,因此一个锁基本不会影响系统性能。

3 基 本 例 程

下面的基本例程位于kernel/sys.c文件中
要接收某些事件的通知需要先注册到支持这些事件的notification chain中:
int notifier_chain_register(struct notifier_block **list, struct notifier_block *n)
list为notification chain
n为当前子系统提供的notifier_block,其中指明了回调函数
该函数会根据notifier_block的优先级priority将n插入到list中合适位置
如果不想接受已订阅事件的通知,则需要取消订阅注册:
int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n)
nl为notification chain
n为当前子系统提供的notifier_block
当事件发生时,要通知订阅该事件的子系统:
int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v)
n为notification chain
val为事件类型,前面提到过一个chain可能支持多个事件,该参数用来对事件进行区分
v存放特定于事件的信息
该 函数会遍历chain,对chain中每个notifier block,以参数val和v调用其notifier_call函数,若notifier_call函数返回值中标志了 NOTIFY_STOP_MASK(如NOTIFY_BAD、NOTIFY_STOP),则函数停止处理,返回当前notifier block的返回值;否则返回chain中最后一个notifier block的返回值。
注意:多数子系统都定义了这些基本例程的封装函数,因此很少看到对这些函数的直接调用。例如后面的例子中用到的register_reboot_notifier和unregister_reboot_notifier就是简单的封装函数。

4 简 单 示 例

在 kernel/sys.c文件中定义了一个全局reboot_notifier_list,该chain用来挂接想在系统shutdown时执行的函数, 例如进行某种清理工作。前面提到过register_reboot_notifier和unregister_reboot_notifier是对 notifier_chain_register和notifier_chain_unregister简单封装,具体请参考实现,在此不再赘述。下面给 出一个简单的使用notification chain的module例子:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/notifier.h>
static int myreboot(struct notifier_block *self, unsigned long event, void *data)
{
printk(KERN_ALERT "Just a test! Event code: %li! System reboot now...", event);
return NOTIFY_OK;
}
static struct notifier_block myreboot_notifier = {
.notifier_call = myreboot,
};
static int myreboot_init(void)
{
register_reboot_notifier(&myreboot_notifier);
return 0;
}
static void myreboot_exit(void)
{
unregister_reboot_notifier(&myreboot_notifier);
}
module_init(myreboot_init);
module_exit(myreboot_exit);
该 模块向reboot_notifier_list注册了一个函数myreboot,该函数在系统reboot时会简单地打印一些信息(在系统 shutdown时也可以)。下面图中划红线的部分为函数myreboot的输出。注意到传递给myreboot的event参数值为1,而 include/linux/notifier.h文件中定义了与系统关机相关的一组事件常值,而1对应于事件SYS_DOWN。


Linux内核中通知块操作
Linux内核中通知块操作

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

1. 前言

notify是Linux内核中一种常用的事件回调处理机制,提供了基于优先级的回调链表处理功能。

以下内核代码版本为2.6.19.2。

2. 数据结构
/* include/linux/notifier.h */
// 基本的通知块结构
struct notifier_block {
// 回调函数
 int (*notifier_call)(struct notifier_block *, unsigned long, void *);
// 链表中的下一个结构, 这个一个单向链表
 struct notifier_block *next;
// 该块的优先级, 在链表中各个块是按此优先级值进行排序的, 值大的在链表前, 表明
// 相应回调函数执行的顺序
 int priority;
};

出来基本的通知块结构, 还定义了一些扩展的通知块结构:
// 原子通知头结构, 增加了一个锁来保证操作的原子性
struct atomic_notifier_head {
 spinlock_t lock;
 struct notifier_block *head;
};

// 阻塞通知头结构, 增加了一个读写信号灯
struct blocking_notifier_head {
 struct rw_semaphore rwsem;
 struct notifier_block *head;
};

// 原始通知头结构, 就是一个通知块指针
struct raw_notifier_head {
 struct notifier_block *head;
};

// srcu: Sleepable Read-Copy Update mechanism
// srcu通知头结构, 增加了锁和srcu结构
struct srcu_notifier_head {
 struct mutex mutex;
 struct srcu_struct srcu;
 struct notifier_block *head;
};

以下是一些宏来初始化各种类型的通知头结构, 一般在程序中使用:
#define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \
  spin_lock_init(&(name)->lock); \
  (name)->head = NULL;  \
 } while (0)
#define BLOCKING_INIT_NOTIFIER_HEAD(name) do { \
  init_rwsem(&(name)->rwsem); \
  (name)->head = NULL;  \
 } while (0)
#define RAW_INIT_NOTIFIER_HEAD(name) do { \
  (name)->head = NULL;  \
 } while (0)
 
以下这些宏也是用来初始化各种类型的通知头结构, 但是在参数定义时使用:
#define ATOMIC_NOTIFIER_INIT(name) {    \
  .lock = __SPIN_LOCK_UNLOCKED(name.lock), \
  .head = NULL }
#define BLOCKING_NOTIFIER_INIT(name) {    \
  .rwsem = __RWSEM_INITIALIZER((name).rwsem), \
  .head = NULL }
#define RAW_NOTIFIER_INIT(name) {    \
  .head = NULL }
注意, 没有定义scru通知头结构的初始化, 因为scru是不能静态初始化的.
 
以下这些宏用来直接定义通知头结构:
#define ATOMIC_NOTIFIER_HEAD(name)    \
 struct atomic_notifier_head name =   \
  ATOMIC_NOTIFIER_INIT(name)
#define BLOCKING_NOTIFIER_HEAD(name)    \
 struct blocking_notifier_head name =   \
  BLOCKING_NOTIFIER_INIT(name)
#define RAW_NOTIFIER_HEAD(name)     \
 struct raw_notifier_head name =    \
  RAW_NOTIFIER_INIT(name)
 
3. 基本通知块操作函数

关于通知块的基本操作函数都在kernel/sys.c中定义

3.1 登记

该函数将一个通知块结构挂接到指定的通知链表
/*
 * Notifier chain core routines.  The exported routines below
 * are layered on top of these, with appropriate locking added.
 */
// nl是链表头块的地址, n是要添加到该链表的通知块
static int notifier_chain_register(struct notifier_block **nl,
  struct notifier_block *n)
{
// 使用的是dummy header算法, 即使刚开始时链表为空也不用显示判断区分
 while ((*nl) != NULL) {
// 判断优先权值, 优先权值越大位置越靠前
  if (n->priority > (*nl)->priority)
   break;
  nl = &((*nl)->next);
 }
// 将节点n链接到链表nl中的合适位置
 n->next = *nl;
// 使用rcu处理函数保证SMP下的安全性, 相当于加上锁再赋值
 rcu_assign_pointer(*nl, n);
 return 0;
}

3.2 撤销
该函数将一个通知块结构从通知链表中拆除:
// nl是链表头块的地址, n是要删除的通知块
static int notifier_chain_unregister(struct notifier_block **nl,
  struct notifier_block *n)
{
 while ((*nl) != NULL) {
// 地址匹配, 进行拆除操作
  if ((*nl) == n) {
// *nl=n->next的安全赋值操作,相当于将节点从链表断开
   rcu_assign_pointer(*nl, n->next);
   return 0;
  }
  nl = &((*nl)->next);
 }
 return -ENOENT;
}

3.3 回调函数处理

该函数执行链表中各节点的回调函数:

// nl通常是通知块链表头的地址, val和v是传给回调函数的参数
static int __kprobes notifier_call_chain(struct notifier_block **nl,
  unsigned long val, void *v)
{
 int ret = NOTIFY_DONE;
 struct notifier_block *nb, *next_nb;
// 安全地获取通知块指针
 nb = rcu_dereference(*nl);
// 链表循环
 while (nb) {
// 找下一个块
  next_nb = rcu_dereference(nb->next);
// 调用回调函数
  ret = nb->notifier_call(nb, val, v);
// 如果返回停止标志, 不执行后续结构
  if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
   break;
// 循环到下一节点
  nb = next_nb;
 }
 return ret;
}

4. 扩展的通知块操作

扩展的通知块操作功能和基本通知块类似, 但使用了扩展的结构中的参数保证操作的安全

4.1 原子通知块

4.1.1 登记
/*
 * Atomic notifier chain routines.  Registration and unregistration
 * use a spinlock, and call_chain is synchronized by RCU (no locks).
 */
/**
 * atomic_notifier_chain_register - Add notifier to an atomic notifier chain
 * @nh: Pointer to head of the atomic notifier chain
 * @n: New entry in notifier chain
 *
 * Adds a notifier to an atomic notifier chain.
 *
 * Currently always returns zero.
 */
// 只是在基本通知登记操作前后加锁解锁进行保护
int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
  struct notifier_block *n)
{
 unsigned long flags;
 int ret;
// 加锁
 spin_lock_irqsave(&nh->lock, flags);
 ret = notifier_chain_register(&nh->head, n);
// 解锁
 spin_unlock_irqrestore(&nh->lock, flags);
 return ret;
}
EXPORT_SYMBOL_GPL(atomic_notifier_chain_register);

4.1.2 撤销
/**
 * atomic_notifier_chain_unregister - Remove notifier from an atomic notifier chain
 * @nh: Pointer to head of the atomic notifier chain
 * @n: Entry to remove from notifier chain
 *
 * Removes a notifier from an atomic notifier chain.
 *
 * Returns zero on success or %-ENOENT on failure.
 */
// 只是在基本通知块撤销操作前后加锁解锁进行保护
int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
  struct notifier_block *n)
{
 unsigned long flags;
 int ret;
// 加锁
 spin_lock_irqsave(&nh->lock, flags);
 ret = notifier_chain_unregister(&nh->head, n);
// 解锁
 spin_unlock_irqrestore(&nh->lock, flags);
// 同步rcu, 等待一个grace period
 synchronize_rcu();
 return ret;
}
EXPORT_SYMBOL_GPL(atomic_notifier_chain_unregister);

4.1.3 原子回调

这个函数是在原子操作上下文中调用, 是不能阻塞的
 
int __kprobes atomic_notifier_call_chain(struct atomic_notifier_head *nh,
  unsigned long val, void *v)
{
 int ret;
// 禁止了抢占
 rcu_read_lock();
// 使用基本通知块回调
 ret = notifier_call_chain(&nh->head, val, v);
// 允许抢占
 rcu_read_unlock();
 return ret;
}
EXPORT_SYMBOL_GPL(atomic_notifier_call_chain);

4.2 可阻塞通知块

4.2.1 登记
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
  struct notifier_block *n)
{
 int ret;
 /*
  * This code gets used during boot-up, when task switching is
  * not yet working and interrupts must remain disabled.  At
  * such times we must not call down_write().
  */
// 这是内核启动时就进行调用了, 虽然可能性很小, 直接执行基本登记函数
// 不用处理信号灯, 因为此时是不能阻塞
 if (unlikely(system_state == SYSTEM_BOOTING))
  return notifier_chain_register(&nh->head, n);
// 使用信号灯进行同步, 可能阻塞
 down_write(&nh->rwsem);
// 基本登记函数
 ret = notifier_chain_register(&nh->head, n);
 up_write(&nh->rwsem);
 return ret;
}
EXPORT_SYMBOL_GPL(blocking_notifier_chain_register);

4.2.2 撤销
该函数是在进程处理过程中调用,可阻塞:
int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
  struct notifier_block *n)
{
 int ret;
 /*
  * This code gets used during boot-up, when task switching is
  * not yet working and interrupts must remain disabled.  At
  * such times we must not call down_write().
  */
// 这是内核启动时就进行调用了, 虽然可能性很小, 直接执行基本撤销函数
// 不用处理信号灯, 因为此时是不能阻塞
 if (unlikely(system_state == SYSTEM_BOOTING))
  return notifier_chain_unregister(&nh->head, n);
// 使用信号灯进行同步, 可能阻塞
 down_write(&nh->rwsem);
// 基本撤销函数
 ret = notifier_chain_unregister(&nh->head, n);
 up_write(&nh->rwsem);
 return ret;
}
EXPORT_SYMBOL_GPL(blocking_notifier_chain_unregister);

4.2.3 回调

在进行上下文中调用, 可以阻塞:
 
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
  unsigned long val, void *v)
{
 int ret;
// 信号灯同步
 down_read(&nh->rwsem);
// 进行基本回调处理
 ret = notifier_call_chain(&nh->head, val, v);
 up_read(&nh->rwsem);
 return ret;
}
EXPORT_SYMBOL_GPL(blocking_notifier_call_chain);

4.3 原始通知块操作

和基本原始块操作完全相同:

int raw_notifier_chain_register(struct raw_notifier_head *nh,
  struct notifier_block *n)
{
 return notifier_chain_register(&nh->head, n);
}
EXPORT_SYMBOL_GPL(raw_notifier_chain_register);
int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
  struct notifier_block *n)
{
 return notifier_chain_unregister(&nh->head, n);
}
EXPORT_SYMBOL_GPL(raw_notifier_chain_unregister);

int raw_notifier_call_chain(struct raw_notifier_head *nh,
  unsigned long val, void *v)
{
 return notifier_call_chain(&nh->head, val, v);
}
EXPORT_SYMBOL_GPL(raw_notifier_call_chain);

4.4 SRCU通知块操作

4.4.1 登记
必须在进程的上下文中调用, 和blocking通知类似
int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
  struct notifier_block *n)
{
 int ret;
 /*
  * This code gets used during boot-up, when task switching is
  * not yet working and interrupts must remain disabled.  At
  * such times we must not call mutex_lock().
  */
 if (unlikely(system_state == SYSTEM_BOOTING))
  return notifier_chain_register(&nh->head, n);
 mutex_lock(&nh->mutex);
 ret = notifier_chain_register(&nh->head, n);
 mutex_unlock(&nh->mutex);
 return ret;
}
EXPORT_SYMBOL_GPL(srcu_notifier_chain_register);

4.4.2 撤销

必须在进程的上下文中调用, 和blocking通知类似
int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
  struct notifier_block *n)
{
 int ret;
 /*
  * This code gets used during boot-up, when task switching is
  * not yet working and interrupts must remain disabled.  At
  * such times we must not call mutex_lock().
  */
 if (unlikely(system_state == SYSTEM_BOOTING))
  return notifier_chain_unregister(&nh->head, n);
 mutex_lock(&nh->mutex);
 ret = notifier_chain_unregister(&nh->head, n);
 mutex_unlock(&nh->mutex);
 synchronize_srcu(&nh->srcu);
 return ret;
}
EXPORT_SYMBOL_GPL(srcu_notifier_chain_unregister);

4.4.3 回调

在进程的上下文中调用, 可以阻塞:
int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
  unsigned long val, void *v)
{
 int ret;
 int idx;
// 使用srcu读锁来加锁
 idx = srcu_read_lock(&nh->srcu);
 ret = notifier_call_chain(&nh->head, val, v);
 srcu_read_unlock(&nh->srcu, idx);
 return ret;
}
EXPORT_SYMBOL_GPL(srcu_notifier_call_chain);
4.4.4 初始化
因为SRCU通知不能通过宏来初始化, 必须要专门定义一个初始化函数来初始化srcu的通知块参数:
void srcu_init_notifier_head(struct srcu_notifier_head *nh)
{
// 初始化锁
 mutex_init(&nh->mutex);
// 初始化scru结构
 if (init_srcu_struct(&nh->srcu) < 0)
  BUG();
 nh->head = NULL;
}
EXPORT_SYMBOL_GPL(srcu_init_notifier_head);

5. 应用

下面以连接跟踪中的事件处理来说明, 就是通过通知块来实现的:
初始化定义一个静态的原子通知头参数:
/* net/ipv4/netfilter/ip_conntrack_core.c */
ATOMIC_NOTIFIER_HEAD(ip_conntrack_chain);

连接跟踪的事件处理函数, 实际就是通知回调函数:
/* include/linux/netfilter_ipv4/ip_conntrack.h */
// 连接事件处理
static inline void ip_conntrack_event(enum ip_conntrack_events event,
          struct ip_conntrack *ct)
{
// 判断连接是否合法
 if (is_confirmed(ct) && !is_dying(ct))
// 调用原子通知回调函数, 执行登记的回调函数
  atomic_notifier_call_chain(&ip_conntrack_chain, event, ct);
}

连接跟踪相关事件的登记和撤销:
/* include/linux/netfilter_ipv4/ip_conntrack.h */
// 就是标准的原子通知登记和撤销函数
static inline int ip_conntrack_register_notifier(struct notifier_block *nb)
{
 return atomic_notifier_chain_register(&ip_conntrack_chain, nb);
}
static inline int ip_conntrack_unregister_notifier(struct notifier_block *nb)
{
 return atomic_notifier_chain_unregister(&ip_conntrack_chain, nb);
}
在net/ipv4/netfilter/ip_conntrack_netlink.c中定义了netlink通知回调函数:
static struct notifier_block ctnl_notifier = {
 .notifier_call = ctnetlink_conntrack_event,
};
......
 ret = ip_conntrack_register_notifier(&ctnl_notifier);
......
这样, 在任何地方ip_conntrack_event()函数时就会调用到该netlink通知回调函数.

6. 结论

notify通知块处理是内核中的一种回调处理机制, 一般不是直接调用原始的通知处理函数, 而是根据
要完成的功能, 如事件回调, 重启回调等重新定义新的处理函数, 然后在必要的地方调用相应的回调
包装函数就可以实现回调。

分享到:
评论

相关推荐

    Linux Notification chains--1

    ### Linux Notification Chains详解 #### 一、概述 Linux内核中的`Notification Chains`是一种机制,用于在内核的不同子系统间共享事件信息。这种机制基于发布-订阅模型,其中一个子系统(发布者)检测到事件时,...

    linux notification chains

    ### Linux Notification Chains详解 Linux内核作为开源操作系统的核心,其设计精妙且高效,通过将内核划分为多个独立但相互协作的子系统,如网络、存储管理等,形成了一个高度模块化的架构。这种设计灵感来源于ISO/...

    Notification

    在Android开发中,`Notification`是用户界面的一个关键组件,用于在状态栏向用户显示重要的信息或提醒。在"疯狂Android中有关Notification的简单例子"这个主题中,我们将深入探讨`Notification`的基本概念、创建过程...

    Notification最新用法、实现Notification的通知栏常驻、Notification的big View、解决Notification点击无效

    在Android开发中,Notification是应用与用户交互的重要方式,它能够在状态栏显示消息,即使用户不在应用程序中也能接收到信息。本教程将深入探讨Notification的最新用法,如何实现通知栏常驻,以及如何利用big View...

    Notification示例

    本示例着重讲解了如何创建和使用不同类型的Notification,包括普通Notification、折叠式Notification以及悬挂式Notification,并涉及到Notification的显示等级设置。 1. **普通Notification**: 这是最基础的...

    android notification完全解析Demo

    在Android开发中,Notification是应用与用户交互的重要方式,它能够在状态栏中显示信息,即使用户不在应用程序中也能提醒用户有新的活动或消息。本文将深入解析Android Notification的工作原理、设计模式以及如何...

    Notification的示例源码

    在Android开发中,`Notification`是用户界面的一个关键组件,用于在状态栏中显示消息,即使应用程序在后台运行,也能提醒用户有新的活动或事件发生。`Notification`的设计旨在提供一致且非侵入性的用户体验,使得...

    实现Notification的通知栏常驻

    在Android系统中,Notification是应用与用户交互的重要方式之一,特别是在后台运行时,它能向用户提供关键信息。常驻Notification是指即使用户关闭了应用程序,Notification仍然保留在通知栏,持续提醒用户有未处理...

    Ext JS Notification 插件

    在Ext JS中,“Notification”插件是用于显示通知消息的一个组件,它可以帮助开发者在用户界面上创建吸引人且易于理解的提示信息。本文将深入探讨Ext JS Notification插件的使用方法、功能特性以及如何集成到项目中...

    linux_notification_center:Linux的通知守护程序中心

    "linux_notification_center"可能是指一个开源项目,它可能是基于GTK+或者Haskell开发的,用于提供一个通用的通知中心解决方案。 首先,让我们了解Linux通知守护程序的工作原理。通知守护程序通过DBus消息总线与...

    Notification顶部通知栏demo

    在Android开发中,`Notification`是系统提供的一种机制,它能够在状态栏或者顶部通知栏显示信息,即使应用在后台运行或者被用户关闭,仍然能够向用户传达关键信息。本示例"Notification顶部通知栏demo"显然是为了...

    Notification Demo

    在Android开发中,Notification是应用与用户交互的重要方式之一,特别是在后台运行时,它能向用户提供关键信息,而不会打扰到他们的主要活动。"Notification Demo"是一个示例项目,专门展示了如何在Android应用中...

    Android notification+Service实时更新

    在Android开发中,`Notification`、`Service`和`BroadcastReceiver`是三个核心组件,它们在许多场景下都有着重要的作用,特别是在实现应用后台运行、实时更新等任务时。本项目"Android notification+Service实时更新...

    Android NOtification 使用

    在Android系统中,Notification是应用与用户交互的重要方式,它能提醒用户有新的事件或信息需要处理,即使应用不在前台运行。Notification分为多种类型,包括Toast、StatusBar Notification和Dialog Notification,...

    Notification的实用技巧

    在Android开发中,Notification是应用与用户交互的重要方式,它能够在状态栏中显示消息,即使应用在后台运行或用户没有直接与应用交互时,也能提醒用户有新的活动或信息。Notification的实用技巧涵盖了许多方面,...

    Notification的使用示例各种效果

    在Android开发中,Notification是应用与用户交互的重要方式,它可以在状态栏中显示消息,即使应用在后台运行也能提醒用户。本示例主要探讨如何利用Notification API创建各种效果的提示,包括系统默认样式以及自定义...

    javascript 实现 Notification 消息通知框

    使用javascript 封装实现 Notification 消息通知框

    android Notification使用大全

    在Android系统中,Notification是应用与用户交互的重要方式,它可以在状态栏中显示信息,即使用户不在使用应用时也能提醒用户有新的事件发生。本文将深入探讨如何在Android中使用Notification,包括基本用法、自定义...

Global site tag (gtag.js) - Google Analytics