近期由于業(yè)務需求,要換掉以前的推送,首先選擇了阿里云推送,官方介紹阿里移動推送(Alibaba Cloud Mobile Push)是基于大數(shù)據(jù)的移動智能推送服務,幫助App快速集成移動推送的功能,在實現(xiàn)高效、精確、實時的移動推送的同時,極大地降低了開發(fā)成本。讓開發(fā)者最有效地與用戶保持連接,從而提高用戶活躍度、提高應用的留存率。那么接下來我們就一起看看是如何接入的。
一. 在阿里云后臺創(chuàng)建自己的App
具體的步驟請詳看阿里云移動推送的技術文檔:阿里云移動推送快速入門
打開阿里云移動研發(fā)平臺EMAS控制臺,進行后面的操作
一、創(chuàng)建產(chǎn)品和應用
移動服務當前創(chuàng)建應用,需要兩步
- (1)添加產(chǎn)品(產(chǎn)品是一個集合的概念,產(chǎn)品下包含iOS應用、Android應用);
- (2)在產(chǎn)品處,點擊管理后,右上角點擊“創(chuàng)建應用”完成應用創(chuàng)建。
1、點擊頁面中的“添加產(chǎn)品”按鈕,即可創(chuàng)建一個新的產(chǎn)品
?2、輸入產(chǎn)品的基本信息創(chuàng)建App時需要輸入產(chǎn)品的名稱,上傳產(chǎn)品圖標,選擇產(chǎn)品分類。
3、產(chǎn)品創(chuàng)建成功
App創(chuàng)建成功后,產(chǎn)品列表會多出一個產(chǎn)品,強烈建議您去配置app。
4、創(chuàng)建產(chǎn)品對應的應用
在產(chǎn)品列表頁面,點擊已經(jīng)創(chuàng)建的產(chǎn)品按鈕,進入產(chǎn)品管理頁面。
?5、在產(chǎn)品管理頁面,點擊添加應用圖標,創(chuàng)建應用(目前需要分端創(chuàng)建)。
- (1)創(chuàng)建Android應用,并填寫APP名稱和PackageName
創(chuàng)建完成后,應用會出現(xiàn)在應用列表中:
二. Android SDK 3.0配置
安卓sdk的配置,個人強烈建議采用Maven庫快速集成(遠程同步),本博客也將采用遠程快速集成。
1. 在Project根目錄下build.gradle文件中配置maven庫URL
allprojects {
repositories {
jcenter()
maven {
url 'http://maven.aliyun.com/nexus/content/repositories/releases/'
}
}
}
2. 在對應的app下的build.gradle文件中添加對應依賴
android {
......
defaultConfig {
applicationId "com.xxx.xxx" //包名
......
ndk {
//選擇要添加的對應cpu類型的.so庫。
abiFilters 'armeabi', 'x86'
}
......
}
......
}
dependencies {
......
compile 'com.aliyun.ams:alicloud-android-push:3.1.4@aar'
// 或(二選一)
compile 'com.aliyun.ams:alicloud-android-push:3.1.4'
compile 'com.aliyun.ams:alicloud-android-utils:1.1.3'
compile 'com.aliyun.ams:alicloud-android-beacon:1.0.1'
compile 'com.aliyun.ams:alicloud-android-ut:5.4.0'
......
}
特別注意 : 如果在添加以上 abiFilter 配置之后android Studio出現(xiàn)以下提示:
NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin.
則在 Project 根目錄的gradle.properties文件中添加:
android.useDeprecatedNdk=true
3. appKey, appSecret配置
在AndroidManifest文件中設置appKey,appSecret:
<application android:name="*****">
<meta-data android:name="com.alibaba.app.appkey" android:value="*****"/> <!-- 請?zhí)顚懩阕约旱? appKey -->
<meta-data android:name="com.alibaba.app.appsecret" android:value="****"/> <!-- 請?zhí)顚懩阕约旱腶ppSecret -->
</application>
**特別提示:**com.alibaba.app.appkey和com.alibaba.app.appsecret為您App的對應信息,在推送控制臺APP列表頁的應用證書中獲取。appkey和appsecret請務必寫在application標簽下,否則sdk會報找不到appkey錯誤。如果您是百川云推送用戶,不能直接使用百川平臺的appKey和appSecret,需要登錄阿里云移動推送控制臺,登錄賬號為您的百川平臺賬號,并使用阿里云平臺的appKey,appSecret。
Permission權限配置:
<!-- 阿里云推送相關權限 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RESTART_PACKAGES" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
4. 消息接收Receiver配置
創(chuàng)建消息接收Receiver,繼承自com.alibaba.sdk.android.push.MessageReceiver,并在對應回調(diào)中添加業(yè)務處理邏輯,可參考以下代碼:
public class MyMessageReceiver extends MessageReceiver {
// 消息接收部分的LOG_TAG
public static final String REC_TAG = "receiver";
@Override
public void onNotification(Context context, String title, String summary, Map<String, String> extraMap) {
// TODO 處理推送通知
Log.e("MyMessageReceiver", "Receive notification, title: " + title + ", summary: " + summary + ", extraMap: " + extraMap);
}
@Override
public void onMessage(Context context, CPushMessage cPushMessage) {
Log.e("MyMessageReceiver", "onMessage, messageId: " + cPushMessage.getMessageId() + ", title: " + cPushMessage.getTitle() + ", content:" + cPushMessage.getContent());
}
@Override
public void onNotificationOpened(Context context, String title, String summary, String extraMap) {
Log.e("MyMessageReceiver", "onNotificationOpened, title: " + title + ", summary: " + summary + ", extraMap:" + extraMap);
}
@Override
protected void onNotificationClickedWithNoAction(Context context, String title, String summary, String extraMap) {
Log.e("MyMessageReceiver", "onNotificationClickedWithNoAction, title: " + title + ", summary: " + summary + ", extraMap:" + extraMap);
}
@Override
protected void onNotificationReceivedInApp(Context context, String title, String summary, Map<String, String> extraMap, int openType, String openActivity, String openUrl) {
Log.e("MyMessageReceiver", "onNotificationReceivedInApp, title: " + title + ", summary: " + summary + ", extraMap:" + extraMap + ", openType:" + openType + ", openActivity:" + openActivity + ", openUrl:" + openUrl);
}
@Override
protected void onNotificationRemoved(Context context, String messageId) {
Log.e("MyMessageReceiver", "onNotificationRemoved");
}
}
將該receiver添加到AndroidManifest.xml中(切不要忘記了)
<!-- 消息接收監(jiān)聽器 (用戶可自主擴展) -->
<receiver
android:name=".MyMessageReceiver"
android:exported="false"> <!-- 為保證receiver安全,建議設置不可導出,如需對其他應用開放可通過android:permission進行限制 -->
<intent-filter>
<action android:name="com.alibaba.push2.action.NOTIFICATION_OPENED" />
</intent-filter>
<intent-filter>
<action android:name="com.alibaba.push2.action.NOTIFICATION_REMOVED" />
</intent-filter>
<intent-filter>
<action android:name="com.alibaba.sdk.android.push.RECEIVE" />
</intent-filter>
</receiver>
如果是從V2.3.7及以下版本升級到V3.0.0及以上版本的用戶,需將<action android:name="org.agoo.android.intent.action.RECEIVE" />改為<action android:name="com.alibaba.sdk.android.push.RECEIVE" />,否則會接收不到推送。
5. Proguard配置
-keepclasseswithmembernames class ** {
native <methods>;
}
-keepattributes Signature
-keep class sun.misc.Unsafe { *; }
-keep class com.taobao.** {*;}
-keep class com.alibaba.** {*;}
-keep class com.alipay.** {*;}
-keep class com.ut.** {*;}
-keep class com.ta.** {*;}
-keep class anet.**{*;}
-keep class anetwork.**{*;}
-keep class org.android.spdy.**{*;}
-keep class org.android.agoo.**{*;}
-keep class android.os.**{*;}
-dontwarn com.taobao.**
-dontwarn com.alibaba.**
-dontwarn com.alipay.**
-dontwarn anet.**
-dontwarn org.android.spdy.**
-dontwarn org.android.agoo.**
-dontwarn anetwork.**
-dontwarn com.ut.**
-dontwarn com.ta.**
6. 在應用中注冊和啟動移動推送
- 首先通過
PushServiceFactory獲取到CloudPushService,然后調(diào)用register()初始化并注冊云推送通道,并確保Application上下文中進行初始化工作。 - 請參照以下代碼段進行初始化:
import android.app.Application;
import android.content.Context;
import android.util.Log;
import com.alibaba.sdk.android.push.CloudPushService;
import com.alibaba.sdk.android.push.CommonCallback;
import com.alibaba.sdk.android.push.noonesdk.PushServiceFactory;
public class MainApplication extends Application {
private static final String TAG = "Init";
@Override
public void onCreate() {
super.onCreate();
initCloudChannel(this);
}
/**
* 初始化云推送通道
* @param applicationContext
*/
private void initCloudChannel(Context applicationContext) {
PushServiceFactory.init(applicationContext);
CloudPushService pushService = PushServiceFactory.getCloudPushService();
pushService.register(applicationContext, new CommonCallback() {
@Override
public void onSuccess(String response) {
Log.d(TAG, "init cloudchannel success");
}
@Override
public void onFailed(String errorCode, String errorMessage) {
Log.d(TAG, "init cloudchannel failed -- errorcode:" + errorCode + " -- errorMessage:" + errorMessage);
}
});
}
}
【注意】:
- 移動推送的初始化必須在Application中,不能放到Activity中執(zhí)行。移動推送在初始化過程中將啟動后臺進程channel,必須保證應用進程和channel進程都執(zhí)行到推送初始化代碼。
- 如果設備成功注冊,將回調(diào)callback.onSuccess()方法。
- 但如果注冊服務器連接失敗,則調(diào)用callback.onFailed方法,并且自動進行重新注冊,直到onSuccess為止。(重試規(guī)則會由網(wǎng)絡切換等時間自動觸發(fā)。)
- 請在網(wǎng)絡通暢的情況下進行相關的初始化調(diào)試,如果網(wǎng)絡不通,或者App信息配置錯誤,在onFailed方法中,會有相應的錯誤碼返回,可參考錯誤處理。
啟動正常確認方法:
- 回調(diào)方法callback.onSuccess()被調(diào)用。以上文接入代碼為例,logcat將會打印以下日志:
11-24 12:55:51.096 15235-15535/com.alibaba.xxxx D/YourApp﹕ init cloudchannel success
- 確認cloudchannel初始化正常,在logcat日志中:輸入awcn關鍵字:
11-24 12:53:51.036 15235-15556/com.alibaba.xxxx E/awcn﹕ |[seq:AWCN1_1] AUTH httpStatusCode: 200
11-24 12:53:51.036 15235-15556/com.alibaba.xxxx E/awcn﹕ |[seq:AWCN1_1] status:AUTH_SUCC
確認DeviceId獲取正常:在初始化成功后使用
cloudPushService.getDeviceId()獲取deviceId,應該能夠成功獲取。如果集成移動推送的過程中遇到了
utdid沖突,可參考:阿里云-移動云產(chǎn)品SDK UTDID沖突解決方案
以上就是阿里云推送的創(chuàng)建、接入和sdk初始化的過程,做到這里我們就可以阿里云移動推送后臺發(fā)送測試推送了,但是在安卓8.0以上發(fā)現(xiàn)收不到推送,自8.0(API Level 26)起,Android 推出了NotificationChannel機制,旨在對通知進行分類管理。如果用戶App的targetSdkVersion大于等于26,且并未設置NotificaitonChannel,創(chuàng)建的通知是不會彈出的,所以我們要對8.0及其以上的設配設置NotificaitonChannel。
具體調(diào)用位置為:Application的onCreate,云推初始化前后都可以,具體代碼如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 通知渠道的id
String id = "1";
// 用戶可以看到的通知渠道的名字.
CharSequence name = "notification channel";
// 用戶可以看到的通知渠道的描述
String description = "notification description";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel mChannel = new NotificationChannel(id, name, importance);
// 配置通知渠道的屬性
mChannel.setDescription(description);
// 設置通知出現(xiàn)時的閃燈(如果 android 設備支持的話)
mChannel.enableLights(true);
mChannel.setLightColor(Color.RED);
// 設置通知出現(xiàn)時的震動(如果 android 設備支持的話)
mChannel.enableVibration(true);
mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
//最后在notificationmanager中創(chuàng)建該通知渠道
mNotificationManager.createNotificationChannel(mChannel);
}
在服務端的話一定也要設置:
// 指定notificaitonchannel id
pushRequest.setAndroidNotificationChannel("1");
特別注意:利用阿里云控制臺推送
使用阿里云控制臺推送通知時,需要設置 “高級設置”,在最下面的 “Android8.0 特殊配置:” ,填寫 “通知通道:”,也就是上述客戶端注冊的 NotificationChannel 的 id,例如上述代碼中NotificationChannel 為1,如下圖所示:
以上就是阿里云的全部推送,那么還有一個很致命的問題在這,目前谷歌的推送通道在大陸被墻了,一般的推送只能用service接收,并且app只能在啟動或者后臺的情況下收的到。當App進程被殺死后,推送是收不到的,那么該如何解決這種問題呢?
目前市面上華為、小米、oppo、vivo和魅族品牌占據(jù)大陸手機市場前五,每個品牌的手機都有自己的推送渠道,以下我簡稱輔助渠道。也就是說當app的進程被殺死的情況下,我們可以通過廠商渠道來推送,這樣即使App不啟動也可以收到推送。
接下來我們一起來看一下輔助渠道的接入,不要走開,這個是關鍵點哦?。。?!
一. 小米/華為/OPPO系統(tǒng)推送支持
- 輔助通道:移動推送針對小米、華為設備管控較嚴的情況特意接入華為,小米推送輔助通道以提高在華為、小米設備上的到達率。移動推送優(yōu)先選擇自有通道進行推送消息下發(fā),只有在自有通道斷連時選擇輔助通道下發(fā)消息。當前輔助通道通過華為、小米推送下發(fā)透傳消息,消息到達應用后經(jīng)移動推送SDK處理后觸發(fā)
onNotification,onMessage回調(diào)。小米、華為推送在下發(fā)透傳消息時并不保證會拉起被殺死進程(相關機制可參考小米、華為推送官網(wǎng)),所以輔助通道在進程被殺死情況下無法保證消息一定到達。 - 輔助彈窗:輔助彈窗通過系統(tǒng)通道下發(fā)通知,可以在進程被殺死情況下推送成功。由于輔助彈窗通過在對應設備上推送通知實現(xiàn),因而通過輔助彈窗下發(fā)的通知不會觸發(fā)
onNotification回調(diào)。當前移動推送已接入小米、華為、OPPO輔助彈窗。其中華為彈窗到達率統(tǒng)計只覆蓋用戶點擊華為彈窗推送通知的場景,未點擊部分暫未覆蓋;小米彈窗到達率統(tǒng)計覆蓋所有場景。
在對應的應用市場配置應用
在 小米開放平臺 注冊你的App, 得到相應的小米AppID,小米AppKey,小米AppSecert。在控制臺
應用配置設置你的小米AppSecert。(注意:最新的小米開放平臺是分開 push 功能的,需要在 push 功能區(qū) 開通/啟用 推送功能)。同理在 華為開發(fā)者聯(lián)盟 注冊 App,應用審核通過后,能夠得到華為的AppID和AppSecert。在控制臺
應用配置中設置你的華為AppID和AppSecert。(注意:最新的華為開放平臺是分開push功能的,需要在push功能區(qū) 開通/啟用 推送功能)在OPPO開放平臺 注冊OPPO企業(yè)開發(fā)者賬號,添加應用并開通oppo推送服務,目前應用需滿足:1.在oppo市場上架,2.評級為A,才能使用推送服務,具體政策可咨詢oppo客服。同樣需要在控制臺
應用配置設置你的OppoAppkey和OppoMasterSecret(AppServerSecret )。
二. 依賴Maven集成
1. 項目頂層build.gradle中添加Maven倉庫地址:
allprojects {
repositories {
maven {
url 'http://maven.aliyun.com/nexus/content/repositories/releases/'
}
}
}
2. gradle添加依賴:
dependencies {
compile 'com.aliyun.ams:alicloud-android-third-push:3.0.6@aar'
}
需要特別注意的地方:oppo 通道 需使用 v3.0.6 版本,應用滿足:1、在 oppo 市場上架,2、評級為 A.
3. Proguard配置
如果集成推送SDK的工程開啟代碼混淆,在Proguard配置的基礎上,需要添加以下輔助通道的Proguard配置。
# 小米通道
-keep class com.xiaomi.** {*;}
-dontwarn com.xiaomi.**
# 華為通道
-keep class com.huawei.** {*;}
-dontwarn com.huawei.**
# OPPO通道
-keep public class * extends android.app.Service
4. 在應用中初始化輔助通道
將以下代碼加入你application.onCreate()方法中初始通道。注意:輔助通道注冊務必在Application中執(zhí)行且放在推送SDK初始化代碼之后,否則可能導致輔助通道注冊失敗
// 注冊方法會自動判斷是否支持小米系統(tǒng)推送,如不支持會跳過注冊。
MiPushRegister.register(applicationContext, "小米AppID", "小米AppKey");
// 注冊方法會自動判斷是否支持華為系統(tǒng)推送,如不支持會跳過注冊。
HuaWeiRegister.register(applicationContext);
//GCM/FCM輔助通道注冊
GcmRegister.register(this, sendId, applicationId); //sendId/applicationId為步驟獲得的參數(shù)
// OPPO通道注冊
OppoRegister.register(applicationContext, appKey, appSecret); // appKey/appSecret在OPPO通道開發(fā)者平臺獲取
注意:1. 本方法會自動判斷是否支持小米系統(tǒng)推送,如不支持會跳過注冊。
2\. 如果控制臺配置了`小米/華為`的信息,app需要加對應的jar包依賴,不然會有crash的風險。
3\. OPPO通道是否注冊成功, 可以通過過濾`MPS:oppo`關鍵字查看, 注冊成功會打印`onRegister regid=****`相關日志, 否則檢 查參數(shù)是否正確填入;
4\. 客戶端接入完畢,服務端推送時如果設備無法收到推送,可先查看 [移動推送Android SDK:Android輔助通道和彈窗排查步驟](https://help.aliyun.com/knowledge_detail/57105.html)
接入輔助通道后,需要結合輔助彈窗來接收推送
1. 當前輔助彈窗已接入小米、華為、OPPO(小米輔助彈窗:v2.3.0及以上支持;華為輔助彈窗:v3.0.8及以上支持;OPPO輔助彈窗:v3.1.4及以上支持);
2. 當前華為輔助彈窗僅支持Emotion UI(華為定制ROM)4.1級以上版本的設備;
輔助彈窗在客戶端設置:
輔助彈窗送達的通知展示效果,和普通通知相同;
服務端指定輔助彈窗通道推送時,一定要指定通知點擊后要打開的Activity,該Activity需繼承自抽象類
AndroidPopupActivity(MiPushSystemNotificationActivity已廢棄,小米彈窗、華為彈窗、OPPO彈窗統(tǒng)一繼承AndroidPopupActivity),否則無法獲取到通知的相關信息,并且會影響通知到達率的統(tǒng)計;AndroidPopupActivity中提供抽象方法onSysNoticeOpened(),實現(xiàn)該方法后可獲取到輔助彈窗通知的標題、內(nèi)容和額外參數(shù),在通知點擊時觸發(fā),原本的通知回調(diào)onNotification()和onNotificationOpened()不適用于輔助彈窗;指定打開的托管彈窗Activity在AndroidManifest.xml中注冊時需要聲明屬性:
android:exported=true接入如下所示:
import com.alibaba.sdk.android.push.AndroidPopupActivity;
public class PopupPushActivity extends AndroidPopupActivity {
static final String TAG = "PopupPushActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
/**
* 實現(xiàn)通知打開回調(diào)方法,獲取通知相關信息
* @param title 標題
* @param summary 內(nèi)容
* @param extMap 額外參數(shù)
*/
@Override
protected void onSysNoticeOpened(String title, String summary, Map<String, String> extMap) {
Log.d("OnMiPushSysNoticeOpened, title: " + title + ", content: " + summary + ", extMap: " + extMap);
}
}
以上就是輔助通道和輔助彈窗的接入的全過程,需要特別的注意的是華為的開發(fā)者聯(lián)盟里push需要設置sha2和回調(diào)地址,這一條需要特別的注意。以上有不明白的小伙伴,可以評論留言咨詢。