去年做我们这个产品的时候,SE在客户端设计了一个推送功能,SE经过调研决定在Android和IOS端都集成百度的云推送SDK来支持这个推送功能。最近领导在做运营分析的时候,发现云推送的报表显示,在Android端消息的达到率非常低,设备的在线率波动比较大,有时高有时非常低。
我们的这个产品经过将近两年的折腾进步是有目共睹的,在今年的巴塞罗那GSMA世界移动通信大会上荣获“Best Mobile Music App”大奖,让我们这帮苦逼了将近两年的屌丝士气大振,领导也欣喜不已并且决定将精品策略进行到底,任何影响我们产品发展的因素都不可能容忍的,而推送功能无疑对产品的推广和用户的留存都是非常重要,现在发现这种现象,领导当然容忍不了,于是决定分析原因,我“不幸”被点将了,领导还要求周五下班前给出分析结论。
接到这个任务有点小迷茫,虽然我们的产品集成百度的推送SDK已经快一年了,但是我并没有深入研究过,之前一直是专人负责的,现在负责的人已经不在我们团队了,而且时间比较短,周三下班的时候下的任务,也就是说只有两天的时间。当然迷茫归迷茫,事儿还是要做问题还是要解决。
首先先快速的学习一下SDK的集成方式,好在这个SDK使用起来还是比较简单的,基本上照着文档做进行了,而且百度还提供了Demo供参考。当然要解决问题光知道怎么用肯定是不行的,接下来就要开始分析问题了。
要分析消息到达率低,最好能知道哪些手机接收消息有问题,幸运的是我们的测试发现在他的MATE7手机收不到我们应用的推送消息,即使打开了我们的app也不行,而且还有一个奇怪的现象,在他手机上打开过一款“掌上营业厅”应用之后,消息接收就正常了。
这个现象比较奇怪,由于我对这个SDK不熟,所以当时认为最快捷的方式应该直接问百度的工程师,百度也提供了客服答疑,并且还有些专门讨论推送SDK的QQ群,但是结果比较失望,QQ群里提问题基本上没有人理,而且里面的人很多我问的问题很快就被淹没了,在开发者网站问客服问题,反应非常慢,头一天晚上问的问题第二天下午才给回复,而且给的答案不痛不痒,看来此路不通,就算客服能帮我解决问题估计时间也早过了。后来想到有大学同学在度娘做工程师,于是赶紧联系,让他帮忙找找看看能不能在内部做SDK的工程师帮我解答一下。同学利用他的关系终于找到了push团队的工程师,赶紧微信加之,那帅哥提议加Hi,于是加Hi,当时在Hi上聊人家后来压根就不搭理了,后来想可能是太晚了,明天早上在说吧。第二天早上电脑上Hi发消息,照样没理,可能度娘的工作比较饱满人家没用闲暇功夫管我们这些根本不认识的人吧。
看来还得靠自己了,于是尝试把SDK jar包反编译,通过代码看看里面的工作原理,但是非常遗憾的是jar包已经混淆过了阅读非常困难,而且核心代码并不在jar包里面而是在so库里面没法反编译。
看代码不行就先看文档吧,于是我带着我的疑问仔细的看文档里面的每一个字,终于我在文档里面发现了两个重要的线索:
百度push SDK采用了单服务单通道策略,即如果手机上安装了多个集成了push SDK的应用时这些应用会共享同一个服务和长连接,目的是为了给用户省电省流量,好的看到这里终于解决了我的第一个疑问:为什么启动了“掌上营业厅”之后我们的应用就能收到推送消息了,因为“掌上营业厅”也集成了百度的推送SDK,通过ps命令查看手机的进程确实发现有一个百度相关的进程(问题是在公司定位,博客是在家写的,所以没有把截图带出来)。
还有一个重要线索是,在文档里面提到了最新的SDK版本解决了一些高系统版本手机的适配问题,最新的SDK版本是4.4.0,我看看我们应用集成的版本是3.2.0,显然这个版本已经很老了,而这个MATE7手机是最近新出的手机,所以有没有可能是因为版本问题呢?赶紧动手试一下,决定先用新版本的Demo试一下,在试之前为了对比先用老版本的Demo测试一下看看在该手机有没有问题,经过测试是有问题的。接下来用新版本的Demo试一下,惊喜的发现用新的Demo能收到消息了,于是马上把新版本SDK集成到我们的工程里面,代码不用变只需修改manifest中的配置,同老版本相比,新版本需要给PushService增加一个intent-filter
action,并且增加一个Service声明CommandService:
<service android:name="com.baidu.android.pushservice.PushService" android:exported="true" android:process=":bdservice_v1" > <intent-filter > <action android:name="com.baidu.android.pushservice.action.PUSH_SERVICE"/> </intent-filter> </service> <!-- 4.4版本新增的CommandService声明,提升小米和魅族手机上的实际推送到达率 --> <service android:name="com.baidu.android.pushservice.CommandService" android:exported="true" /> <!-- push结束 -->
修改之后运行工程(测试的时候要重启手机,开机之后只要有百度推送系的应用运行过我们的应用时可以收到消息的),终于我们的应用也能收到消息,但是还是有个问题,当开机重启之后第一执行我们的应用时,无法收到消息,而且上应用管理查看push服务并没有被启动。在一筹莫展之际在运行日志中发现下面几行可疑日志(因为某些原因比较敏感的信息叉掉了):
03-19 19:57:25.822 D/Utility(7056): Find more higher priority pkg:com.baidu.BaiMap priority = 408,Current highest pkg: com.smart.softclient.music.baseline priority = 0 03-19 19:57:25.822 D/Utility(7056): Current highest priority Push PackageName:com.baidu.BaiMap 03-19 19:57:25.822 D/PushSDK(7056): Current push service:com.smart.softclient.music.baseline should stop!!! highest priority service is: com.baidu.BaiMap 03-19 19:57:25.822 D/PushService(7056): stopSelf:exitOnDestroy=true --- immediate=true
日志描述得比较清晰,我们的push服务在启动时因为百度地图这个应用又马上被停止了,日志还解释百度地图优先级比较高,了解过推送服务的单服务单通道策略之后这个动作我也能理解,因为只要保证有一个推送服务就可以了,但是比较魂淡的是通过查看应用中运行的服务,发现并没有启动任何推送相关或者百度地图相关的服务,所以这应该是SDK判断规则上的一个bug,我也把这个问题提给度娘了,暂时还没给反馈这到底是不是个问题(昨天提的,速度你懂的~~~)。幸运的是,通过研究发现这个问题是可以规避的,我们可以通过给intent-filter设置android:priority属性来把我们应用的优先级调高,通过上面的日志可以看到百度地图的优先级是408,我把我们的设成1000,只需设置push服务广播和push服务的intent-filter就可以了:
<!-- push必须的receviver和service声明 --> <receiver android:name="com.baidu.android.pushservice.PushServiceReceiver" android:process=":bdservice_v1"> <intent-filter android:priority="1000"> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <action android:name="com.baidu.android.pushservice.action.notification.SHOW" /> <action android:name="com.baidu.android.pushservice.action.media.CLICK" /> </receiver> <service android:name="com.baidu.android.pushservice.PushService" android:exported="true" android:process=":bdservice_v1" > <intent-filter android:priority="1000"> <action android:name="com.baidu.android.pushservice.action.PUSH_SERVICE"/> </intent-filter> </service>
修改之后发现推送消息正常了,开机之后第一次启动客户端时也能正常的收到消息了,可以查看日志发现现在轮到别的应用因为我们应用的优先级高而停掉服务了。
另外在百度的SDK文档中提到了一点,可以通过监听一些系统事件来提高我们服务的存活率,目前新版本的SDK支持监听到下面的系统事件广播之后尝试重启push服务:
- 切网
- 插拔电源
- 屏幕解锁
- 手机开机
- SD卡插拔
在我们原来的配置中只监听了切网广播和开机广播,所以如果我们把上面那些流氓行为如加上的话应该是能提高我们的服务存活率的,因为推送的长连接就是在推送服务中建立的,所以推送服务的存活率意味着设备的在线率。把push服务广播(这个广播的功能就是监听到某些系统事件之后尝试启动push服务)改成下面的配置:
<!-- push必须的receviver和service声明 --> <receiver android:name="com.baidu.android.pushservice.PushServiceReceiver" android:process=":bdservice_v1"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <action android:name="com.baidu.android.pushservice.action.notification.SHOW" /> <action android:name="com.baidu.android.pushservice.action.media.CLICK" /> <!-- 以下四项为可选的action声明,可大大提高service存活率和消息到达速度 --> <action android:name="android.intent.action.MEDIA_MOUNTED" /> <action android:name="android.intent.action.USER_PRESENT" /> <action android:name="android.intent.action.ACTION_POWER_CONNECTED" /> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" /> </intent-filter> </receiver>
但是由于这些事件跟系统强相关,所以会存在适配问题,由于测试机型有限,我只测试了MOTO ME860、三星Note4、小米2S、MATE7,发现MATE7的开机广播没有生效,原因还没有搞清楚,所以在这台MATE7手机上会有个问题,在手机重启之后无法收到push消息,只有启动过一次我们的应用才能正常收消息,启动过之后无论应用切后台或者退出应用都可以正常的收消息。
通过上面的分析,我觉得可以通过下面几点来改善消息达到率和push服务的存活率:
- 升级SDK到最新版本
- 设置android:priority属性
- 加入一些流氓行为,在插拔电源、插拔SD卡、屏幕解锁事件中尝试启动push服务
当然效果要等产品升级之后才能看到。经过将近两天的折腾又涨姿势了。