Android推送全鏈路簡(jiǎn)析

前言

國(guó)內(nèi)的Android推送就是個(gè)悲劇

國(guó)內(nèi)Android缺少Google的生態(tài),如Google的Paly Store,Google Mobile Services(GSM)等,導(dǎo)致衍生出很多畸形的產(chǎn)業(yè),比如五花八門的APP市場(chǎng),光怪陸離的推送平臺(tái),這里要說的是推送平臺(tái)。Google本身的GSM服務(wù)是包含一套推送在里面的,跟iOS系統(tǒng)的推送類似,它保證每臺(tái)手機(jī)維護(hù)一個(gè)推送通道就能收到各方推送,但由于Google沒法進(jìn)入中國(guó)市場(chǎng),國(guó)產(chǎn)Android基本上算被閹割了一個(gè)核心部件,由此衍生的種種弊端數(shù)不勝數(shù),首當(dāng)其沖的就是推送。

國(guó)內(nèi)的手機(jī)廠商基本都有自家的推送服務(wù),來替代GSM的缺失,性能、用法參差不齊。在離線場(chǎng)景下(APP死亡),如果想要收到推送,就必須接入對(duì)應(yīng)廠家的推送服務(wù),否則壓根收不到。所以Android APP在誕生之初基本就要集成華為push、小米push、魅族push、oppo push、Vivo push等,相對(duì)GSM,復(fù)雜且沒有增益,就好比用江南七怪代替了黃老邪,難用的一B。然而,你別無選擇。不過國(guó)內(nèi)各種廠商倒是樂此不疲,他們多了一個(gè)觸達(dá)用戶及統(tǒng)計(jì)的渠道,并且還能不受Google挾制,對(duì)于開發(fā)者而言,就要麻煩很多,工作量平白翻了很多倍;有的聊天APP為了走自家的推送SDK,還要琢磨各種黑科技:包活,APP相互喚起等,惡之花,開的漫山遍野。更有意思的是,為了解決這種問題,制定出規(guī)范,還促生個(gè)各種機(jī)構(gòu),像推送聯(lián)盟,綠色聯(lián)盟等,但并沒什么卵用,成立3年,亂象依舊,很多說Android很垃圾,那推送的這個(gè)問題要負(fù)一大半責(zé)任。

吐槽完,你仍然要接。

推送概念

為什么一定要接廠商的推送SDK呢?不接入收不到推送嗎?想要弄清這個(gè)東西,就要對(duì)推送有個(gè)簡(jiǎn)單的了解,推送:它的點(diǎn)在推(push上,與其對(duì)應(yīng)的是拉(Pull),核心就是客戶端跟服務(wù)器建立一個(gè)長(zhǎng)鏈接,服務(wù)器會(huì)將信息分發(fā)到各個(gè)客戶端,簡(jiǎn)化示意如下:

image

對(duì)于手機(jī)端APP來說,推送分APP在線推送還是離線推送,其實(shí)就是APP是否存活,APP存活情況下,有多種選擇,如果APP通過Socket跟自家服務(wù)器建立了鏈接,則可以由自家服務(wù)器直接推送到APP端,也可以通過后端推送到第三方推送服務(wù),借由第三方推送給APP端,也就是在線情況下,可以不用接入第三方SDK。但是在APP死亡的情況,只有一種方式:借由第三方推送服務(wù),推送給手機(jī)端,這種場(chǎng)景,APP必須接入第三方廠商SDK,拿華為平臺(tái)為例,其推送模型如下:

華為消息回執(zhí)模式

與兩者對(duì)應(yīng)也有兩種消息的概念:透?jìng)飨⑴c通知欄消息:

  • 透?jìng)飨ⅲ篈PP存活情況下,由推送服務(wù)直接把消息發(fā)送給APP應(yīng)用,由APP自己選擇如何處理,注意透?jìng)鞯那疤崾茿PP存活 ,透?jìng)飨⒖梢圆挥媒尤氲谌絊DK。
  • 通知欄消息:在設(shè)備接收到消息之后,由系統(tǒng)彈出標(biāo)準(zhǔn)安卓通知,用戶點(diǎn)擊通知欄才激活應(yīng)用,這種場(chǎng)景,APP無需存活(活著也不受影響),離線場(chǎng)景下,只有通知欄消息這一條路。

透?jìng)飨⒚總€(gè)APP自己維護(hù)一條通道,離線消息只要一條系統(tǒng)通道,簡(jiǎn)單看下兩者對(duì)比,示意如下:

image

對(duì)于在線透?jìng)飨?,由于是在APP存活的情況下收到的,APP端可以統(tǒng)計(jì)到所有必要信息,無論是推送達(dá)時(shí)間、推送內(nèi)容還是通知的點(diǎn)擊都能統(tǒng)計(jì)到;但是離線推送就沒那么幸運(yùn),很多信息APP自己是拿不到的,但是,業(yè)務(wù)方通常非常關(guān)心到達(dá)率、點(diǎn)擊率這些數(shù)據(jù),必須有一個(gè)有效的解決方案。

推送統(tǒng)計(jì)問題 (離線推送)

如何到達(dá)率

這里不考慮在線推送,只考慮離線(APP死亡),那么離線推送APP能統(tǒng)計(jì)到達(dá)嗎?

答案是 不能,原因其實(shí)很簡(jiǎn)單,APP進(jìn)程都死了,怎么統(tǒng)計(jì)。這種情況下,通知的展示屬于系統(tǒng)行為,APP壓根無法感知,更無從統(tǒng)計(jì)。不過,各三方推送服務(wù)平臺(tái)扔提供了推送到達(dá)統(tǒng)計(jì)的能力,即采用三方推送平臺(tái)的回執(zhí),以上面的華為推送模型為例:

華為消息回執(zhí)模式

可以看到,離線推送的情況下,華為設(shè)備在展示完通知欄消息后,會(huì)給華為Push服務(wù)一個(gè)回執(zhí),而華為Push服務(wù)會(huì)把這個(gè)回執(zhí)頭傳給開發(fā)者服務(wù)器,如此,APP服務(wù)端就能判斷推送是否到達(dá)。

如何統(tǒng)計(jì)點(diǎn)擊率

同樣,在離線推送的場(chǎng)景下,能統(tǒng)計(jì)到點(diǎn)擊事件嗎?關(guān)于這個(gè)場(chǎng)景,不同的廠商ROM及SDK真是亂七八糟,有的支持,有的不行,簡(jiǎn)單整理下如下:

ROM 小米 華為 魅族 oppo vivo
App是否可以統(tǒng)計(jì)到離線點(diǎn)擊事件

因此,各方平臺(tái)給的方式并沒太多參考意義,必須通過其他方式來統(tǒng)計(jì)點(diǎn)擊,離線推送基本都是通過scheme方式來處理,可以通過加參數(shù)來搞定,后續(xù)詳述。

推送送達(dá)率=本次推送真正送達(dá)的設(shè)備數(shù)/所覆蓋的所有設(shè)備數(shù)(按理說,是應(yīng)該清理掉無效設(shè)備)

哪些因素影響送達(dá)率

    1. 留存率。已經(jīng)卸載了APP,肯定收不到,但是有些三方平臺(tái)可能會(huì)歸結(jié)到分母中,需要自家后臺(tái)根據(jù)回執(zhí)手動(dòng)清理regID。
    1. 消息有效期,基本所有第三方PUSH平臺(tái)都支持設(shè)置有效期,有效期越短,觸達(dá)設(shè)備就越少,送達(dá)率會(huì)下降,可以適當(dāng)選擇有效時(shí)間。
    1. 聯(lián)網(wǎng)情況, 在有效期內(nèi),設(shè)備沒聯(lián)網(wǎng),也無法送達(dá),但會(huì)被計(jì)入分母
    1. 目標(biāo)人群設(shè)備的選取,活躍人群設(shè)備送達(dá)率肯定要高于全量推送

因此為了能精準(zhǔn)的計(jì)算送達(dá)率,APP服務(wù)端要定期清理無效regID(推送token),否則統(tǒng)計(jì)的送達(dá)率也會(huì)偏低

各離線推送平臺(tái)接入事項(xiàng)

很多大公司都有自家的推送SDK來處理透?jìng)飨ⅲ」疽话悴痪邆溥@個(gè)能力,所以在接入Push的時(shí)候也分兩種情況,

  • 1:有自己加的PushSDK,
  • 2:沒有自家PushSDK

如果APP有自己的PushSDK,那只要接入第三方離線推送能力就好了,一些關(guān)于透?jìng)鞯奶幚砼渲每梢酝耆挥藐P(guān)心,用自己PushSDK那套就可以。如果沒有自家PushSDK,那就需要選擇一個(gè)SDK進(jìn)行透?jìng)魈幚恚?dāng)然,仍要接入第三方離線推送能力。不過即使如此,各家ROM的接入規(guī)則也個(gè)不相同,比如小米有個(gè)奇葩的權(quán)限叫:“后臺(tái)彈出界面權(quán)限 ”,如果后端服務(wù)Push姿勢(shì)不對(duì),可能會(huì)引入奇葩問題:比如,手機(jī)能收到PUSH,但是拉不起界面,坑爹。

簡(jiǎn)單看下各ROM計(jì)入注意事項(xiàng),只看離線能力,不考慮透?jìng)鳎?/p>

小米

關(guān)于MIPUSH的接入,直接看官方文檔即可,沒太多問題,需要注意的是,小米有個(gè)奇葩的權(quán)限設(shè)置:后臺(tái)彈出界面權(quán)限 ,該權(quán)限默認(rèn)是關(guān)閉,這個(gè)選項(xiàng)可能會(huì)影響推送通知的點(diǎn)擊行為,小米有兩大類點(diǎn)擊行為:

完全自定義點(diǎn)擊行為

在這種行為下,開發(fā)者可以攔截通知點(diǎn)擊事件,自定義如何處理后續(xù)事件,點(diǎn)擊后,MiPushMessage通過PushMessageReceiver繼承類的onNotificationMessageClicked方法傳到APP進(jìn)程,開發(fā)者可自行處理,如果想要啟動(dòng)界面,只需要在其中調(diào)用context.startActivity方法即可,但是,這種自定義的行為會(huì)受到后臺(tái)彈出界面權(quán)限的影響,尤其是高版本的MIUI ROM中。

image

你會(huì)發(fā)現(xiàn),在這些手機(jī)上,此方式壓根沒法拉起APP,除非通過先啟動(dòng)一個(gè)Service,然后在Service中拉起,非常像小米的一個(gè)BUG,并且,即使通過此下策能拉起,你會(huì)發(fā)現(xiàn),拉起速度非常慢,可能是過多AMS交互通信導(dǎo)致的,所以這種策略可以斃了。

預(yù)定義點(diǎn)擊行為

預(yù)定義點(diǎn)擊行為不用用戶在onNotificationMessageClicked中處理,系統(tǒng)會(huì)直接拉起目標(biāo)頁面,小米支持三種預(yù)定義點(diǎn)擊行為:

  • (1) 打開當(dāng)前的Launcher Activity
  • (2) 打開當(dāng)前app內(nèi)的任意一個(gè)Activity
  • (3) 打開網(wǎng)頁。

APP一般會(huì)采用第二種行為,打開APP任意一個(gè)Activity,其實(shí)最終會(huì)選擇一個(gè)DeepLink Activity,由其路由到其他界面。服務(wù)端調(diào)用Message.Builder類的extra(String key, String value)方法,將key設(shè)置為Constants.EXTRA_PARAM_NOTIFY_EFFECT,value設(shè)置為Constants.NOTIFY_ACTIVITY便可以達(dá)到該效果,用戶點(diǎn)擊了客戶端彈出的通知消息后,封裝消息的MiPushMessage對(duì)象通過Intent傳到客戶端,客戶端可在Activity中解析,并自行處理后續(xù)流程。離線推送情況下,推送服務(wù)端核心字段如下:

image

采用離線非透?jìng)飨?,并利用extra自定義Click行為,最后推送給小米的消息格式簡(jiǎn)化如下:

{
    title=通知標(biāo)題, 
    description=通知內(nèi)容, 
    restrictedPackageNames=[com.test.example], 
    notifyType=1, 
    notifyId=1249808047, 
    <!--打開任意Activity配置-->
    extra.intent_uri=yanxuan://re?opOrderId=crm_a1d05c1d3d1743e192a08b461a376785_20200715,
    extra.notify_effect=2
}

extra.intent_uri的值就是APP端定義的私有scheme,點(diǎn)擊通知會(huì)直接拉起相應(yīng)的DeepLink Activity,從而喚起應(yīng)用,至于DeepLink Activity最終路由到哪個(gè)界面,可以從extra.intent_uri中解析出來。對(duì)于上文層說過的click事件不易統(tǒng)計(jì)的問題,可以通過在scheme家參數(shù)的方式解決,如下:

extra.intent_uri= yanxuan://re?opOrderId=0200715, 

轉(zhuǎn)為

extra.intent_uri= yanxuan://re?opOrderId=0200715&platform=xiaomi 

之后在路由Activity中可以解析出platform參數(shù),從而標(biāo)記click事件及來源平臺(tái)。預(yù)定義行為系統(tǒng)會(huì)幫我們處理好喚起,在APP中,不需要在onNotificationMessageClicked再次響應(yīng)click事件了,避免重復(fù)處理。后面其余各方SDK的能力基本都跟小米類似,大同小異,沒多少花樣。

華為

流程同小米類似,按文檔即可,預(yù)定義行為有如下四種:

  • 1:用戶定義Uri,打開目標(biāo)界面
  • 2:點(diǎn)擊后打開特定網(wǎng)頁
  • 3:點(diǎn)擊后打開應(yīng)用
  • 4:點(diǎn)擊后打開富媒體信息

一般選擇自定義Uri行為,所有數(shù)據(jù)通過intent uri傳輸給APP,依舊是私有scheme的DeepLink實(shí)現(xiàn)方式。對(duì)應(yīng)參數(shù)意義如下:

image

基本上,選擇type=1 同 intent uri配合,uri生成格式如下:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("pushscheme://com.huawei.codelabpush/deeplink?name=abc&age=180"));
String intentUri = intent.toUri(Intent.URI_INTENT_SCHEME);

最終通過API發(fā)送給華為push平臺(tái)數(shù)據(jù)格式簡(jiǎn)化如下:

"msg":{
    "action":{
        "param":{
            "intent":"intent://member?url=http%3A%2F%2Fm.you.163.com%2Fmembership%2Findex&_yanxuan_hwpush=1&_mid=a397314518947995648#Intent;scheme=yanxuan;launchFlags=0x4000000;end"
        },
        "type":1
    },
    "body":{
        "title":"huawei免郵券禮包",
        "content":"快來領(lǐng)取你的每月專屬免運(yùn)費(fèi)券,立即領(lǐng)取>>"
    }
}

同小米類似,如果需要添加額外參數(shù),放到scheme中,不再敖述。

魅族

接入類似,支持四種預(yù)定義行為:

  • 打開應(yīng)用主頁
  • 打開應(yīng)用內(nèi)頁面
  • 打開URI頁面
  • 客戶端自定義

同樣選擇預(yù)定義Uri頁面,具體參數(shù)如下

image

最終發(fā)送數(shù)據(jù)格式簡(jiǎn)化如下:

 {
     noticeBarType = 0,
     title = 'meizu明天之后?恢復(fù)原價(jià)', 
     content = '店慶爆款返場(chǎng)!乳膠床墊直降500,拉桿箱僅7折!??每滿150減25消費(fèi)券全品類通用,最后1天>>',
     clickType = 2, 
     url = 'yanxuan://yxwebview?url=https%3A%2F%2Fact.you.163.com%2Fact%2Fpub%2FDisjY2u1n9p4SB3.html%3Fanchor%3DSeen3xcj%26opOrderId%3Dcrm_task_20200414160053263_1'
 }

clickType = 2 配合Uri scheme來實(shí)現(xiàn),預(yù)定義拉起對(duì)應(yīng)界面,如果需要添加額外參數(shù),同上。

oppo

接入類似,oppo無法感知click事件,支持五種預(yù)定義行為(有冗余):

  • 0,啟動(dòng)應(yīng)用;
  • 1,打開應(yīng)用內(nèi)頁(activity的intent action)
  • 2,打開網(wǎng)頁;
  • 4,打開應(yīng)用內(nèi)頁(利用activity全名)
  • 5, Intent scheme URL
image

處理類似,選click_action_type選擇5,通過私有scheme拉起APP,具體數(shù)據(jù)格式如下

{
    "notification":{
        "app_message_id":"a467798011733344256",
        "channel_id":"NotificationChannel",
        "click_action_url":"yanxuan://yxwebview?url=https%3A%2F%2Fact.you.163.com%2Fact%2Fpub%2FDisjY2u1n9p4SB3.html%3Fanchor%3DSeen3xcj%26opOrderId%3Dcrm_task_20200414160053263_1",
        "click_action_type":5,
        "content":"明天之后恢復(fù)原價(jià)",
        "title":"明天之后恢復(fù)原價(jià)"
    },
    "target_type":2,
    "target_value":"CN_04f112241e183f6309611df2a95d6237"
}

click_action_type= 5 配合 scheme拉起APP,,如果需要添加額外參數(shù),同上。

vivo

vivo跟oppo很類似,不過它也可以收到click事件(并沒什么卵用),因此支持完全自定義(然而不用),支持五種Click行為

  • 1:打開APP首頁
  • 2:打開鏈接
  • 3:自定義
  • 4:打開app內(nèi)指定頁面

同樣,為了防止禁止后臺(tái)啟動(dòng),不采用自定義的方式,而直接打開打開app內(nèi)指定頁面, "skipType":4,

{
    "classification":1,
    "content":"adssdsr345436",
    "notifyType":1,
    "pushMode":1,
    "regId":"15905547110541891320627",
    "requestId":"a467798011733344256",
    "skipContent":"yanxuan://yxwebview?url=https%3A%2F%2Fact.you.163.com%2Fact%2Fpub%2FDisjY2u1n9p4SB3.html%3Fanchor%3DSeen3xcj%26opOrderId%3Dcrm_task_20200414160053263_1",
    "skipType":4,
    "title":"adssdsr345436"
}

skipType:4配合 scheme拉起APP,,如果需要添加額外參數(shù),同上。

各ROM接入事項(xiàng)小結(jié)

以上是幾種離線推送的接入方式,整體總結(jié)就是:

  • 盡量選擇預(yù)定義Uri scheme方式,不要采用自定義的方式
  • 可以在scheme中填加參數(shù),統(tǒng)一鑒別click事件
  • 在預(yù)定義的方式下,不要在click回調(diào)中重復(fù)處理事件
  • 如果只要離線推送功能,沒必要處理透?jìng)髋渲茫ū热缡裁碦eceiver Service之類的配置)

總結(jié)

  • 不得不接入第三方SDK是為了離線推送
  • 各家離線推送大同小異,為了統(tǒng)一建議統(tǒng)一采用預(yù)定義Uri方式,配合私有scheme拉起APP
  • 額外追蹤參數(shù)可以通過添加scheme字段解決
  • 不同ROM可能有自己的額外限制,比如小米,盡量避免受其限制

最后,Android的推送困境是個(gè)悲劇...

作者:看書的小蝸牛
原文鏈接:Android推送的群魔亂舞

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容