代码片段服务器学习android技术专栏

如何提高推送的到达率

2017-01-11  本文已影响3146人  蚊子Skeeter

为了提高用户体验,现在大多数的应用都会增加推送功能,目前主流的第三方推送有 个推、mi push、百度、Jpush、极光等,但是推送的到达率却是不尽人意的,拿个推而言,服务器这边统计的结果是到达率仅有90%(仅做参考)。当然了还有官方的推送Google Cloud Messaging,可惜在国内然并卵,暂不做讨论。

推送到达率问题的解决是刻不容缓的,因为在目前互联网大用户量的场景下,10%的用户数还是相当大的。

原因

我们知道,推送的技术原理主要是保持网络的长连接,在TCP长连接建立成功的基础上,推送不能如期到达的原因主要和网络状况有关,比如网络慢、丢包等等,这个是所有网络访问遇到的问题,不是导致推送到达率如此低的主要原因。

那么,其最主要原因是什么呢?显然是TCP长连接持续保持这个前提未能得到保证,也就是:
推送时,移动端未在线

解决方案

现在我们找到了其原因所在,那么要解决这个问题,就要从两方面入手:

保证移动端在线

其实也就是我们常说的进程保活,可以创建一个幽灵进程进行保活操作,也可以直接用应用主进程进行保活,用这个进程中建立TCP连接,保证其存活的最大时长。方案主要以下几种:

注意:
保证移动端在线确实能有效的提高推送的到达率,但是需要注意频繁的唤醒应用会导致应用耗电量的增加,所以要把握一定的度。

广播唤醒

利用系统广播

监听系统事件广播来唤醒应用,常用的广播有:

注意:

不同的app进程,用广播相互唤醒

前台service

该方案是应用范围最最广泛的一种手段,主要是启动一个前台service,并利用系统漏洞避免其在通知栏处显示Notification。这样既能保证进程的优先级高于普通后台进程,又将用户感知降到最低。

思路:

public class DaemonService extends Service {
    private static final int DAEMON_SERVICE_ID = 123456789;
    private static boolean mAlive = false;

    @Override
    public void onCreate() {
        super.onCreate();
        mAlive = true;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            // API < 18时,直接传入new Notification()
            startForeground(DAEMON_SERVICE_ID, new Notification());
        } else {
            // API >= 18时,启动两个id相同的service,然后将后startForeground的service stopForeground/stop
            startService(new Intent(this, DaemonInnerService.class));
            startForeground(DAEMON_SERVICE_ID, new Notification());
        }

        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mAlive = false;
    }

    public static boolean isAlive() {
        return mAlive;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * 用于API >= 18时灰色保活Service
     */
    public static class DaemonInnerService extends Service {

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            startForeground(DAEMON_SERVICE_ID, new Notification());
            stopForeground(true);
            stopSelf();
            return super.onStartCommand(intent, flags, startId);
        }

        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
}

adb shell dumpsys activity services 查看结果看到前台service已经启动,但在通知栏里并未显示

service状态.png

当然了,我们可以结合上面这两个方案:
创建一个广播DaemonReceiver,该广播监听某些系统事件广播,在广播处理中启动DaemonService

public class DaemonReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        startDaemonService(context);
    }
    
     private void startDaemonService(Context context) {
        if (DaemonService.isAlive()) {
            return;
        }

        Intent serviceIntent = new Intent(context, DaemonService.class);
        context.startService(serviceIntent);
    }
}

鉴于当app被杀死后是监听不到系统广播的,而我们还需要保持DaemonService以确保推送TCP连接的建立,那我们可以在DaemonServiceonDestroy()中启动一个新的service DaemonReStartService, 在DaemonReStartService中来重新启动DaemonService

Android中的应用就是这么一步步被玩的卡的不要不要的,所以请谨慎使用。

缓存推送消息

流程如下:

总结

不以用户利益为出发点的手段都是耍流氓。
进程保活必定导致应用一直保持唤醒状态一直在后台运行,不可避免的导致耗电量增加;发送回执消息则会额外消耗用户流量(可以考虑一段时间内的回执消息合并后统一发送),服务器保存每条推送记录可能会导致服务器压力过大。
所以,在尽可能保证用户到达率的情况下,也要考虑节能和流量,和使用设计模式一样,凡事皆有度,万事不可过。

上一篇 下一篇

猜你喜欢

热点阅读