Android实现推送的那些事

推送定义:在任何时间地点服务端向客户端推送一条消息,如果客户端在线或者下次上线,就能接收到该消息。

通常想到的实现方式是:轮询、tcp长连,其目的都是让服务端和客户端之间时刻保持在线状态

对于客户端而言,

轮询:无非是写个线程按某种配置的时间间隔无限循环去请求服务端是否有新的消息,当有新的消息,就提醒给用户

tcp长连:与服务端建立tcp长连,这样服务端就可以直接给客户端发送消息了,当前市面上基本上是以此种方式居多

上面两种方式都是比较消耗资源的,而这里我们使用的是另外的一种方式来实现的,如MobPush

udp无连接:其实现基本原理为,客户端创建socket并向服务端发送udp包,服务端接收到请求连接的udp包之后,将客户端id与ip和端口号进行绑定,当要向某个客户端id发送消息时,找到其对应的ip和端口号,然后将消息组装成udp包发送即可,其大致流程如下:


而对于客户端需要解决的如下几个问题

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上的图标

}

```

神奇的开发者们发现了一种通过多实现一个TmpService,在MainService和TmpService两个service中同时发送具有相同 ID 的 Notification,然后干掉TmpService,这时Notification会自动消失,而MainService的优先级已经被提升到2了,从而达到了目的。缺陷是:需要开发者在manifest多配置一个service,可能在不久的将来也会被官方更新掉,不建议采纳。

3.利用系统广播拉活

该方式是通过AndroidManifest.xml注册一些特定的系统广播方式,来拉活进程,但是这种方式在高版本中已经被官方封掉了,所以也成了一堆没必要的配置

当然我们可以在代码中添加了相关的系统广播注册、同时在监听进程死掉时发送的广播,测试在某些低版本的手机上有效

```

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占用时间等等方面来控制内存和电量的消耗

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容