本文中涉及到的所有代碼現(xiàn)已在Github上開源,地址: https://github.com/xuexiangjys/JPushSample
前言
極光推送是國內(nèi)最早做第三方消息推送平臺(tái)的公司桨啃,在消息推送界還是相對有影響力的。我最早是在2016年接觸到極光消息推送的清寇,那時(shí)候公司需要做消息推送業(yè)務(wù)吁恍,但是由于之前沒做過消息推送倍阐,且自建消息推送平臺(tái)代價(jià)太高,而且穩(wěn)不穩(wěn)定誰也不敢打包票,于是就選擇了當(dāng)時(shí)較為有名的極光推送斜筐。
那么當(dāng)時(shí)我為什么選擇極光推送呢冰啃?
1.免費(fèi)邓夕。免費(fèi)版本的每個(gè) Appkey 的最高推送頻率為 600 次/分鐘,而且沒有推送數(shù)量限制阎毅,者對于消息推送業(yè)務(wù)剛起步的企業(yè)來說焚刚,完全夠用了。
2.上手簡單扇调,文檔齊全矿咕。平臺(tái)官網(wǎng)上的文檔非常詳細(xì),下載下來的演示demo也非常豐富狼钮,通過簡單的幾行代碼就可以輕松接入碳柱。
3.功能豐富。比起小米推送熬芜、華為推送莲镣、信鴿推送、友盟推送來說涎拉,極光推送的功能是最全的瑞侮。想具體了解這幾種推送的可參見我的開源框架XPush.
4.社區(qū)支持度高。就拿我們Android來說鼓拧,不僅支持原生集成半火,還支持React Native、Flutter季俩、Weex慈缔、HBuilder、Cordova等混合開發(fā)方式种玛。
那么極光推送真的有那么好嗎藐鹤?其實(shí)也不全是,我在使用的過程中也發(fā)現(xiàn)了一些問題:
1.推送的到達(dá)率差一點(diǎn)赂韵。只要應(yīng)用退到后臺(tái)被系統(tǒng)回收或者被用戶殺死娱节,基本就很難再收到推送了。這點(diǎn)自然比不上那些手機(jī)廠商的推送祭示。
2.沒有免費(fèi)開放廠商通道推送集成肄满。想要集成廠商通道推送的話,還需要充錢成為VIP才行。
不過如果你是消息推送的初學(xué)者的話稠歉,我想極光推送肯定是你不二的選擇掰担。那么下面來跟著我學(xué)習(xí)如何使用極光推送吧!
快速集成指南
本文是基于
jpush:3.5.4
和jcore:2.2.6
版本介紹的怒炸,暫只介紹最新推薦的使用方法带饱,那些過時(shí)的用法這里我就不多介紹了,想了解的可以去極光推送官方文檔查看阅羹。
集成前的準(zhǔn)備工作
在接入極光推送前勺疼,首先需要獲取到應(yīng)用的AppKey,它是應(yīng)用的唯一標(biāo)識(shí)捏鱼。
1.創(chuàng)建極光推送開發(fā)者帳號(hào)
要?jiǎng)?chuàng)建極光推送開發(fā)者帳號(hào)执庐,請?jiān)L問極光推送官方網(wǎng)站: https://www.jiguang.cn/push
2.創(chuàng)建應(yīng)用
進(jìn)入極光控制臺(tái)后,點(diǎn)擊“創(chuàng)建應(yīng)用”按鈕导梆,填寫應(yīng)用名稱即可創(chuàng)建應(yīng)用成功轨淌。同時(shí)點(diǎn)擊“推送設(shè)置”,在 Android 版塊填上你的應(yīng)用包名看尼,選擇保存即可猿诸。
3.獲取應(yīng)用的AppKey
在極光控制臺(tái)點(diǎn)擊"應(yīng)用設(shè)置"中的"應(yīng)用信息",獲取應(yīng)用的AppKey狡忙。
引入依賴庫
方法一 jcenter自動(dòng)集成
使用 jcenter 自動(dòng)集成的開發(fā)者梳虽,不需要在項(xiàng)目中添加 jar 和 so,jcenter 會(huì)自動(dòng)完成依賴灾茁;在 AndroidManifest.xml 中不需要添加任何 JPush SDK 相關(guān)的配置窜觉,jcenter 會(huì)自動(dòng)導(dǎo)入。
1.配置項(xiàng)目的build.gradle
文件
android {
defaultConfig {
applicationId "com.xxx.xxx" //JPush平臺(tái)上注冊的應(yīng)用包名.
...
ndk {
//選擇要添加的對應(yīng) cpu 類型的 .so 庫北专。
abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'
//,'x86', 'x86_64', 'mips', 'mips64'
}
manifestPlaceholders = [
JPUSH_PKGNAME: defaultConfig.applicationId,
JPUSH_APPKEY : "你的 Appkey ",//值來自開發(fā)者平臺(tái)取得的AppKey
JPUSH_CHANNEL: "default_developer",
]
}
}
dependencies {
...
//引入JPush依賴庫
implementation 'cn.jiguang.sdk:jpush:3.5.4'
implementation 'cn.jiguang.sdk:jcore:2.2.6'
}
2.配置項(xiàng)目的AndroidManifest.xml
文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xuexiang.jpush">
<application>
<!-- 1.這個(gè)是自定義Service禀挫,要繼承極光JCommonService,可以在更多手機(jī)平臺(tái)上使得推送通道保持的更穩(wěn)定 -->
<service
android:name=".PushService"
android:enabled="true"
android:exported="false"
android:process=":pushcore">
<intent-filter>
<action android:name="cn.jiguang.user.service.action" />
</intent-filter>
</service>
<!-- 2.用戶自定義接收消息器,所有你想要知道的消息都在這里-->
<receiver android:name=".core.push.PushMessageReceiver">
<intent-filter>
<action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
</application>
</manifest>
點(diǎn)擊參見自動(dòng)集成的項(xiàng)目源碼
方法二 本地手動(dòng)集成
1.首先你需要先去下載SDK拓颓,下載地址: https://docs.jiguang.cn/jpush/resources/
2.解壓SDK语婴,將壓縮包下的libs內(nèi)容復(fù)制到項(xiàng)目的libs下
3.配置項(xiàng)目的build.gradle
文件
android {
defaultConfig {
applicationId "com.xxx.xxx" //JPush平臺(tái)上注冊的應(yīng)用包名.
...
ndk {
//選擇要添加的對應(yīng) cpu 類型的 .so 庫。
abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'
//,'x86', 'x86_64', 'mips', 'mips64'
}
manifestPlaceholders = [
JPUSH_PKGNAME: defaultConfig.applicationId,
JPUSH_APPKEY : "你的 Appkey ",//值來自開發(fā)者平臺(tái)取得的AppKey
JPUSH_CHANNEL: "default_developer",
]
}
sourceSets {
//設(shè)置libs目錄為so包的加載目錄
main {
jniLibs.srcDirs = ['libs']
}
}
}
4.配置項(xiàng)目的AndroidManifest.xml
文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.xxx.xxx">
<permission
android:name="${applicationId}.permission.JPUSH_MESSAGE"
android:protectionLevel="signature" />
<!-- Required 一些系統(tǒng)要求的權(quán)限驶睦,如訪問網(wǎng)絡(luò)等-->
<uses-permission android:name="${applicationId}.permission.JPUSH_MESSAGE" />
<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 用于開啟 debug 版本的應(yīng)用在6.0 系統(tǒng)上 層疊窗口權(quán)限 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<!-- Optional for location -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_TASKS" />
<application>
<!-- Required SDK核心功能-->
<activity
android:name="cn.jpush.android.ui.PushActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:theme="@android:style/Theme.NoTitleBar">
<intent-filter>
<action android:name="cn.jpush.android.ui.PushActivity" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="${applicationId}" />
</intent-filter>
</activity>
<!-- Required SDK 核心功能-->
<!-- 可配置android:process參數(shù)將PushService放在其他進(jìn)程中 -->
<service
android:name="cn.jpush.android.service.PushService"
android:exported="false"
android:process=":pushcore">
<intent-filter>
<action android:name="cn.jpush.android.intent.REGISTER" />
<action android:name="cn.jpush.android.intent.REPORT" />
<action android:name="cn.jpush.android.intent.PushService" />
<action android:name="cn.jpush.android.intent.PUSH_TIME" />
</intent-filter>
</service>
<!-- since 3.0.9 Required SDK 核心功能-->
<provider
android:name="cn.jpush.android.service.DataProvider"
android:authorities="${applicationId}.DataProvider"
android:exported="false" />
<!-- since 1.8.0 option 可選項(xiàng)砰左。用于同一設(shè)備中不同應(yīng)用的JPush服務(wù)相互拉起的功能。 -->
<!-- 若不啟用該功能可刪除該組件场航,將不拉起其他應(yīng)用也不能被其他應(yīng)用拉起 -->
<service
android:name="cn.jpush.android.service.DaemonService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="cn.jpush.android.intent.DaemonService" />
<category android:name="${applicationId}" />
</intent-filter>
</service>
<!-- since 3.1.0 Required SDK 核心功能-->
<provider
android:name="cn.jpush.android.service.DownloadProvider"
android:authorities="${applicationId}.DownloadProvider"
android:exported="true" />
<!-- Required SDK核心功能-->
<receiver
android:name="cn.jpush.android.service.PushReceiver"
android:enabled="true"
android:exported="false">
<intent-filter android:priority="1000">
<!--Required 顯示通知欄 -->
<action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY" />
<category android:name="${applicationId}" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
<!-- Optional -->
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<!-- Required SDK核心功能-->
<receiver
android:name="cn.jpush.android.service.AlarmReceiver"
android:exported="false" />
<!--since 3.3.0 Required SDK核心功能-->
<activity
android:name="cn.jpush.android.service.JNotifyActivity"
android:exported="true"
android:taskAffinity="jpush.custom"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
<intent-filter>
<action android:name="cn.jpush.android.intent.JNotifyActivity" />
<category android:name="${applicationId}" />
</intent-filter>
</activity>
<!-- *********************下面這兩個(gè)是需要你自己定義的**************************** -->
<!-- since 3.3.0 Required SDK 核心功能-->
<!-- 1.這個(gè)是自定義Service缠导,要繼承極光JCommonService,可以在更多手機(jī)平臺(tái)上使得推送通道保持的更穩(wěn)定 -->
<service
android:name=".PushService"
android:enabled="true"
android:exported="false"
android:process=":pushcore">
<intent-filter>
<action android:name="cn.jiguang.user.service.action" />
</intent-filter>
</service>
<!-- 2.用戶自定義接收消息器,所有你想要知道的消息都在這里-->
<receiver android:name=".core.push.PushMessageReceiver">
<intent-filter>
<action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<meta-data
android:name="JPUSH_CHANNEL"
android:value="${JPUSH_CHANNEL}" />
<!-- 值來自開發(fā)者平臺(tái)取得的AppKey-->
<meta-data
android:name="JPUSH_APPKEY"
android:value="${JPUSH_APPKEY}" />
</application>
</manifest>
點(diǎn)擊參見手動(dòng)集成的項(xiàng)目源碼
初始化
1.在Application中初始化JPush
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
initJPush();
}
}
/**
* 初始化極光推送
*/
private void initJPush() {
JPushInterface.setDebugMode(BuildConfig.DEBUG);
//只需要在應(yīng)用程序啟動(dòng)時(shí)調(diào)用一次該 API 即可
JPushInterface.init(this);
}
2.在應(yīng)用的第一個(gè)頁面申請權(quán)限(可選)
由于Android手機(jī)定制ROM太多溉痢,部分手機(jī)的通知欄權(quán)限默認(rèn)是關(guān)閉的僻造,需要用戶手動(dòng)打開憋他。如果不打開通知欄權(quán)限的話,即使你連上了推送髓削,也無法收到推送消息竹挡。
/**
* 申請定位、存儲(chǔ)和通知欄的權(quán)限
*
* @param activity
*/
public static void requestPermission(Activity activity) {
//打開通知欄的權(quán)限
if (JPushInterface.isNotificationEnabled(activity) == 0) {
new AlertDialog.Builder(activity)
.setCancelable(false)
.setMessage("通知權(quán)限未打開立膛,是否前去打開揪罕?")
.setPositiveButton("是", (d, w) -> JPushInterface.goToAppNotificationSettings(activity))
.setNegativeButton("否", null)
.show();
}
//申請定位、存儲(chǔ)權(quán)限
JPushInterface.requestPermission(activity);
}
運(yùn)行調(diào)試
當(dāng)完成以上步驟后旧巾,可直接運(yùn)行程序耸序,并查看logcat日志忍些,設(shè)置過濾條件為"JIGUANG"鲁猩,如果出現(xiàn)"Register succeed"和"registrationId:xxxxxxxxxxxxxx"字樣,即為集成成功罢坝!如下圖所示:
注意事項(xiàng):
- 一定要保證配置的AppKey和應(yīng)用的包名保持一致廓握。
- 一定要保證運(yùn)行的設(shè)備網(wǎng)絡(luò)是可用的,否則無法連接推送嘁酿。
混淆配置
配置項(xiàng)目的proguard-rules.pro
文件隙券。
-dontoptimize
-dontpreverify
-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }
-dontwarn cn.jiguang.**
-keep class cn.jiguang.** { *; }
-keep class cn.jiguang.** { *; }
-keep class * extends cn.jpush.android.service.JPushMessageReceiver{*;}
基礎(chǔ)功能使用
初始化
1.上面已經(jīng)講過了,推送初始化建議在自定義的 Application 中的 onCreate 中調(diào)用闹司,且推送初始化只需要調(diào)用一次即可娱仔。
JPushInterface.init(Context context);
2.推送初始化成功后,平臺(tái)會(huì)返回一個(gè)唯一的token令牌游桩,那就是RegistrationID
牲迫,獲取它的方法如下:
JPushInterface.getRegistrationID(Context context);
3.獲取當(dāng)前推送的連接狀態(tài)方法如下:
JPushInterface.getConnectionState(Context context)
推送狀態(tài)控制
1.停止推送。在某些業(yè)務(wù)中借卧,我們需要臨時(shí)暫停推送盹憎,例如賬戶退出登陸等,這個(gè)時(shí)候我們可以調(diào)用如下方法:
JPushInterface.stopPush(Context context);
需要注意的是铐刘,這里的停止推送只是個(gè)本地客戶端的操作陪每,并不會(huì)通知到推送服務(wù)平臺(tái)。其表現(xiàn)效果類似設(shè)備斷網(wǎng)镰吵,將不會(huì)收到任何推送消息檩禾,并且極光推送所有的其他 API 調(diào)用都無效,除了resumePush
恢復(fù)推送服務(wù)的方法疤祭。
2.恢復(fù)推送锌订。當(dāng)調(diào)用了停止推送的方法后,只有調(diào)用恢復(fù)推送的方法后画株,極光推送服務(wù)才能正常工作辆飘。方法如下:
JPushInterface.resumePush(Context context);
3.獲取推送的工作狀態(tài)啦辐。想要知道當(dāng)前推送服務(wù)是否正在工作,可通過如下方法:
JPushInterface.isPushStopped(Context context);
操作別名alias
別名在極光推送中尤為重要蜈项,通常我們用得最多的就是根據(jù)別名進(jìn)行推送芹关。我們通常的做法是用戶登陸后,業(yè)務(wù)平臺(tái)會(huì)返回一個(gè)平臺(tái)生成的唯一識(shí)別號(hào)作為推送的別名紧卒,然后后臺(tái)需要推送的時(shí)候侥衬,就直接拿著這個(gè)別名通知極光推送服務(wù)進(jìn)行消息推送。
1.綁定別名alias跑芳。
JPushInterface.setAlias(Context context, int sequence, String alias);
2.解綁別名alias轴总。
JPushInterface.deleteAlias(Context context, int sequence);
3.獲取綁定的別名alias。
JPushInterface.getAlias(Context context, int sequence);
注意事項(xiàng):
1.這里的sequence
主要就是操作識(shí)別碼博个,用于識(shí)別操作類型怀樟,由使用者自己定義。
2.以上所有的方法返回的都是void(都是異步操作)盆佣,方法的返回都在自定義的消息接收器中往堡,就是上面繼承JPushMessageReceiver由使用者自定義的廣播接收器中獲取。
3.別名相關(guān)操作的結(jié)果都在JPushMessageReceiver
的onAliasOperatorResult
方法中回調(diào)共耍,需要獲取別名操作結(jié)果的可重寫該方法虑灰。
操作標(biāo)簽Tags
標(biāo)簽好比一個(gè)分組,當(dāng)我們需要對某一類特殊群體進(jìn)行消息推送時(shí)痹兜,便可使用標(biāo)簽進(jìn)行推送穆咐。
1.增加標(biāo)簽Tags。這是一個(gè)增量請求字旭。
JPushInterface.addTags(Context context, int sequence, Set<String> tags);
2.刪除標(biāo)簽Tags对湃。
JPushInterface.deleteTags(Context context, int sequence, Set<String> tags);
3.獲取標(biāo)簽Tags。
JPushInterface.getAllTags(Context context, int sequence);
4.設(shè)置標(biāo)簽Tags谐算。這是一個(gè)全量請求熟尉,會(huì)覆蓋之前設(shè)置的標(biāo)簽。
JPushInterface.setTags(Context context, int sequence, Set<String> tags);
5.清除所有標(biāo)簽洲脂。
JPushInterface.cleanTags(Context context, int sequence);
6.查詢指定 tag 與當(dāng)前用戶綁定的狀態(tài)斤儿。
JPushInterface.checkTagBindState(Context context, int sequence, String tag);
注意事項(xiàng):
1.這里的sequence
和別名方法中的一樣,也是操作識(shí)別碼恐锦,用于識(shí)別操作類型往果,由使用者自己定義。
2.以上所有的方法返回的都是void(都是異步操作)一铅,方法的返回都在自定義的消息接收器中陕贮,就是上面繼承JPushMessageReceiver由使用者自定義的廣播接收器中獲取。
3.標(biāo)簽相關(guān)操作的結(jié)果都在JPushMessageReceiver
的onTagOperatorResult
方法中回調(diào)潘飘,需要獲取標(biāo)簽操作結(jié)果的可重寫該方法肮之。
4.checkTagBindState
方法的結(jié)果是在JPushMessageReceiver
的onCheckTagOperatorResult
方法中回調(diào)掉缺,需要獲取標(biāo)簽查詢匹配結(jié)果的可重寫該方法。
操作結(jié)果獲取
這里的操作主要包括:注冊戈擒、別名(綁定眶明、解綁、獲取)筐高、標(biāo)簽(添加搜囱、刪除、獲取柑土、設(shè)置蜀肘、清除、狀態(tài)檢查)稽屏、手機(jī)號(hào)設(shè)置等扮宠。由于極光提供的這些操作都是異步的,且方法不能直接返回結(jié)果和提供回調(diào)接口诫欠,因此只能通過重寫
JPushMessageReceiver
中相應(yīng)的方法獲取涵卵。
所有的操作結(jié)果都可以從JPushMessageReceiver
提供的回調(diào)方法中獲取浴栽。但是JPushMessageReceiver
最多只能作為消息的中轉(zhuǎn)站荒叼,使用起來極為不便,因此我們可以結(jié)合一些事件機(jī)制來處理典鸡,將這些結(jié)果包裝為一個(gè)個(gè)推送事件向外發(fā)出去被廓,這樣只需要在需要的地方訂閱一下事件就可以獲取到結(jié)果了塘匣。下面我以RxBus為例簡單編寫赌朋,使用的庫是我的開源庫RxUtil2
1.定義操作事件的類型,用于識(shí)別操作類型。上文中提到的sequence
參數(shù)就可以使用它扭吁。
/**
* 推送事件的類型
*/
@IntDef({TYPE_REGISTER, TYPE_UNREGISTER, TYPE_CONNECT_STATUS_CHANGED, TYPE_ADD_TAGS, TYPE_DEL_TAGS, TYPE_GET_TAGS, TYPE_BIND_ALIAS, TYPE_UNBIND_ALIAS, TYPE_GET_ALIAS})
@Retention(RetentionPolicy.SOURCE)
public @interface EventType {
/**
* 注冊推送
*/
int TYPE_REGISTER = 2000;
/**
* 取消注冊推送
*/
int TYPE_UNREGISTER = 2001;
/**
* 推送連接狀態(tài)發(fā)生變化
*/
int TYPE_CONNECT_STATUS_CHANGED = 2002;
/**
* 綁定別名
*/
int TYPE_BIND_ALIAS = 2010;
/**
* 解綁別名
*/
int TYPE_UNBIND_ALIAS = 2011;
/**
* 獲取別名
*/
int TYPE_GET_ALIAS = 2012;
/**
* 添加標(biāo)簽[增量]
*/
int TYPE_ADD_TAGS = 2020;
/**
* 刪除標(biāo)簽
*/
int TYPE_DEL_TAGS = 2021;
/**
* 獲取標(biāo)簽
*/
int TYPE_GET_TAGS = 2022;
/**
* 設(shè)置標(biāo)簽[全量]
*/
int TYPE_SET_TAGS = 2023;
/**
* 清除所有標(biāo)簽
*/
int TYPE_CLEAN_TAGS = 2024;
/**
* 查詢指定 tag 與當(dāng)前用戶綁定的狀態(tài)
*/
int TYPE_CHECK_TAG_BIND_STATE = 2025;
}
2.定義推送事件的載體.
該載體只需要定義三個(gè)成員變量:mType(事件類型)球碉、mIsSuccess(是否成功)蜓斧、mData(攜帶的數(shù)據(jù))。如下所示:
/**
* 推送事件的載體
*/
public final class PushEvent {
public static final String KEY_PUSH_EVENT = "com.xuexiang.jpushsample.core.push.event.KEY_PUSH_EVENT";
/**
* 事件類型
*/
private int mType;
/**
* 是否成功(也可以定義為int型的結(jié)果碼)
*/
private boolean mIsSuccess;
/**
* 攜帶的數(shù)據(jù)(也可以定義為String型的數(shù)據(jù))
*/
private Object mData;
public PushEvent(@EventType int type) {
mType = type;
}
public PushEvent(@EventType int type, boolean isSuccess) {
mType = type;
mIsSuccess = isSuccess;
}
public PushEvent(@EventType int type, Object data) {
mType = type;
mData = data;
}
public int getType() {
return mType;
}
public PushEvent setType(@EventType int type) {
mType = type;
return this;
}
public boolean isSuccess() {
return mIsSuccess;
}
public PushEvent setSuccess(boolean success) {
mIsSuccess = success;
return this;
}
public Object getData() {
return mData;
}
public PushEvent setData(Object data) {
mData = data;
return this;
}
}
3.事件處理并發(fā)送.
在JPushMessageReceiver
中重寫指定的方法睁冬,并將結(jié)果轉(zhuǎn)譯為一個(gè)個(gè)PushEvent
發(fā)送出去挎春。
/**
* 極光推送消息接收器
*/
public class PushMessageReceiver extends JPushMessageReceiver {
private static final String TAG = "JPush-Receiver";
//======================下面的都是操作的回調(diào)=========================================//
@Override
public void onRegister(Context context, String registrationId) {
Log.e(TAG, "[onRegister]:" + registrationId);
RxBusUtils.get().post(KEY_PUSH_EVENT, new PushEvent(EventType.TYPE_REGISTER, true, registrationId));
}
/**
* 連接狀態(tài)發(fā)生變化
*
* @param context
* @param isConnected 是否已連接
*/
@Override
public void onConnected(Context context, boolean isConnected) {
Log.e(TAG, "[onConnected]:" + isConnected);
RxBusUtils.get().post(KEY_PUSH_EVENT, new PushEvent(EventType.TYPE_CONNECT_STATUS_CHANGED, isConnected));
}
/**
* 所有和標(biāo)簽相關(guān)操作結(jié)果
*
* @param context
* @param jPushMessage
*/
@Override
public void onTagOperatorResult(Context context, JPushMessage jPushMessage) {
Log.e(TAG, "[onTagOperatorResult]:" + jPushMessage);
PushEvent pushEvent = new PushEvent(jPushMessage.getSequence(), jPushMessage.getErrorCode() == 0)
.setData(JPushInterface.getStringTags(jPushMessage.getTags()));
RxBusUtils.get().post(KEY_PUSH_EVENT, pushEvent);
}
/**
* 所有和別名相關(guān)操作結(jié)果
*
* @param context
* @param jPushMessage
*/
@Override
public void onAliasOperatorResult(Context context, JPushMessage jPushMessage) {
Log.e(TAG, "[onAliasOperatorResult]:" + jPushMessage);
PushEvent pushEvent = new PushEvent(jPushMessage.getSequence(), jPushMessage.getErrorCode() == 0)
.setData(jPushMessage.getAlias());
RxBusUtils.get().post(KEY_PUSH_EVENT, pushEvent);
}
/**
* 標(biāo)簽狀態(tài)檢測結(jié)果
*
* @param context
* @param jPushMessage
*/
@Override
public void onCheckTagOperatorResult(Context context, JPushMessage jPushMessage) {
Log.e(TAG, "[onCheckTagOperatorResult]:" + jPushMessage);
PushEvent pushEvent = new PushEvent(jPushMessage.getSequence(), jPushMessage.getErrorCode() == 0)
.setData(jPushMessage);
RxBusUtils.get().post(KEY_PUSH_EVENT, pushEvent);
}
}
4.在需要獲取結(jié)果的地方訂閱或者取消事件。
@Override
protected void initListeners() {
//訂閱推送事件
mPushEvent = RxBusUtils.get().onMainThread(KEY_PUSH_EVENT, PushEvent.class, this::handlePushEvent);
}
/**
* 處理推送事件豆拨,獲取操作的結(jié)果
* @param pushEvent
*/
private void handlePushEvent(PushEvent pushEvent) {
String content = pushEvent.getData().toString();
switch (pushEvent.getType()) {
case TYPE_BIND_ALIAS:
if (pushEvent.isSuccess()) {
tvAlias.setText(content);
XToastUtils.success("別名[" + content + "]綁定成功");
} else {
XToastUtils.error("別名[" + content + "]綁定失敗");
}
break;
case TYPE_UNBIND_ALIAS:
//別名解綁
break;
case TYPE_GET_ALIAS:
//獲取別名
break;
case TYPE_ADD_TAGS:
//添加標(biāo)簽
break;
case TYPE_DEL_TAGS:
//刪除標(biāo)簽
break;
case TYPE_GET_TAGS:
//獲取標(biāo)簽
break;
case TYPE_SET_TAGS:
//設(shè)置標(biāo)簽
break;
case TYPE_CLEAN_TAGS:
//清除標(biāo)簽
break;
case TYPE_CHECK_TAG_BIND_STATE:
//檢查標(biāo)簽
break;
........
default:
break;
}
}
@Override
public void onDestroyView() {
if (mPushEvent != null) {
//取消訂閱推送事件
RxBusUtils.get().unregister(KEY_PUSH_EVENT, mPushEvent);
mPushEvent = null;
}
super.onDestroyView();
}
點(diǎn)擊參見自定義JPushMessageReceiver的源碼
消息接收
自定義消息
自定義消息直奋,又稱之為透傳消息。顧名思義是由使用者自己定義一套解析格式的消息施禾,這種消息在接收到后不會(huì)有任何界面上的展示脚线,攜帶內(nèi)容為String型,通常的做法是傳一個(gè)json弥搞。這種比較靈活的消息推送方式是最常用的一種邮绿。但是這里需要注意的是渠旁,這種消息是一種應(yīng)用內(nèi)的消息,一旦應(yīng)用被殺死船逮,將無法及時(shí)收到該消息一死。
1.自定義消息體(CustomMessage)介紹
字段名 | 類型 | 字段說明 |
---|---|---|
messageId | String | 消息ID,對應(yīng)推送平臺(tái)上的消息唯一號(hào) |
message | String | 對應(yīng)推送消息界面上的“自定義消息內(nèi)容”字段 |
extra | String | 保存服務(wù)器推送下來的附加字段傻唾。這是個(gè) JSON 字符串投慈,對應(yīng)推送消息界面上的“可選設(shè)置”里的附加字段。 |
title | String | 消息的標(biāo)題(沒多大作用) |
2.自定義消息接收
如果想要接收自定義消息冠骄,只需重寫JPushMessageReceiver
中的onMessage
方法即可伪煤。在onMessage
方法中將會(huì)回調(diào)CustomMessage
自定義消息體。
普通通知消息
普通通知消息凛辣,就是在系統(tǒng)通知欄上顯示的消息抱既。但是如果通知的內(nèi)容為空,則不會(huì)在通知欄上展示通知扁誓。
1.通知消息體(NotificationMessage)介紹
字段名 | 類型 | 字段說明 |
---|---|---|
messageId | String | 消息ID防泵,對應(yīng)推送平臺(tái)上的消息唯一號(hào) |
notificationId | int | 通知欄的 Notification ID,用于清除 Notification |
notificationTitle | String | 通知的標(biāo)題蝗敢,對應(yīng)推送通知界面上的“通知標(biāo)題”字段 |
notificationContent | String | 通知的內(nèi)容捷泞,對應(yīng)推送通知界面上的“通知內(nèi)容”字段 |
notificationExtras | String | 附加字段,對應(yīng)推送通知界面上的“可選設(shè)置”里的附加字段 |
notificationTitle | String | 通知的標(biāo)題寿谴,對應(yīng)推送通知界面上的“通知標(biāo)題”字段 |
2.普通通知消息接收
如果想要接收自定義消息锁右,只需重寫JPushMessageReceiver
中的onNotifyMessageArrived
方法即可。在onNotifyMessageArrived
方法中將會(huì)回調(diào)NotificationMessage
通知消息體讶泰。
3.通知消息被點(diǎn)擊
在做消息推送開發(fā)的時(shí)候咏瑟,我們一定會(huì)有一個(gè)需求:希望用戶點(diǎn)擊通知后,能夠自動(dòng)跳轉(zhuǎn)到我們應(yīng)用的某個(gè)頁面痪署。這個(gè)頁面可能是某一個(gè)活動(dòng)宣傳頁面码泞,也有可能是某個(gè)新聞或者視頻頁面。這個(gè)時(shí)候狼犯,我們就需要對通知消息點(diǎn)擊后的動(dòng)作進(jìn)行自定義余寥。
那么我們該如何自定義通知消息被點(diǎn)擊后的動(dòng)作呢?很簡單辜王,我們只需要重寫JPushMessageReceiver
中的onNotifyMessageOpened
方法劈狐,在方法中讀取傳遞過來的參數(shù),然后結(jié)合頁面路由機(jī)制(例如:ARouter)直接跳轉(zhuǎn)至指定頁面即可呐馆。
下面我將通過兩種不同的途徑來實(shí)現(xiàn) 點(diǎn)擊通知消息后跳轉(zhuǎn)至某一特定界面:
1.重寫JPushMessageReceiver
中的onNotifyMessageOpened
方法肥缔。
public class PushMessageReceiver extends JPushMessageReceiver {
/**
* 點(diǎn)擊通知回調(diào)
*
* @param context
* @param message 通知消息
*/
@Override
public void onNotifyMessageOpened(Context context, NotificationMessage message) {
Log.e(TAG, "[onNotifyMessageOpened]:" + message);
//自定義打開到通知欄點(diǎn)擊后的容器頁
Intent intent = parseNotificationMessage(IntentUtils.getIntent(context, NotificationTransferActivity.class, null, true), message);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
ActivityUtils.startActivity(intent);
}
/**
* 解析極光通知消息:NotificationMessage
*/
public static Intent parseNotificationMessage(@NonNull Intent intent, NotificationMessage message) {
//這只是一個(gè)例子,暫時(shí)把跳轉(zhuǎn)的目標(biāo)頁設(shè)為 "通知信息展示"
intent.putExtra("pageName", "通知信息展示");
//通知標(biāo)題
intent.putExtra("title", message.notificationTitle);
//通知內(nèi)容
intent.putExtra("content", message.notificationContent);
//通知附帶拓展內(nèi)容
intent.putExtra("extraMsg", message.notificationExtras);
//通知附帶鍵值對
intent.putExtra("keyValue", message.notificationExtras);
return intent;
}
}
2.通過DeepLink
技術(shù)和通知欄中可選設(shè)置的自定義(打開指定頁面)
相結(jié)合的方法汹来。
(1)首先需要在AndroidManifest.xml
中定義deeplink攔截续膳。
<!--通知被點(diǎn)擊之后跳轉(zhuǎn)的頁面-->
<activity android:name=".activity.NotificationTransferActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="com.xuexiang.jpush"
android:path="/notification"
android:scheme="jpush" />
</intent-filter>
</activity>
(2)在容器界面NotificationTransferActivity
中解析傳遞過來的參數(shù)改艇。
/**
* 通知欄點(diǎn)擊后的容器頁
*
* deeplink格式
*
* jpush://com.xuexiang.jpush/notification?pageName=通知信息展示&title=這是一個(gè)通知&content=這是通知的內(nèi)容&extraMsg=xxxxxxxxx&keyValue={"param1": "1111", "param2": "2222"}
*
*/
@Router(path = "/push/notification/transfer")
public class NotificationTransferActivity extends BaseActivity {
@AutoWired
String pageName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
XRouter.getInstance().inject(this);
Uri uri = getIntent().getData();
Bundle bundle = getIntent().getExtras();
if (uri != null) {
//deeplink跳轉(zhuǎn)
pageName = uri.getQueryParameter("pageName");
bundle = Utils.parseNotificationDeepLinkUri(uri, bundle);
}
if (!StringUtils.isEmpty(pageName)) {
//打開指定頁面
if (openPage(pageName, bundle) == null) {
XToastUtils.toast("頁面未找到!");
finish();
}
} else {
XToastUtils.toast("頁面未找到坟岔!");
finish();
}
}
/**
* DeepLink的格式:
* jpush://com.xuexiang.jpush/notification?pageName=xxxxx&title=這是一個(gè)通知&content=這是通知的內(nèi)容&extraMsg=xxxxxxxxx&keyValue={"param1": "1111", "param2": "2222"}
* @param uri
* @param bundle
* @return
*/
public static Bundle parseNotificationDeepLinkUri(@NonNull Uri uri, Bundle bundle) {
if (bundle == null) {
bundle = new Bundle();
}
bundle.putString("pageName", uri.getQueryParameter("pageName"));
//通知標(biāo)題
bundle.putString("title", uri.getQueryParameter("title"));
//通知內(nèi)容
bundle.putString("content", uri.getQueryParameter("content"));
//通知附帶拓展內(nèi)容
bundle.putString("extraMsg", uri.getQueryParameter("extraMsg"));
//通知附帶鍵值對
bundle.putString("keyValue", uri.getQueryParameter("keyValue"));
return bundle;
}
}
注意:上面的openPage
方法主要使用了我的開源XPage,主要的作用就是Fragment頁面路由谒兄,加載一個(gè)Fragment頁面。
(3)發(fā)通知消息的時(shí)候社付,記得設(shè)置上自定義(打開指定頁面)
的鏈接承疲,如下圖所示:
- 鏈接示例,其中
jpush://com.xuexiang.jpush/notification
對應(yīng)上面AndroidManifest.xml
中配置的信息鸥咖。
jpush://com.xuexiang.jpush/notification?pageName=xxxxx&title=這是一個(gè)通知&content=這是通知的內(nèi)容&extraMsg=xxxxxxxxx&keyValue={"param1": "1111", "param2": "2222"}
- 設(shè)置示例:
消息接收處理
同樣的燕鸽,自定義消息和通知消息都是在
JPushMessageReceiver
的回調(diào)方法中獲取,和上面的操作結(jié)果類似啼辣,JPushMessageReceiver
最多只是作為消息的中轉(zhuǎn)站啊研,如果我們想要在任何頁面都能夠訂閱到接收到的消息,那么我們依舊可以和上面處理得一樣鸥拧,使用RxBus將這些消息向外發(fā)送出去党远。
下面我給出實(shí)現(xiàn)的簡要步驟:
1.定義消息的類型,這里暫時(shí)就是自定義消息和通知消息富弦。
/**
* 消息的類型
*/
@IntDef({TYPE_CUSTOM, TYPE_NOTIFICATION})
@Retention(RetentionPolicy.SOURCE)
public @interface MessageType {
/**
* 自定義消息
*/
int TYPE_CUSTOM = 1000;
/**
* 普通通知消息
*/
int TYPE_NOTIFICATION = 1001;
}
2.定義推送消息的載體.
目前為了偷懶沟娱,暫時(shí)就只定義了兩個(gè)成員變量:mType(消息類型)和mMessage(消息數(shù)據(jù))。如下所示:
/**
* 推送消息
*/
public final class PushMessage {
public static final String KEY_PUSH_MESSAGE = "com.xuexiang.jpushsample.core.push.event.KEY_PUSH_MESSAGE";
/**
* 消息類型
*/
private int mType;
/**
* 消息數(shù)據(jù)
*/
private Object mMessage;
public static PushMessage wrap(@MessageType int type, Object message) {
return new PushMessage(type, message);
}
public PushMessage(@MessageType int type, Object message) {
mType = type;
mMessage = message;
}
public int getType() {
return mType;
}
public PushMessage setType(int type) {
mType = type;
return this;
}
public <T> T getMessage() {
return (T) mMessage;
}
public PushMessage setMessage(Object message) {
mMessage = message;
return this;
}
public String getMessageType() {
switch (mType) {
case TYPE_CUSTOM:
return "自定義消息";
case TYPE_NOTIFICATION:
return "普通通知消息";
default:
return "未知消息";
}
}
}
3.消息接收并分發(fā).
在JPushMessageReceiver
中重寫onMessage
和onNotifyMessageArrived
方法舆声,并將結(jié)果轉(zhuǎn)譯為一個(gè)個(gè)PushMessage
發(fā)送出去花沉。
/**
* 極光推送消息接收器
*/
public class PushMessageReceiver extends JPushMessageReceiver {
private static final String TAG = "JPush-Receiver";
/**
* 收到自定義消息回調(diào)
*
* @param context
* @param message 自定義消息
*/
@Override
public void onMessage(Context context, CustomMessage message) {
Log.e(TAG, "[onMessage]:" + message);
RxBusUtils.get().post(KEY_PUSH_MESSAGE, PushMessage.wrap(MessageType.TYPE_CUSTOM, message));
}
/**
* 收到通知回調(diào)
*
* @param context
* @param message 通知消息
*/
@Override
public void onNotifyMessageArrived(Context context, NotificationMessage message) {
Log.e(TAG, "[onNotifyMessageArrived]:" + message);
RxBusUtils.get().post(KEY_PUSH_MESSAGE, PushMessage.wrap(MessageType.TYPE_NOTIFICATION, message));
}
}
此外柳爽,如果因業(yè)務(wù)需要媳握,在消息發(fā)送出去之前,我們還可以在發(fā)送前添加一個(gè)過濾器處理磷脯,對一些重復(fù)蛾找、無效的消息進(jìn)行過濾,或者對同時(shí)接收到的消息進(jìn)行消息合并等操作赵誓。
4.在需要獲取推送消息的地方訂閱打毛。
@Override
protected void initListeners() {
//訂閱消息
RxBusUtils.get().onMainThread(KEY_PUSH_MESSAGE, PushMessage.class, this::handlePushMessage);
}
/**
* 處理接收到的推送消息
*/
private void handlePushMessage(PushMessage pushMessage) {
tvType.setText(pushMessage.getMessageType());
tvMessage.setText(pushMessage.getMessage().toString());
}
@Override
public void onDestroyView() {
//取消訂閱
RxBusUtils.get().unregisterAll(KEY_PUSH_MESSAGE);
super.onDestroyView();
}
結(jié)尾
好啦,以上就是《極光推送之Android客戶端使用指南--基礎(chǔ)篇》的全部內(nèi)容,本文主要介紹了極光推送最常用也是最基礎(chǔ)的幾部分內(nèi)容,相信如果你能完全掌握上面的內(nèi)容的話,推送基本上也算是駕輕就熟了.下面我還將深入挖掘極光推送的幾個(gè)高級(jí)使用,敬請期待吧!