接入友盟推送實戰(zhàn)
標簽(空格分隔): Android
請注意:###
- 消息推送SDK 不支持在QEMU模擬器上調(diào)試亲配,調(diào)試時請盡量使用真機吸耿。
- 友盟推送為什么對“廣播”的發(fā)送次數(shù)有3次限制抠蚣?博客
- 友盟消息推送API調(diào)用有什么頻率或者次數(shù)的限制 博客
如果分別下發(fā)了兩條消息嚣潜,卻只收到了一條巷懈,先不要著急。因為同一臺設(shè)備在1分鐘內(nèi)收到同一個應(yīng)用的多條通知時览绿,不會重復(fù)提醒策严,同時在通知欄里新的通知會替換掉舊的通知。
1饿敲、首先是自己申請一個APPKey妻导,不能偷懶,媽的怀各!我就是想偷一下懶栗竖,直接用主管的demo里的APPKey,結(jié)果搞了半天沒出渠啤,因為可能他在友盟上已經(jīng)注銷了這個AppKey對應(yīng)的應(yīng)用或者關(guān)閉了對這個應(yīng)用的推送功能狐肢,所以肯定是不能收到推送的。
2沥曹、然后是最坑的一個Bug份名,可能是中國人的軟肋吧碟联,就是依賴中文,雖然自己是個程序員僵腺,但是很多時候還是用能用中文就盡量用中文吧鲤孵。所以導(dǎo)致今天在友盟的官網(wǎng)上出現(xiàn)了用中文起名的悲劇,圖中的用圓圈畫住的部分辰如,千萬不要用中文普监,不然會出現(xiàn)意想不到的Bug×鸲担總的開說,在代碼的世界里凯正,最好一切都用英文!M泱廊散!
3、第三個就是自己真的太粗心了梧疲,竟然認為所以然就沒有加上這一步允睹,真是該打!
原文:
在所有的Activity 的onCreate方法或在應(yīng)用的BaseActivity的onCreate方法中添加:
PushAgent.getInstance(context).onAppStart();
**注意**: 此方法與統(tǒng)計分析sdk中統(tǒng)計日活的方法無關(guān)幌氮!請務(wù)必調(diào)用此方法缭受!
如果不調(diào)用此方法,不僅會導(dǎo)致按照"幾天不活躍"條件來推送失效该互,還將導(dǎo)致廣播發(fā)送不成功以及設(shè)備描述紅色等問題發(fā)生贯涎。可以只在應(yīng)用的主Activity中調(diào)用此方法慢洋,但是由于SDK的日志發(fā)送策略,有可能由于主activity的日志沒有發(fā)送成功陆盘,而導(dǎo)致未統(tǒng)計到日活數(shù)據(jù)普筹。
我竟然還沒看完就認為這行代碼只是用來統(tǒng)計App啟動的次數(shù)的,真是日了狗隘马!殊不知沒有這行代碼的話太防,是無法接收到推送的,這行代碼跟統(tǒng)計App的啟動次數(shù)是兩碼事酸员!
4蜒车、為什么一旦把應(yīng)用關(guān)了就收不到推送?
除了跟著官方文檔的說明步驟第四大步驟:輕松集成外幔嗦,還要記得在自的項目中添加權(quán)限和在application標簽中添加相應(yīng)的組件酿愧,這樣才能使應(yīng)用不再運行在前臺時,還可以開啟一個在后臺服務(wù)接受推送邀泉,才不會只能在應(yīng)用開啟時才能接到推送嬉挡!
權(quán)限如下:
//下面列出的是必選的權(quán)限钝鸽,還有可選的權(quán)限沒有列出,例如前臺是否可以顯示通知這個可選權(quán)限
<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.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
添加相應(yīng)的友盟組件如下:
//注意一共有三處地方要將友盟得包名改為自己的包名
<receiver
android:name="com.umeng.message.NotificationProxyBroadcastReceiver"
android:exported="false" >
</receiver>
<receiver
android:name="com.umeng.message.SystemReceiver"
android:process=":push" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver
android:name="com.umeng.message.MessageReceiver"
android:exported="false"
android:process=":push" >
<intent-filter>
<action android:name="org.agoo.android.intent.action.RECEIVE" />
</intent-filter>
</receiver>
<receiver
android:name="com.umeng.message.ElectionReceiver"
android:process=":push" >
<intent-filter>
<action android:name="org.agoo.android.intent.action.ELECTION_RESULT_V4" />
<category android:name="umeng" />
</intent-filter>
</receiver>
<receiver
android:name="com.umeng.message.RegistrationReceiver"
android:exported="false" >
<intent-filter>
<action android:name="com.zun1.whenask.intent.action.COMMAND" />//這是第一處地方要將友盟得包名改為自己的包名
</intent-filter>
</receiver>
<receiver android:name="com.umeng.message.UmengMessageBootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name="com.umeng.message.UmengService"
android:label="PushService"
android:exported="true"
android:process=":push" >
<intent-filter>
<action android:name="com.zun1.whenask.intent.action.START" />//這是第二處地方要將友盟得包名改為自己的包名
</intent-filter>
<intent-filter>
<action android:name="com.zun1.whenask.intent.action.COCKROACH" />//這是第三處地方要將友盟得包名改為自己的包名
</intent-filter>
<intent-filter>
<action android:name="org.agoo.android.intent.action.PING_V4" />
<category android:name="umeng" />
</intent-filter>
</service>
<service android:name="com.umeng.message.UmengIntentService"
android:process=":push" />
<service
android:name="com.umeng.message.UmengMessageIntentReceiverService"
android:process=":push"
android:exported="true" >
<intent-filter>
<action android:name="org.android.agoo.client.MessageReceiverService" />
</intent-filter>
<intent-filter>
<action android:name="org.android.agoo.client.ElectionReceiverService" />
</intent-filter>
</service>
<service android:name="com.umeng.message.UmengMessageCallbackHandlerService"
android:exported="false">
<intent-filter>
<action android:name="com.umeng.messge.registercallback.action" />
</intent-filter>
<intent-filter>
<action android:name="com.umeng.message.unregistercallback.action"/>
</intent-filter>
<intent-filter>
<action android:name="com.umeng.message.message.handler.action"/>
</intent-filter>
<intent-filter>
<action android:name="com.umeng.message.autoupdate.handler.action"/>
</intent-filter>
</service>
<service android:name="com.umeng.message.UmengDownloadResourceService" />
5庞钢、關(guān)于獲取不到device_token的問題——參考友盟官方論壇
本人證實可行的方法:
【原理:因為首次獲取device_token時用到的是 mPushAgent.enable(new IUmengRegisterCallback(),這種異步回調(diào)來獲取device_token基括。注意點有兩個:
第一:正是因為是異步回調(diào)的獲取,所以不能保證在用到device_token的時候就已經(jīng)完成了異步回調(diào)獲取河爹,所以會造成device_token還沒獲取到就已經(jīng)執(zhí)行了調(diào)用device_token的代碼,造成device_token為空的原因揪阶,所以解決方法是一定要把那些用到device_token的全部代碼放在獲取到device_token的代碼的后面昌抠,如第39-39行
第二:注意到第一點沒有鲁僚?首次獲取device_token時用到的是 mPushAgent.enable(new IUmengRegisterCallback(),因為是首次啟動應(yīng)用及安裝后運行的這種情況才會執(zhí)行到它冰沙,如果退出應(yīng)用后再次打開時就不走這個方法了侨艾,所以要做好是否第一次啟動應(yīng)用的判斷,如第10-11行拓挥、36-37行、40-41行都是對是否第一次啟動應(yīng)用的判斷以及數(shù)值記錄】
private PushAgent mPushAgent;
private String device_token;
private static final int REQUEST_CODE=200;
private SharedPreferences sp;
private SharedPreferences.Editor editor;
mPushAgent = PushAgent.getInstance(this);
mPushAgent.onAppStart();
//在這里判斷是否是第一次啟動應(yīng)用
Boolean isFirstOpen=sp.getBoolean("isFirstOpen",true);//默認是true当叭,第一次啟動盖灸!
if(isFirstOpen){//如果是第一次啟動
Log.i("Lee","第一次啟動應(yīng)用");
mPushAgent.enable(new IUmengRegisterCallback() {//用這種回調(diào)方法的話,只有在首次安裝時才會執(zhí)行醉箕!用于初次獲取測試設(shè)備的Device Token。
@Override
public void onRegistered(final String s) {
Log.i("Lee","go into onRegistered()");
new Handler().post(new Runnable() {
@Override
public void run() {
Log.i("Lee","go into run()");
int count=0;
do {//關(guān)鍵是在這里一直循環(huán)獲取device_token讥裤,這里是在主線程不斷循環(huán)姻报,為什么不會造成ANR?難道是異步回調(diào)的原因嗎剧辐?
count++;
device_token = UmengRegistrar.getRegistrationId(SetLanguage.this);
// device_token = mPushAgent.getRegistrationId();
try {
Thread.sleep(1000);
} catch (InterruptedException e)
{
Log.i("LeeInterruptedException",e.getMessage());
}
} while (TextUtils.isEmpty(device_token));//當device_token為null或""時,即還沒獲取到device_token溉奕,若是就繼續(xù)循環(huán)
Log.i("Lee count",""+count);
Log.i("Lee ——onRegistered", "device_token=" + device_token);
editor.putBoolean("isFirstOpen",false);//已運行過應(yīng)用一次忍啤,
editor.commit();
//以下是添加拿到device_token后的操作
AAAAAA..................
}else {
Log.i("Lee","不是第一次啟動應(yīng)用");
//mPushAgent.enable();//因為上面已經(jīng)開啟了mPushAgent.enable(new IUmengRegisterCallback(),所以不用再次開啟mPushAgent.enable()
//device_token = UmengRegistrar.getRegistrationId(this);//使用這一句跟下面這句是一樣的
device_token = mPushAgent.getRegistrationId();
Log.i("Lee ——Not onRegistered", "device_token=" + device_token);
//以下是添加拿到device_token后的操作
AAAAAA..................
}
下面這個是對上面代碼的一些優(yōu)化鳄梅,因為經(jīng)過試驗未檩,就算是寫了循環(huán)去獲取device_token ,但是結(jié)果顯示循環(huán)只執(zhí)行了一次孙蒙,所以說用不著循環(huán)(如果你過大膽的話悲雳,不過建議還是要用循環(huán)比較安全保守)
PushAgent mPushAgent = PushAgent.getInstance(this);
mPushAgent.onAppStart();
mPushAgent.enable(new IUmengRegisterCallback() {
@Override
public void onRegistered(final String registrationId) {
//onRegistered方法的參數(shù)registrationId即是device_token
//new Handler().post(new Runnable() {
// @Override
// public void run() {
// Log.i("xiyuan", "device_token=" + registrationId);
// }
// });
//其實可以直接不寫一個handler來執(zhí)行一個runnable,然后再Run方法里打log坦胶,可以直接在onRegistered里直接打一個Log(不用寫handler晴楔,如下) Log.i("xiyuan", "device_token=" + s);
}
});
//mPushAgent.enable();//因為上面已經(jīng)開啟了mPushAgent.enable(new IUmengRegisterCallback(),所以不用再次開啟mPushAgent.enable()
//String device_token = UmengRegistrar.getRegistrationId(this);//使用這一句跟下面這句是一樣的
//關(guān)鍵點是這里税弃,不要在onRegistered方法里獲取device_token钙皮,雖然onRegistered方法的參數(shù)registrationId就是device_token顽决,但是經(jīng)過本人實踐短条,是獲取不到的才菠!所以才會在enable后加上下面這句赋访。
String device_token = mPushAgent.getRegistrationId();
Log.i("xiyuani 2", "device_token=" + device_token);
下面是自己另外做的調(diào)試代碼缓待,錯誤例子:
//不使用異步的回調(diào)去一次獲取device_token,第一次安裝時獲取不到渠牲,但那時第二次啟動時可以獲取到
// mPushAgent.enable();
// device_token = mPushAgent.getRegistrationId();
// //device_token = UmengRegistrar.getRegistrationId(SetLanguage.this);
// Log.i("Lee —NoCallBack", "device_token=" + device_token);
//不使用異步的回調(diào)去循環(huán)獲取device_token,這樣的不斷循環(huán)會出現(xiàn)ANR!
// mPushAgent.enable();
// int count=0;
// do {//關(guān)鍵是在這里一直循環(huán)獲取device_token
// count++;
// device_token = mPushAgent.getRegistrationId();
// //device_token = UmengRegistrar.getRegistrationId(SetLanguage.this);
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e)
// {
// Log.i("LeeInterruptedException",e.getMessage());
// }
// } while (TextUtils.isEmpty(device_token));//判斷device_token是否為空瘫镇,若是就繼續(xù)循環(huán)
// Log.i("Lee count",""+count);
// Log.i("Lee —NoCallBack", "device_token=" + device_token);
下面這個是用計時器去不斷獲取device_token 答姥,但那時還沒驗證
if (device_token.isEmpty()) {
timer.schedule(new TimerTask() {
@Override
public void run() {
if (device_token.isEmpty() && count < 10) {
String temp = UmengRegistrar.getRegistrationId(SetLanguage.this);
if (temp == null || temp.isEmpty()) {
count++;
} else {
device_token = temp;
count = 0;
}
} else {
count = 0;
cancel();
Log.d("device_token1", "task has cancle and deviceToken=" + device_token);
}
}
}, 0, 800);
}
6、關(guān)于API23 6.0的系統(tǒng)集成友盟時尚粘,權(quán)限android.permission.WRITE_SETTINGS"——系統(tǒng)設(shè)置權(quán)限的授權(quán)的解決方案
android.permission.WRITE_SETTINGS不能自動授權(quán)敲长,也不能運行時請求授權(quán),咋整靶屑痢钳降?通過打開Intent來讓用戶設(shè)置。貌似SETTINGS的權(quán)限只能這么處理铲觉,from CommonsWare research Android 6.0 changes
@TargetApi(Build.VERSION_CODES.M)//當現(xiàn)在運行程序的系統(tǒng)是API23時才會執(zhí)行這個方法
public void permissionToWRITE_SETTINGS(){
if(Build.VERSION.SDK_INT >= 23){
if(!Settings.System.canWrite(this)){
//打開設(shè)置界面讓用戶設(shè)置是否授權(quán)系統(tǒng)設(shè)置權(quán)限
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
}
}
}
@TargetApi(Build.VERSION_CODES.M)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE) {
if (Settings.System.canWrite(this)) {
//檢查返回結(jié)果
Toast.makeText(SetLanguage.this, "WRITE_SETTINGS permission granted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(SetLanguage.this, "WRITE_SETTINGS permission not granted", Toast.LENGTH_SHORT).show();
}
}
}
7撵幽、在友盟注冊時忽略的一個小問題導(dǎo)致接收不到推送
官方文檔說明如下:
![此處輸入圖片的描述](http://o6uwc0k25.bkt.clouddn.com/API.png)
app_master_secret要填寫在安卓端的androidmanifest中還是只是填寫在服務(wù)器的配置中礁击?還是兩個都要填寫?不管怎樣链烈,兩個端都寫上就最安全挚躯!
使用情況:在有自己的服務(wù)器轉(zhuǎn)接友盟的推送時,記得加上自己的服務(wù)器IP地址
![此處輸入圖片的描述](http://o6uwc0k25.bkt.clouddn.com/QQ%E6%88%AA%E5%9B%BE20160817145231.png)