而对于客户端需要解决的如下几个问题:
1. 如何维护客户端id与路由之间的绑定关系;
2. 如何延长客户端的在线状态(app保活)
3. 客户端性能考虑
下面将针对这几点进行逐步介绍
维护客户端与路由的绑定关系
这里我们需要了解一下NAT,所谓NAT就是,在局域网内部网络中使用内部地址,而当内部节点要与外部网络进行通讯时,就在网关处,将内部地址替换成公用地址,从而在外部公网上正常使用。
所以当发送udp包到服务器时,服务器拿到的ip和端口其实是客户端在路由上映射的ip和端口,所以我们需要维护路由上的映射表,这时就需要定期发送心跳包,以保证路由上的映射关系不会被清除掉。
1. 维护心跳包
主要作用是防止NAT超时, 和探测连接是否断开,并根据实际情况进行重连操作,其流程如下:
2. 网络监测
当网络切换和变化时,会导致映射关系失效,所以我们需要做相应的监测和重连
1. 监听网络变化,当网络类型变化或者断开后重新连接上时,进行重连
2. 定期监测ip地址变化,如果监测到ip地址有变化时,则进行重连
APP保活
app保活是一个老生常谈的话题,经过广大开发者多年累积与筛选,互联网上相关文章层出不穷,目前看来不算什么硬梗,大多都按套路出行,这里也套路套路
当应用退到后台时,为了确保推送通道能够正常使用而不被系统回收,通常会做一些进程保活和拉活的策略,大体分为以下几类:
1. 利用系统Service机制拉活
将 Service 设置为 START_STICKY,利用系统机制在 Service 挂掉后自动拉活
如下两种情况无法拉活:
1.Service 第一次被异常杀死后会在5秒内重启,第二次被杀死会在10秒内重启,第三次会在20秒内重启,一旦在短时间内 Service 被杀死达到5次,则系统不再拉起。
2.进程被取得 Root 权限的管理工具或系统工具通过 forestop 停止掉,无法重启。
经测试,在绝大多数手机任务进程中,手动杀掉进程后,是不会自动重启的(符合情况2)
2. 设置进程优先级
当进程退到后台后,系统在回收资源时,会根据进程优先级,进行资源回收,优先级越高越晚被回收,所以尽可能地提高service进程的优先级,可以在一定程度上保障其在后台时不被系统回收
进程按照重要性分为如下5类:
1.前台进程(Foreground process)
2.可见进程(Visible process)
3.服务进程(Service process)
4.后台进程(Background process)
5.空进程(Empty process)
一般的后台进程进程属于第4类,我们可以通过setForeground将service提升到2,但是这种方案必须与一条可见的通知绑定在一起,而这种体验显然不能被用户接受
当然我们可以通过new Notification()的方式设置一个空的通知,与之绑定,但只在4.3以下版本才有效,如下:
if (Build.VERSION.SDK_INT < 18) {
service.startForeground(1001, new Notification());//API < 18 ,此方法能有效隐藏Notification上的图标
}
```
```
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_PRESENT);
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
```
4. 使用AlarmManager
使用AlarmManager定时发送心跳、定时检查ip变化
但是经测试当系统休眠时,AlarmManager也停止了工作,且在不同sdk版本上需要采用不同的set方式,如下:
```
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
if (pingPendingIntent != null) {
am.cancel(pingPendingIntent);
}
pingPendingIntent = PendingIntent.getBroadcast(MobSDK.getContext(), 0, new Intent(ALARM_ACTION_PING),
PendingIntent.FLAG_UPDATE_CURRENT);
final long nextTime = SystemClock.elapsedRealtime() + interval * 1000L;
if (Build.VERSION.SDK_INT >= 23) {
am.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, pingPendingIntent);
} else if (Build.VERSION.SDK_INT >= 19) {
am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, pingPendingIntent);
} else {
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, pingPendingIntent);
}
```
5. 进程间相互拉活
当某台手机上有多个应用都在使用sdk时,可根据用户在后台配置授权后,选择性的进行相互之间的拉活
6. 利用native进程拉活
网络中流传的一种利用 Linux 中的 fork 机制创建 Native 进程,在 Native 进程中监控主进程的存活,当主进程挂掉后,在 Native 进程中立即对主进程进行拉活。
在 Android 中所有进程和系统组件的生命周期受 ActivityManagerService 的统一管理。而且,通过 Linux 的 fork 机制创建的进程为纯 Linux 进程,其生命周期不受 Android 的管理。
这种方案在网上流传已久,听说在5.0以上版本也不成立,且需要额外添加本地代码编译so,无形的添加了app体积,不采纳
7. JobScheduler和账号同步机制拉活
这种两种方式同样需要在AndroidManifest.xml中注册相关配置和权限,版本限制,效果一般
8. 将应用加入厂商或管理软件白名单
9. 第三方push通道接入:
GSM:国内不支持
小米推送、华为推送
性能考虑
APP性能也是老生常谈的话题,总结其出发点和最终的目的都是为了减少用户流量、内存占用、电量消耗等等方面的优化,以达到省电省流量且界面流畅的终极目标。
在开发时,大致可从如下几个方面思考和稍加注意:
1. 减少网络请求次数,缩小网络中传输数据的体积,像推送这种主动的操作,可通过自定义数据传输协议来控制流量的消耗
2. 控制唤醒屏幕,避免开启没必要的线程,合理释放资源,减少IO操作,避免使用广播机制,减少cup占用时间等等方面来控制内存和电量的消耗