百度云推送:Android推送服務(wù)GCM最好的替代品

2013-09-27 08:05:09來源:Ryan's Zone作者:唐韌_Ryan

一、推送服務(wù)簡介
消息推送,顧名思義,是由一方主動(dòng)發(fā)起,而另一方與發(fā)起方以某一種方式建立連接并接收消息。在Android開發(fā)中,這里的發(fā)起方我們把它叫做推送服務(wù)器(Push Server),接收方叫做客戶端(Client)。

一、推送服務(wù)簡介

消息推送,顧名思義,是由一方主動(dòng)發(fā)起,而另一方與發(fā)起方以某一種方式建立連接并接收消息。在Android開發(fā)中,這里的發(fā)起方我們把它叫做推送服務(wù)器(Push Server),接收方叫做客戶端(Client)。相比通過輪詢來獲取新消息或通知,推送無論是在對(duì)客戶端的資源消耗還是設(shè)備耗電量來說都比輪詢要好,所以,目前絕大多數(shù)需要及時(shí)消息推送的App都采用Push的方式來進(jìn)行消息通知。

push

Android生態(tài)系統(tǒng)原本提供了類似于Apple iOS推送服務(wù)APNS的GCM(Google Cloud Messaging for Android),以前叫C2DM,但是由于某些原因,導(dǎo)致這項(xiàng)服務(wù)在國內(nèi)不是很好使,為了彌補(bǔ)這個(gè)不足,并且我朝各大同胞又想使用Android推送服務(wù),所以國內(nèi)各大平臺(tái)陸續(xù)推出了GCM的替代品,今天要介紹的就是其中一家,由百度提供的云推送。另外,國內(nèi)做消息推送服務(wù)的還有極光推送和個(gè)推等,他們的客戶包括新浪微博、淘寶等國內(nèi)一線大公司。

推送的實(shí)現(xiàn)技術(shù)簡單來說就是利用Socket維持Client和Server間的一個(gè)TCP長連接,通過這種方式能大大降低由輪詢方式帶來的Device的耗電量和數(shù)據(jù)訪問流量。目前,百度云推送提供的推送服務(wù)支持的單一消息體大小是4k,如果超過4k,則建議在消息內(nèi)攜帶服務(wù)請(qǐng)求URL進(jìn)行二次請(qǐng)求。目前,百度云推送針對(duì)Android端提供通知推送,文本消息推送以及富媒體推送。

二、使用場(chǎng)景

1. 單播消息推送

Push Server向指定的設(shè)備(Device)或是用戶(User)推送消息,一個(gè)用戶對(duì)應(yīng)一個(gè)userID,一個(gè)User可能擁有多臺(tái)Device,我們希望向同一個(gè)userID推送消息時(shí),他所有綁定了userID的Device都能收到消息。百度云推送給出的解決方案是通過Client向Push Server注冊(cè),并在Client端的監(jiān)聽端口取得Push Server返回的 channelID和userID,channelID指定一個(gè)終端,在向Push Server注冊(cè)的過程中,Device可以發(fā)送IMIE碼或者UUID作為唯一標(biāo)示,在Push Server注冊(cè)后再返回給Client生成的channelID和userID。這兩個(gè)ID獲取到后由開發(fā)者自行維護(hù),注冊(cè)完畢后,Push Server維護(hù)一個(gè)注冊(cè)設(shè)備列表,這個(gè)列表維護(hù)了userID和channelID以及與Device對(duì)應(yīng)的關(guān)系,當(dāng)需要向指定的設(shè)備或用戶推送消息時(shí),Push Server會(huì)首先遍歷這個(gè)設(shè)備列表,通過這兩個(gè)ID來做唯一性判斷并找到需要推送消息的Device,然后就可以進(jìn)行消息推送了。

push

實(shí)例:用戶A發(fā)表問題時(shí),記錄問題id及其對(duì)應(yīng)的A的userID(或channelID),用戶B發(fā)表問題回答時(shí),通過服務(wù)端API向問題id對(duì)應(yīng)的userID(或channelID)指向的Device推送答案。

2. 分組消息推送

百度云推送通過對(duì)Client設(shè)置標(biāo)簽(Tag)的方式來進(jìn)行用戶分組,Tag的產(chǎn)生方式可以是由Client維護(hù)也可以由Server收集,Push Server針對(duì)不同的Tag進(jìn)行推送過濾,最終將消息推送到指定的Client。無論是由Client主動(dòng)設(shè)置的Tag還是由Server根據(jù)用戶使用習(xí)慣收集的,都由Push Server進(jìn)行統(tǒng)一管理,在基于Tag的分組消息推送實(shí)現(xiàn)上,Push Server首先根據(jù)指定Tag從所有Tag下遍歷出的對(duì)應(yīng)的已注冊(cè)的Device,從而可以獲得與Device對(duì)應(yīng)的userID和channelID,繼而可以針對(duì)指定Tag進(jìn)行分組消息推送。對(duì)比單播消息推送,分組消息推送在推送周期上勢(shì)必要長一些,并且在待推消息列表的維護(hù)上也需要做一些處理,哪些消息是推送成功的,哪些是失敗的,這需要接收消息推送的Client在接收到消息后給Push Server一個(gè)消息回執(zhí),這樣就保證了消息送達(dá)的準(zhǔn)確性,如果消息推送失敗,則分組列表里的待推消息會(huì)繼續(xù)推送,直到推送消息成功。另外,在消息推送的實(shí)時(shí)性上,分組消息推送對(duì)比單播消息推送會(huì)根據(jù)分組消息隊(duì)列的先后存在一個(gè)消息接收的延時(shí),好比現(xiàn)在微信公眾賬號(hào)的推送,就是一個(gè)分組消息推送的實(shí)例,在消息接收的時(shí)效性上對(duì)比單播推送存在一定的延時(shí)性。

另外,還有一類消息推送使用場(chǎng)景,就是廣播消息,該類型可以理解為分組消息的一個(gè)特列,即向所有的Tag對(duì)應(yīng)的Client推送消息。廣播消息是對(duì)全體集合的一個(gè)消息推送,在消息隊(duì)列維護(hù)和消息推送時(shí)效性上比單個(gè)或幾個(gè)Tag的分組推送成本要高。

實(shí)例:給應(yīng)用提供喜好設(shè)置頁面,用戶勾選不同的類別,觸發(fā)對(duì)應(yīng)Tag的設(shè)置,這種方式是由Client主動(dòng)維護(hù)Tag。或者用戶閱讀了某個(gè)類別的圖書,觸發(fā)對(duì)應(yīng)Tag的設(shè)置,在服務(wù)端,給指定類別的圖書設(shè)置Tag,后續(xù)會(huì)根據(jù)服務(wù)端收集的Tag給應(yīng)用推送該Tag下的新書信息,這種方式就是由服務(wù)端來維護(hù)Tag分組。

三、百度云推送Android_SDK

百度提供了完整的Demo幫助開發(fā)者集成云推送服務(wù),推送服務(wù)SDK通過.jar包和.so文件的方式可以集成到我們自己的工程中。在此之前,需要到百度開發(fā)者中心進(jìn)行應(yīng)用注冊(cè)并獲取API Key,這個(gè)作為使用推送服務(wù)應(yīng)用的唯一標(biāo)示,具體流程我就不贅述了,需要使用的話可以直接訪問百度開發(fā)者中心進(jìn)行查看。

下面主要看看Android_SDK的整體概覽和內(nèi)部運(yùn)行機(jī)制:

structure

上圖是百度云推送Android_SDK的框架圖,通過SDK可以繞過復(fù)雜的Push HTTP/HTTPS API直接和Push服務(wù)器進(jìn)行交互,主要提供如下功能:

  • Push服務(wù)初始化以及Client注冊(cè)綁定
  • 創(chuàng)建或刪除標(biāo)簽(Tag)
  • 接收Push Server的通知并提供自定義展現(xiàn)消息方式
  • 推送統(tǒng)計(jì)分析功能,包括通知的點(diǎn)擊和刪除統(tǒng)計(jì)以及應(yīng)用使用情況統(tǒng)計(jì)
  • 富媒體推送

在Android端,總共實(shí)現(xiàn)了三個(gè)Receiver和一個(gè)Service,其中,一個(gè)Receiver是用來處理注冊(cè)綁定后接收服務(wù)端返回的channelID等信息:

1
2
3
4
5
6
7
8
9
10
<receiver android:name="com.baidu.android.pushservice.RegistrationReceiver"
android:process=": bdservice_v1">
<intent-filter>
<action android:name="com.baidu.android.pushservice.action.METHOD " />
<action android:name="com.baidu.android.pushservice.action.BIND_SYNC " />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED"/> <data android:scheme="package" />
</intent-filter>
</receiver>

第二個(gè)Receiver是用于接收系統(tǒng)消息以保證PushService正常運(yùn)行:

1
2
3
4
5
6
7
8
<receiver android:name="com.baidu.android.pushservice.PushServiceReceiver" android:process=": bdservice_v1">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="com.baidu.android.pushservice.action.notification.SHOW" /> 
<action android:name="com.baidu.android.pushservice.action.media.CLICK" />
</intent-filter>
</receiver>

第三個(gè)Receiver就是開發(fā)者自己實(shí)現(xiàn)的用來接收并處理推送消息:

1
2
3
4
5
6
7
<receiver android:name="your.package.PushMessageReceiver"> <intent-filter>
<!-- 接收 push 消息 -->
<action android:name="com.baidu.android.pushservice.action.MESSAGE" />
<!-- 接收 bind、setTags 等 method 的返回結(jié)果 -->
<action android:name="com.baidu.android.pushservice.action.RECEIVE" />
</intent-filter>
</receiver>

一個(gè)Service就是在后臺(tái)運(yùn)行的用于保障與Push Server維持長連接并做相關(guān)處理的后臺(tái)服務(wù):

1
2
<service android:name="com.baidu.android.pushservice.PushService"
android:exported="true" android:process=" bdservice_v1"/> <!-- push service end -->

在開發(fā)者自己需要處理的廣播接收器中,可以對(duì)接收到的推送消息進(jìn)行處理,Push消息通過 action為com.baidu.android.pushservice.action.MESSAGE的Intent把數(shù)據(jù)發(fā)送給客戶端your.package.PushMessageReceiver,消息格式由應(yīng)用自己決定,PushService只負(fù)責(zé)把服務(wù)器下發(fā)的消息以字符串格式透?jìng)鹘o客戶端。接口調(diào)用回調(diào)通過action為com.baidu.android.pushservice.action.RECEIVE的Intent 返回給your.package.PushMessageReceiver。

PushMessageReceiver.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
 * Push消息處理receiver
 * @Author Ryan
 * @Create 2013-8-6 下午5:59:38
 */
public class PushMessageReceiver extends BroadcastReceiver {
  public static final String TAG = PushMessageReceiver.class.getSimpleName();

  @Override
  public void onReceive(final Context context, Intent intent) {

      if (intent.getAction().equals(PushConstants.ACTION_MESSAGE)) {
          //獲取消息內(nèi)容
          String message = intent.getExtras().getString(
                  PushConstants.EXTRA_PUSH_MESSAGE_STRING);
          //消息的用戶自定義內(nèi)容讀取方式
          Log.i(TAG, "onMessage: " + message);

      } else if (intent.getAction().equals(PushConstants.ACTION_RECEIVE)) {
          //處理綁定等方法的返回?cái)?shù)據(jù)
          //PushManager.startWork()的返回值通過PushConstants.METHOD_BIND得到
          
          //獲取方法
          final String method = intent
                  .getStringExtra(PushConstants.EXTRA_METHOD);
          //方法返回錯(cuò)誤碼。若綁定返回錯(cuò)誤(非0),則應(yīng)用將不能正常接收消息。
          //綁定失敗的原因有多種,如網(wǎng)絡(luò)原因,或access token過期。
          //請(qǐng)不要在出錯(cuò)時(shí)進(jìn)行簡單的startWork調(diào)用,這有可能導(dǎo)致死循環(huán)。
          //可以通過限制重試次數(shù),或者在其他時(shí)機(jī)重新調(diào)用來解決。
          final int errorCode = intent
                  .getIntExtra(PushConstants.EXTRA_ERROR_CODE,
                          PushConstants.ERROR_SUCCESS);
          //返回內(nèi)容
          final String content = new String(
                  intent.getByteArrayExtra(PushConstants.EXTRA_CONTENT));
          
          //用戶在此自定義處理消息,以下代碼為demo界面展示用 
          Log.d(TAG, "onMessage: method : " + method);
          Log.d(TAG, "onMessage: result : " + errorCode);
          Log.d(TAG, "onMessage: content : " + content);
          
      }
  }

}

通過在入口Activity的onCreate方法中進(jìn)行推送服務(wù)的注冊(cè)綁定后,即可在推送管理后臺(tái)或是自己的應(yīng)用服務(wù)器上進(jìn)行消息推送的操作了。

1
PushManager.startWork(getApplicationContext(),PushConstants.LOGIN_TYPE_API_KEY, "you_api_key");

另外,云推送提供php、java等Server端的SDK供開發(fā)者在自己的服務(wù)器上實(shí)現(xiàn)推送服務(wù)進(jìn)行定制化管理和操作。

四、單服務(wù)單通道機(jī)制

百度云推送實(shí)現(xiàn)了單服務(wù)單通道的機(jī)制,如果在一臺(tái)Device上安裝了多款Push SDK的應(yīng)用,不會(huì)為每個(gè)應(yīng)用都創(chuàng)建PushService,而是會(huì)采用多應(yīng)用共享一個(gè)PushService的模式。這樣既能減少資源消耗也能降低網(wǎng)絡(luò)流量。PushService運(yùn)行于一個(gè)獨(dú)立進(jìn)程,沒有和主進(jìn)程運(yùn)行于同一進(jìn)程,所以主進(jìn)程不需要常駐內(nèi)存,當(dāng)有新的Push消息時(shí),PushService會(huì)通過Intent發(fā)送消息給主進(jìn)程進(jìn)行處理。通過Intent,以指定目標(biāo)應(yīng)用包名的方式,發(fā)送私有消息給應(yīng)用。應(yīng)用即不能接收不屬于自己的消息,也不能截取別人的消息,同時(shí)又降低了消耗,如下為示意圖:

structure

后記:如今,國內(nèi)提供Android推送服務(wù)的還有很多家,例如個(gè)推和極光推送等,實(shí)現(xiàn)的原理大同小異,開發(fā)者可以根據(jù)自身需要進(jìn)行選擇。在國內(nèi)用不到GCM,就創(chuàng)造Android Push Service for China自給,或者,出走!

關(guān)鍵詞:百度云推送AndroidGCM

贊助商鏈接: