如何提高推送的到達(dá)率

為了提高用戶(hù)體驗(yàn)鞭光,現(xiàn)在大多數(shù)的應(yīng)用都會(huì)增加推送功能吏廉,目前主流的第三方推送有 個(gè)推、mi push惰许、百度席覆、Jpush、極光等汹买,但是推送的到達(dá)率卻是不盡人意的佩伤,拿個(gè)推而言,服務(wù)器這邊統(tǒng)計(jì)的結(jié)果是到達(dá)率僅有90%(僅做參考)晦毙。當(dāng)然了還有官方的推送Google Cloud Messaging生巡,可惜在國(guó)內(nèi)然并卵,暫不做討論见妒。

推送到達(dá)率問(wèn)題的解決是刻不容緩的孤荣,因?yàn)樵谀壳盎ヂ?lián)網(wǎng)大用戶(hù)量的場(chǎng)景下,10%的用戶(hù)數(shù)還是相當(dāng)大的。

原因

我們知道盐股,推送的技術(shù)原理主要是保持網(wǎng)絡(luò)的長(zhǎng)連接钱豁,在TCP長(zhǎng)連接建立成功的基礎(chǔ)上,推送不能如期到達(dá)的原因主要和網(wǎng)絡(luò)狀況有關(guān)疯汁,比如網(wǎng)絡(luò)慢牲尺、丟包等等,這個(gè)是所有網(wǎng)絡(luò)訪(fǎng)問(wèn)遇到的問(wèn)題幌蚊,不是導(dǎo)致推送到達(dá)率如此低的主要原因谤碳。

那么,其最主要原因是什么呢霹肝?顯然是TCP長(zhǎng)連接持續(xù)保持這個(gè)前提未能得到保證估蹄,也就是:
推送時(shí),移動(dòng)端未在線(xiàn)

解決方案

現(xiàn)在我們找到了其原因所在沫换,那么要解決這個(gè)問(wèn)題臭蚁,就要從兩方面入手:

  • “不擇手段”的保證移動(dòng)端在線(xiàn),保證TCP長(zhǎng)連接持續(xù)建立
  • 緩存推送消息讯赏,用戶(hù)上線(xiàn)后重新發(fā)送

保證移動(dòng)端在線(xiàn)

其實(shí)也就是我們常說(shuō)的進(jìn)程笨宥遥活,可以創(chuàng)建一個(gè)幽靈進(jìn)程進(jìn)行笔妫活操作系枪,也可以直接用應(yīng)用主進(jìn)程進(jìn)行保活磕谅,用這個(gè)進(jìn)程中建立TCP連接,保證其存活的最大時(shí)長(zhǎng)衬浑。方案主要以下幾種:

  • 利用系統(tǒng)廣播拉起應(yīng)用放刨,包括系統(tǒng)廣播和同系列應(yīng)用廣播
  • 啟動(dòng)前臺(tái)service工秩,由于我們不想讓用戶(hù)感知到,所以應(yīng)利用系統(tǒng)漏洞取消通知欄Notification的顯示

注意:
保證移動(dòng)端在線(xiàn)確實(shí)能有效的提高推送的到達(dá)率进统,但是需要注意頻繁的喚醒應(yīng)用會(huì)導(dǎo)致應(yīng)用耗電量的增加,所以要把握一定的度螟碎。

廣播喚醒

利用系統(tǒng)廣播

監(jiān)聽(tīng)系統(tǒng)事件廣播來(lái)喚醒應(yīng)用,常用的廣播有:

  • 開(kāi)機(jī)掉分,ACTION_BOOT_COMPLETED
  • 亮屏倍谜,ACTION_SCREEN_ON
  • 滅屏迈螟,ACTION_SCREEN_OFF
  • 插拔有線(xiàn)耳機(jī),ACTION_HEADSET_PLUG
  • 電量充足答毫,ACTION_BATTERY_OK

注意:

  • 部分機(jī)型可能對(duì)開(kāi)機(jī)廣播做了限制季春,所以可能收不到開(kāi)機(jī)廣播
  • ACTION_SCREEN_ONACTION_SCREEN_OFF载弄、ACTION_HEADSET_PLUG廣播只能在代碼里注冊(cè),當(dāng)app完全退出后就收不到這個(gè)廣播了

不同的app進(jìn)程惫叛,用廣播相互喚醒

  • 嵌入第三方SDK會(huì)喚醒相應(yīng)的app進(jìn)程逞刷,比如
    • 微信的SDK會(huì)喚醒微信應(yīng)用,支付寶支付的SDK會(huì)喚醒支付寶SDK
    • 個(gè)推SDK會(huì)喚醒其他嵌入個(gè)推SDK的應(yīng)用
  • App會(huì)喚醒同公司的其他app夸浅,比如:支付寶、天貓警医、淘寶坯钦、UC等阿里系的應(yīng)用,打開(kāi)其中一款就有可能順便喚醒其他幾款應(yīng)用

前臺(tái)service

該方案是應(yīng)用范圍最最廣泛的一種手段吟温,主要是啟動(dòng)一個(gè)前臺(tái)service路星,并利用系統(tǒng)漏洞避免其在通知欄處顯示Notification诱桂。這樣既能保證進(jìn)程的優(yōu)先級(jí)高于普通后臺(tái)進(jìn)程,又將用戶(hù)感知降到最低挥等。

思路:

  • API < 18時(shí),啟動(dòng)前臺(tái)Service時(shí)直接傳入new Notification()
  • API >= 18迁客,同時(shí)啟動(dòng)兩個(gè)id相同并傳入new Notification()的前臺(tái)Service,然后再將后啟動(dòng)的Service做stop處理
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時(shí)粘室,直接傳入new Notification()
            startForeground(DAEMON_SERVICE_ID, new Notification());
        } else {
            // API >= 18時(shí)卜范,啟動(dòng)兩個(gè)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時(shí)灰色苯蹙簦活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 查看結(jié)果看到前臺(tái)service已經(jīng)啟動(dòng)奥裸,但在通知欄里并未顯示

service狀態(tài).png

當(dāng)然了,我們可以結(jié)合上面這兩個(gè)方案:
創(chuàng)建一個(gè)廣播DaemonReceiver樟氢,該廣播監(jiān)聽(tīng)某些系統(tǒng)事件廣播创倔,在廣播處理中啟動(dòng)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);
    }
}

鑒于當(dāng)app被殺死后是監(jiān)聽(tīng)不到系統(tǒng)廣播的,而我們還需要保持DaemonService以確保推送TCP連接的建立霸妹,那我們可以在DaemonServiceonDestroy()中啟動(dòng)一個(gè)新的service DaemonReStartService, 在DaemonReStartService中來(lái)重新啟動(dòng)DaemonService知押。

Android中的應(yīng)用就是這么一步步被玩的卡的不要不要的,所以請(qǐng)謹(jǐn)慎使用台盯。

緩存推送消息

流程如下:

  • 客戶(hù)端
    • 收到推送后静盅,發(fā)送回執(zhí)消息給服務(wù)器,并存儲(chǔ)到本地?cái)?shù)據(jù)庫(kù)
    • 收到的推送消息的消息ID已存儲(chǔ)到數(shù)據(jù)庫(kù)中時(shí)蒿叠,不做處理并重發(fā)回執(zhí)消息
  • 服務(wù)器
    • 如果客戶(hù)端未在線(xiàn),則將該條消息保存到數(shù)據(jù)庫(kù)
    • 在客戶(hù)端上線(xiàn)后痊银,取出推送消息發(fā)給客戶(hù)端并標(biāo)記為已刪除
    • 在取消息時(shí)注意同種類(lèi)消息是否需要合并施绎,考慮時(shí)效性只保留一定時(shí)間內(nèi)的推送消息贞绳,合并或者超時(shí)后標(biāo)記為已刪除
    • 推送時(shí)在數(shù)據(jù)庫(kù)里保存記錄致稀,收到客戶(hù)端回執(zhí)后將該條消息標(biāo)記為已刪除,超時(shí)未收到回執(zhí)消息則重發(fā)消息

總結(jié)

不以用戶(hù)利益為出發(fā)點(diǎn)的手段都是耍流氓豺裆。
進(jìn)程背舨拢活必定導(dǎo)致應(yīng)用一直保持喚醒狀態(tài)一直在后臺(tái)運(yùn)行,不可避免的導(dǎo)致耗電量增加蔑歌;發(fā)送回執(zhí)消息則會(huì)額外消耗用戶(hù)流量(可以考慮一段時(shí)間內(nèi)的回執(zhí)消息合并后統(tǒng)一發(fā)送),服務(wù)器保存每條推送記錄可能會(huì)導(dǎo)致服務(wù)器壓力過(guò)大园匹。
所以劫灶,在盡可能保證用戶(hù)到達(dá)率的情況下,也要考慮節(jié)能和流量本昏,和使用設(shè)計(jì)模式一樣,凡事皆有度怔昨,萬(wàn)事不可過(guò)宿稀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市矮烹,隨后出現(xiàn)的幾起案子罩锐,更是在濱河造成了極大的恐慌,老刑警劉巖唯欣,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件境氢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡问芬,警方通過(guò)查閱死者的電腦和手機(jī)寿桨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)挡鞍,“玉大人,你說(shuō)我怎么就攤上這事墨微”獾В” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵锈麸,是天一觀的道長(zhǎng)牺蹄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)虑省,這世上最難降的妖魔是什么僧凰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮训措,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怀大。我一直安慰自己呀闻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布蓖康。 她就那樣靜靜地躺著,像睡著了一般倒信。 火紅的嫁衣襯著肌膚如雪泳梆。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天优妙,我揣著相機(jī)與錄音,去河邊找鬼瘾带。 笑死熟菲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的抄罕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嚷兔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼冒晰!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起壶运,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤浪秘,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后棵癣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體夺衍,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年河劝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡煎娇,死狀恐怖贪染,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情杭隙,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布票髓,位于F島的核電站铣耘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蜗细。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一踪区、第九天 我趴在偏房一處隱蔽的房頂上張望吊骤。 院中可真熱鬧,春花似錦白粉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至池磁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間地熄,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工雅潭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留却特,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓椿浓,卻偏偏與公主長(zhǎng)得像闽晦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子仙蛉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 讓app 的service常駐其實(shí)是很流氓的做法捅儒,但是需求擺在那里。巧还。。 但是要清除一點(diǎn):想百分百迸炫活servic...
    PengPengPro閱讀 16,136評(píng)論 10 65
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理阶牍,服務(wù)發(fā)現(xiàn),斷路器走孽,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,525評(píng)論 25 707
  • 如何能讓我們的應(yīng)用能夠在系統(tǒng)后臺(tái)持續(xù)地運(yùn)行是一個(gè)自Android從娘胎里出來(lái)時(shí)就議論不停的話(huà)題磕瓷,而且這似乎成了一個(gè)...
    駿駿的簡(jiǎn)書(shū)閱讀 1,103評(píng)論 1 19
  • 抱抱,今天的5分鐘 寶貝边翁,我是一個(gè)很少不高興的人,但有時(shí)候就會(huì)低落下來(lái)符匾。你看我這樣覺(jué)得我挫氣,對(duì)不起甸各,我也不喜歡自...
    握著荊條閱讀 272評(píng)論 0 0