Android 4.3开始系统开放了NotificationListenerService API,让应用管理通知提供了可能,本文系统描述清理大师通知管理模块的设计与实现方式,以及开发过程中遇到问题和解决方案。

[TOC]

功能背景

常驻通知栏

清理大师内部一直存在一个问题就是常驻进程可能在某些条件下被kill掉,导致一些依赖常驻进程的功能失效,典型的如自动清理。由于android系统在管理内存的时候按照Low memory killer 机制来决定需要杀掉哪些进程,当系统内存不足的时候,按照oom_adj的值来判断进程的重要程度,这个值越小,表示进程越重要。而常驻通知栏的使用,可以提升进程的重要程度,因为实现上是一个前台服务,系统会认为此服务对用户来说是重要的,能一定程度上降低被系统kill掉的可能性。基于此背景,加入常驻通知栏功能对清理大师

的功能完善上有促进作用,并且常驻通知栏提供一些小工具入口对于产品的活跃度提升也有一定帮助。

关于oom_adj的规则不在本文的讨论范围,这里不再赘述。

通知管理

基于4.3API开放的接口,我们可以对系统发送的通知进行管理。android上很多应用会发送各种各样的通知,当用户下拉statusbar,会看到很多乱七八糟的通知存在通知栏,有些用户会认为这是一件很烦人的事情,所以管理通知可以帮用户解决这个问题。

需求概述提供常驻通知栏入口;

通知栏每一项功能分别为:进入大师内部,加速,通知管理/闹钟,手电/wifi,系统设置;

通知栏样式适配;

通知拦截;根据系统api对通知取消,使通知不再显示在通知栏。

通知缓存;

缓存分为内存缓存与本地缓存,其中内存缓存带跳转信息,可以跳转到原通知的目的地,本地缓存不带跳转信息,只打开应用。

通知管理界面;

对收到的通知界面展示,提供删除和加白功能。

过滤规则;

运营白名单机制,系统应用,带进度的通知,空通知,已经缓存的通知。

前期调研

对于系统回调返回的StatusBarNotification对象,通过源码可以发现里面其实是对Notification对象包装了一下,另外携带了一些基本信息。

先来解决第一问题,怎么取消掉新收到的通知呢?NotificationListenerService提供了两个api,一个是取消所有通知(包括已经显示的),一个是根据参数取消单个通知,看上去第二个比较靠谱。写个demo试一下,果然是靠谱的,发送的新通知会被取消掉。根据经验换个5.0的系统试一下,果然是取消不掉的,又换API了?查官方文档,果然是换了:

This method was deprecated in API level 21.Use cancelNotification(String) instead. Beginning with LOLLIPOP this method will no longer cancel the notification. It will continue to cancel the notification for applications whose targetSdkVersion is earlier than LOLLIPOP.

5.0上不支持,改用cancelNotificatio(String)代替,5.0以下老方法还有效。那就适配一下加个判断解决。

接下来看第二个问题。既然对收到的通知已经取消了,怎么缓存呢?需求是要求通知要带跳转信息,分析下Notification的源码吧,看看我们能获取到什么信息。发现contentIntent,contentRemoteView比较关键。contentIntent是一个PendingIntent对象,光它的内部机制就可以单开一个博文来描述,这里粗暴的理解为四个字“异步激发”。通知我们知道是由NotificationManagerService来管理的,通常我们点击它会启动目标应用,这显然是一个跨进程调用,我们要找的跳转信息就存在pendingIntent中。查看它的源码,发现一个叫send的方法,写个demo验证一下,完美。

那么现在已经知道了,contentIntent这个对象是需要的,它携带了跳转信息。

继续分析notification源码,发现contentRemoteView,这是一个RemoteView对象。熟悉自定义通知栏的同学应该用过这个。这个对象就是你布局的通知栏的样式。查看它的源码,发现有个apply的方法,描述如下:

Inflates the view hierarchy represented by this object and applies all of the actions.

这句话提供了两个信息,第一可以通过这个方法inflate出布局信息,返回一个view;

另外一个是applies all of the actions。通过返回的view,我们可以得到通知栏的布局view,那么我们就可以在界面上还原出这个通知的原样了。

这里的actions很有用,后面解决一个问题就研究了它,这里先不赘述了。

好了,调研到这里,我们数据已经准备好了,接下来该缓存。第一反应,我能把这些数据持久化到本地就好了,这样数据不丢失,状态也能保存,之后需要的时候读取出来。听上去不错,但是一用就犯错,自定义的对象虽然实现了序列化接口,但是pendingIntent这个对象是个Parcelable对象,没法用java的序列化方式往磁盘写,它里面携带binder对象,并且自身没有实现Serializable,直接报错!此路不行。

既然这样,就只能分为两块儿缓存了。内存缓存+磁盘缓存的方式来处理。内存缓存的数据,携带了pendingInetent,可以跳转到目标应用,本地缓存只保存通知基本信息,调研基本上能满足功能需求。

设计思想

缓存系统

由于NotificationListenerService可以帮我们唤醒进程,并且需要有一份内存缓存,所以针对NotificationListenerService选择放在常驻进程中。缓存系统解决需要解决以下问题:

内存限制

出于占用内存的考虑,设计上缓存系统有上限为最新的200条通知,超过200条会移除最老的通知。

多线程问题

service在收到通知的时候,是在binder线程中,也就是说对于缓存系统,put操作是在binder线程中完成的。而UI可以对缓存操作,UI线程可以读取,修改缓存,所以同步问题是需要解决的。有以下两种方案:

1.读写锁,此方式不赘述。

2.任务队列。既然操作缓存不再一个线程,那么就让这些操作在同一个线程中排队完成。可以使用HandlerThread的任务队列来设计。

跨进程传输问题

由于数据在常驻进程,如果Activity也在常驻进程的话,每次开启界面退出后activity销毁内存释放得不是很及时,会导致常驻进程内存额外有UI的内存,所以对activity没有声明所属进程。那么当UI进程启动通知管理页面,需要跨进程取数据,需要用到AIDL方式。需要注意的是,binder在数据传输的时候数据大小限制为1M,而且是进程内部所有的binder通信共享的,所以当数据过大是传输出问题的。所以缓存系统需要提供分页传输,或者单个实体传输,避免使用集合传输。

补充:既然传递大数据有问题,那么为什么bitmap可以传输呢?这是因为binder传输的时候对底层数据的大小做了分类,对于大型数据如bitmap,使用Ashmem匿名共享内存的方式传输的。所以,我们有两种方案:

将数据转成bitmap格式,编码解码传输;

直接使用android提供的MemoryFile传输;

内存本地双缓存

为了设计上方便,对于本地缓存设计成内存缓存的快照。每次更新内存缓存,同步更新本地缓存一次。这样避免设计成两份缓存,需要合并数据的麻烦。

流程

通知管理的主要流程如下:

当收到一条通知的时候,会先经过一个过滤器,通过过滤器的通知,先取消掉它在通知栏的显示,然后进入缓存系统。

当界面上展示的时候,通过一个UIop的中间层访问缓存系统,并通过UIop对数据删除,加白等操作。

1st=>start: Start

e=>end: End

in=>operation: onNotificationPosted

filter=>operation: doFilter

cancle=>condition: cancle?

cache=>subroutine: cache

uiop=>operation: OP

UI=>subroutine: UI

st->in->filter->cancle

cancle(yes,right)->in

cancle(no)->cache

cache(right)->uiop(right)->UI

环境与配置

目前清理大师依赖了额外sdk jar包,新增的NotificationListenerService在4.3以上才有,工程编译版本是4.0,所以将SDK目录下platforms/android-19的android.jar解压找出需要的class文件。按照原包名路径重新打包进android-plus.jar中。

踩坑与填坑如何唯一区分一条通知?

NotificationListenerService服务启动后,只要有任何通知更新,都会收到一条StatusBarNotification,很多应用发送通知的id都是一个,根据包名与id共同判断无法做到通知唯一性,这样就不能保证进入缓存的通知实体能保证不漏、不重cache住所有收到的通知。如何来区分呢?如何来区分呢?经过一些应用尝试,统计如下这些字段共同判断能解决绝大部分。

1

2

3if (entity.id == this.id && entity.pkgName.equals(this.pkgName) && entity.title.equals(this.title) && entity.ticker.equals(this.ticker)) {

...

}

解决带进度的通知

带进度的通知,每次刷新的时候,NotificationListenerService都会收到一条通知,一般应用会有一个进度条,有些应用没有进度条,只有不断变化的百分比数字(titlle字段),当然每次收到的通知按照上面的逻辑是区分不出来的,还有的进度条跟百分比都有,这就需要来抓进度条了。如何判断通知带进度条?

前面留下一个actions的问题,这个action是我们用来抓取是否包含进度的关键。分析RemoteView源码,发现有一个mActions字段,记录了这个view包含的一些列操作,比如setText,setSize,setMax等。这个字段是隐藏的,需要反射来解析里面的数据,然后看是否包含setProgress方法,通过这个标记来确定是否包含进度条。

千牛通知栏常驻是什么意思_通知管理功能与常驻通知栏的设计与实现相关推荐

  1. Weex在千牛开放中的应用实践

    摘要:在2017年1月12日 Weex Conf 2017上,阿里巴巴商家事业部无线千牛团队的无灵结合阿里巴巴无限商家端的实际业务分享了Weex在千牛开放中的应用实践,本文分享了面对业务的各种挑战,无 ...

  2. 千牛,如何一步步修炼成阿里背后最有温度的生意好伙伴?

    今天,对于在阿里平台上经营生意的商家来说,阿里巴巴已不再是一家纯粹的电子商务平台,而是一家新零售的综合服务解决商.在阿里背后,一款基于无线移动端的一站式工作平台--千牛,从2013年诞生到今天,已经成 ...

  3. 千牛通知栏常驻是什么意思_店铺运营|内贸1688 店铺真正的权重是什么?

    想必大家都听说过店铺权重/单品权重/客户标签 这些名词.那到底是怎么操作的呢?  以下是简单的大纲: 店铺权重的拆解拆分里面的小指标,看看我们有没有操作的空间 单品的权重拆分里面的小指标,有哪些因素是 ...

  4. python实现千牛客服自动回复语_千牛客服自动回复话术

    淘宝千牛客服自动回复,如何设置成这种的,第一次询问时弹出这样的文字,买家想问的问题点击红字怎么进入 您如果是千牛,可以设置千牛机器人自动回复. 您截图中的提问选择方式实际意义不大,没有买家愿意这样的方 ...

  5. 千牛包表包下载_探索R中表包的gt语法

    千牛包表包下载 I love migrittr's forward-pipe %>% operator. IMO, it is just the one of the most fitting, ...

  6. python实现千牛客服自动回复语_千牛自动回复语大全

    千牛自动回复语大全 千牛自动回复语大全:对客户的疑问进行应答 1,亲.您真有眼光.这可是我们店主打产品哦.我能为您做些什么? 您还有什么需要?不必客气.没关系.这是我们应该做的.我明白了.好的.是的. ...

  7. python实现千牛客服自动回复语_淘宝客服自动回复语录(客服自动回复大全)

    淘宝客服自动回复语录(客服自动回复大全) 2020-12-05 09:42:32 共7个回答 谁能帮我弄下淘宝的自动回复.就是第一次收到买家信息自动回复什么语句比较好.我离开时回复什么语句比较好 鼠标 ...

  8. python实现千牛客服自动回复语_千牛自动回复设置话术

    淘宝千牛客服自动回复,如何设置成这种的,第一次询问时弹出这样的文字,买家想问的问题点击红字怎么进入 您如果是千牛,可以设置千牛机器人自动回复. 您截图中的提问选择方式实际意义不大,没有买家愿意这样的方 ...

  9. python实现千牛客服自动回复语_千牛自动回复话术

    千牛自动回复话术 对客户的疑问进行应答 1 ,亲.您真有眼光.这可是我们店主打产品哦.我能为您做些 什么 ? 您还有什么需要 ? 不必客气.没关系.这是我们应该做的.我明 白了.好的.是的.非常感谢 ...

最新文章

  1. Linux日志选项详解及日志服务器的实现
  2. java txt中文乱码,JAVA读取TXT文件 可解决中文乱码问题
  3. Java服务启动慢,JVM预热的问题,我在k8s上改进了
  4. 君威u0073故障码解决_顽疾修复过程,君威没倒挡的变速箱故障就是这样修好
  5. 辣味就直往眼睛的专业o2o资讯
  6. Python基础-day01
  7. hdu 1540 Tunnel Warfare (线段树维护左右最长连续区间)
  8. 无法登录谷歌账号,提示次浏览器或应用可能不安全
  9. 编译原理实验之词法分析
  10. 原来网站上可以这样嵌套动态google地图
  11. html5 audio duration,记一次vue中获取audio媒体总时长duration遇到的问题
  12. Timer源代码分析
  13. python在线翻译小程序_Python爬虫学习之翻译小程序
  14. 字符编码 unicode 及其在javascript 中的使用
  15. Homebrew安装与配置(Linux)
  16. 二叉树(Binary Tree)详解
  17. java连接高斯数据库,高斯数据库 (gaussDB) - 基于 JDBC 开发 (9)
  18. 俄勒冈健康与科学大学计算机,俄勒冈健康与科学大学排名
  19. 但愿能带给你们一丝丝的温暖
  20. 游戏服务器的压力测试应该如何进行

热门文章

  1. FATE框架学习笔记
  2. c++实现医院检验科排班程序
  3. KVM创建虚拟机设置桥接网络,使不同网段的宿主机所创建的虚拟机之间可以互相通信
  4. 浅谈NFT抵押借贷的三种模式:点对点、资金池和中心化模式
  5. 计算机产品属于第几类商标,水杯在商标商品和服务区分表中属于第几类商品?...
  6. js判断点在线上位置,距离各端点距离算法
  7. shadermaterial 实现大气层效果和辉光效果
  8. 《Photoshop Lightroom4 经典教程》—第1课1.3节Lightroom的模块
  9. 联发科芯片资料大全,datasheet、FAQ、参考设计等
  10. python django聊天系统