housezhu 2012-03-29
Android是一个相当开放的平台,允许我们开发常驻后台运行的应用程序,依靠TCP长连接接受服务器的消息推送,但也因此在电量消耗方面广遭诟病。如果开发者,特别是类IM应用的开发者自己还不去了解Android底层的机制,没准搞出来的应用就变成待机电池杀手了。
Android手机有两个处理器,一个叫ApplicationProcessor(AP),一个叫BasebandProcessor(BP)。AP是ARM架构的处理器,用于运行Linux+Android系统;BP用于运行实时操作系统(RTOS),通讯协议栈运行于BP的RTOS之上。非通话时间,BP的能耗基本上在5mA左右,而AP只要处于非休眠状态,能耗至少在50mA以上,执行图形运算时会更高。另外LCD工作时功耗在100mA左右,WIFI也在100mA左右。一般手机待机时,AP、LCD、WIFI均进入休眠状态,这时Android中应用程序的代码也会停止执行。
Android为了确保应用程序中关键代码的正确执行,提供了WakeLock的API,使得应用程序有权限通过代码阻止AP进入休眠状态(iOS、WP7都没这种东西)。如果不领会Android设计者的意图而滥用WakeLockAPI,为了自身程序在后台的正常工作而长时间阻止AP进入休眠状态,后果就相当严重了。
首先,完全没必要担心AP休眠会导致收不到消息推送。通讯协议栈运行于BP,一旦收到数据包,BP会将AP唤醒,唤醒的时间足够AP执行代码完成对收到的数据包的处理过程。其它的如Connectivity事件触发时AP同样会被唤醒。那么唯一的问题就是程序如何执行向服务器发送心跳包的逻辑。你显然不能靠AP来做心跳计时。Android提供的AlarmManager就是来解决这个问题的。Alarm运行在BP上,触发时唤醒AP执行程序代码。那么WakeLockAPI有啥用呢?比如心跳包从请求到应答,比如断线重连时验证密码这些关键逻辑的执行过程,就需要WakeLock来保护。而一旦一个关键逻辑执行成功,应该立即释放掉WakeLock了。心跳间隔也不宜过短,至少隔个10分钟吧。
服务器端也不要有事没事都向客户端推送TCP包。客户端后台运行时,最好只在必须要客户端立即在状态栏显示内容时推送一个包,别的数据都得缓存起来,等待客户端进入前台运行时主动请求,或者也可以加在心跳请求的应答中带给客户端。因为每推送一次,客户端都得唤醒AP处理这个推送。这个说起来容易,但作为一个后台TCP长链接的应用,服务端很可能会在一些逻辑的设计上图省事而抑制不住立即向客户端推送的冲动。