Android實現推送的那些事

推送定義:在任什麼時候間地點服務端向客戶端推送一條消息,若是客戶端在線或者下次上線,就能接收到該消息。
一般想到的實現方式是:輪詢、tcp長連,其目的都是讓服務端和客戶端之間時刻保持在線狀態
對於客戶端而言,
輪詢:無非是寫個線程按某種配置的時間間隔無限循環去請求服務端是否有新的消息,當有新的消息,就提醒給用戶
tcp長連:與服務端創建tcp長連,這樣服務端就能夠直接給客戶端發送消息了,當前市面上基本上是以此種方式居多
上面兩種方式都是比較消耗資源的,而這裏咱們使用的是另外的一種方式來實現的,如[MobPush](http://mobpush.mob.com/)
udp無鏈接:其實現基本原理爲,客戶端建立socket並向服務端發送udp包,服務端接收到請求鏈接的udp包以後,將客戶端id與ip和端口號進行綁定,當要向某個客戶端id發送消息時,找到其對應的ip和端口號,而後將消息組裝成udp包發送便可,其大體流程以下:


而對於客戶端須要解決的以下幾個問題:服務器

1. 如何維護客戶端id與路由之間的綁定關係;網絡

2. 如何延長客戶端的在線狀態(app保活) app

3. 客戶端性能考慮socket

下面將針對這幾點進行逐步介紹tcp

維護客戶端與路由的綁定關係工具

這裏咱們須要瞭解一下NAT,所謂NAT就是,在局域網內部網絡中使用內部地址,而當內部節點要與外部網絡進行通信時,就在網關處,將內部地址替換成公用地址,從而在外部公網上正常使用。性能

因此當發送udp包到服務器時,服務器拿到的ip和端口實際上是客戶端在路由上映射的ip和端口,因此咱們須要維護路由上的映射表,這時就須要按期發送心跳包,以保證路由上的映射關係不會被清除掉。測試

1. 維護心跳包優化

主要做用是防止NAT超時, 和探測鏈接是否斷開,並根據實際狀況進行重連操做,其流程以下:ui


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佔用時間等等方面來控制內存和電量的消耗

相關文章
相關標籤/搜索