您的位置:首页 > 其它

自己动手做推送

2016-02-15 15:47 253 查看
转:http://my.oschina.net/kymjs/blog/367325?fromerr=uOhRSBIH

最近一个月一直在考虑实现一种让Android开发者一个人就能完成的推送功能库。因为现有的推送功能,全部都需要服务器端配合,不断测试,即使使用第三方库也需要很长一段时间的测试。这里就是我最近研究的一个小小的成果:http://git.oschina.net/kymjs/KJPush

推送功能在Android应用开发中已经非常普遍了,本文就是来探讨下Android中推送的底层原理与实现推送功能的一些解决方案。

1、什么是推送?

当我们开发需要和服务器交互的应用程序时,基本上都需要获取服务器端的数据,比如开源中国客户端,在有人评论或回复你的时候,客户端需要知道,并作出相应处理。要获取服务器上的信息,有两种方法:第一种是客户端使用Pull(拉)的方式,就是隔一段时间就去服务器上获取一下信息,看是否有更新的信息出现。第二种就是服务器使用Push(推送)的方式,当服务器端有新信息了,则把最新的信息Push到客户端上。这样,客户端就能自动的接收到消息。

Push是服务端主动发消息给客户端,现在有很多第三方推送框架:例如百度推送、极光推送、个推等等,都是基于之前说的第二种方式也就是服务器使用Push的方式。因为第一时间知道数据发生变化的是服务器自己,所以Push的优势是实时性高。但服务器主动推送需要单独开发一套能让客户端持久连接的服务端程序。但有些情况下并不需要服务端主动推送,而是在一定的时间间隔内客户端主动发起查询,这种时候就应该使用Pull的方式去获取。很多人认为Push方式没有任何消耗,其实不然采用Push方式需要长时间维持一条客户端与服务器端通信的socket长连接,依旧是很费流量与电量。如果轮询策略配置的好,消耗的电与数据流量绝不比维持一个socket连接使用的多。譬如有这样一个app,实时性要求不高,每天只要能获取10次最新数据就能满足要求了,这种情况显然轮询更适合一些,推送显得太浪费,而且更耗电。

2、如何实现轮询请求

第一种方式是在一个Service中创建一个计时器,如下代码是在网上找的一段类似实现(节选)

?
但是用Sleep,TimerTask,都会增大Service被系统回收的可能,更合适的方法是使用AlarmManager这个系统的计时器去管理。

实现方法如下,你可以在这里看到完整的实现方式

?
这样就可以在最大程度上解决因为自己实现计时器造成的计时不准确或计时器被系统回收的问题。

但是仅仅这样还没办法实现一个完善且稳定的轮询推送库,做推送最大的问题有三个:电量消耗,数据流量消耗,服务持久化。

3、电量消耗优化与数据流量消耗优化:

这两个问题其实可以合并成一个问题,因为请求服务器其实也是一个费电的事情。与维持一个长连接类似,要实现推送功能,不管是维持一个长连接或者是定时请求服务器都需要耗费网络数据流量,而只不过长连接是一个细水长流不断耗费,而轮询是一次一大断数据的耗费。这样就需要一种可行的策略去配置,让轮询按照我们想要的方式去执行。目前我采用的思路是当手机处于GPRS模式时降低轮询的频率,每5分钟请求一次服务器,当手机处于WiFi模式时每2分钟请求一次服务器,同时设置如果熄灭屏幕则停止推送请求,当屏幕熄灭20秒后杀死推送进程,这样不仅不需要考虑维护一个进程的消耗同时也节省了数据流量的使用。

4、服务持久化

相信这是一个很多人都遇到的问题,网上也有很多类似的问题,像QQ微信这种应用做的就非常好,不管使用第三方手机助手或者使用系统停止一个应用(不是设置里面的那种停止,是长按Home键的那种),后台Service都不会被回收。很可惜,我目前只能做到保证一个Service不被第三方手机助手回收,可以防止部分手机长按Home键停止,但是例如粗粮的MIUI系统,依旧会杀死我的Service且无法恢复。目前为止我依旧没有找到一个公开的完美解决办法,如果你知道如何解决,请不吝指教。下面我就简单讲讲如何最大程度的维护一个Service。

以前在做音乐播放器的时候,相信很多人都遇到了,在应用开启过多的时候,后台播放音乐的Service独立进程会被系统杀死。

在Android的ActivityManager中有一个内部类RunningAppProcessInfo,用来记录当前系统中进程的状态,如下是其中的一些值:

?
一般数值大于RunningAppProcessInfo.IMPORTANCE_SERVICE的进程都长时间没用或者空进程了

一般数值大于RunningAppProcessInfo.IMPORTANCE_VISIBLE的进程都是非可见进程,也就是在后台运行着

第三方清理软件清理的一般是大于IMPORTANCE_VISIBLE的值,所以要想不被杀死就需要将自己的进程降低到IMPORTANCE_VISIBLE以下,也就是可见进程的程度。在每一个Service中有一个方法叫startForeground,也就是以可见进程的模式启动,这里是在SDK源码中的实现与注释,可以看到,它会在通知栏持续显示一个通知,但只需要将id传为0即可避免通知的显示。当然要取消这种可见进程等级的设置只需要调用stopForgeround即可。

?
这里由于篇幅有限就讲这么多了,希望详细了解进程优先级提升的可以看看ActivityManager源码中的定义以及KJPush中的实现方式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: