推送通知的跳轉(zhuǎn)處理和消息提醒
??消息推送功能在App開發(fā)中經(jīng)常用到,用于及時(shí)通知用戶,推送用戶訂閱的相關(guān)的信息。本篇文章并非詳細(xì)介紹如何集成和使用推送,關(guān)于推送相關(guān)知識的介紹,郭神已經(jīng)在慕課網(wǎng)中有相關(guān)的教程,大家如果想要深入研究,可以去觀看郭神的相關(guān)視頻。
效果演示
一、App處于運(yùn)行狀態(tài)下:
1.接收到通知,點(diǎn)擊通知打開對應(yīng)activity的演示:

2.接收到通知,點(diǎn)擊通知傳值并打開對應(yīng)的activity的演示:

二、App進(jìn)程處于銷毀狀態(tài),但是后臺依舊運(yùn)行著推送常駐的service:
1.接收到通知,點(diǎn)擊通知打開對應(yīng)的activity, activity啟動(dòng)流程,SplashActivity -> MainActivity - > 對應(yīng)的activity

2.接收到通知,點(diǎn)擊通知傳值并打開對應(yīng)的activity, activity啟動(dòng)流程,SplashActivity -> MainActivity - > 對應(yīng)的activity

接收到透傳消息后的處理
??目前第三方推送平臺也支持推送自定義消息,也有相應(yīng)的回調(diào)方法,我這里使用的是個(gè)推推送,自己定義一個(gè)類繼承 GTIntentService,并在清單文件中注冊,當(dāng)收到透傳消息時(shí),onReceiveMessageData()方法會進(jìn)行回調(diào),如果你喜歡使用其他的第三方推送平臺,思路也是一樣的,也是在對應(yīng)的回調(diào)方法中進(jìn)行以下處理。這里通過在透傳消息中設(shè)置一個(gè)標(biāo)識,如果是通知類消息,獲取到消息后,自己創(chuàng)建對應(yīng)的通知進(jìn)行顯示。
@Override
public void onReceiveMessageData(Context context, GTTransmitMessage msg) {
byte[] payload = msg.getPayload();
String message = new String(payload);
Log.i(TAG, "onReceiveMessageData: " + message);
try {
JSONObject jsonObject = new JSONObject(message);
int isNotification = jsonObject.optInt(PushConstants.IS_NOTIFICATION);
if (isNotification == 1) {
//屬于通知類的透傳消息
showNotification(context, jsonObject);
} else {
//提醒消息
dealNotifyMessage(context, jsonObject);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
showNotification()方法是對通知類的透傳消息的處理:
/**
* 根據(jù)消息內(nèi)容彈出通知框
*
* @param context
* @param jsonObject
*/
private void showNotification(Context context, JSONObject jsonObject) {
String title = jsonObject.optString(PushConstants.TITLE);
String content = jsonObject.optString(PushConstants.CONTENT);
if (TextUtils.isEmpty(title) || TextUtils.isEmpty(content)) {
return;
}
int pageNum = jsonObject.optInt(PushConstants.PAGE_NUMBER);
String contentId = jsonObject.optString(PushConstants.CONTENT_ID);
//設(shè)置點(diǎn)擊通知后是發(fā)送廣播,傳遞對應(yīng)的數(shù)據(jù)
Intent broadcastIntent = new Intent(context, NotificationReceiver.class);
Bundle bundle = new Bundle();
bundle.putInt(PushConstants.PAGE_NUMBER, pageNum);
bundle.putString(PushConstants.CONTENT_ID, contentId);
broadcastIntent.putExtras(bundle);
PendingIntent pendingIntent = PendingIntent.
getBroadcast(context, NotificationUtils.getRandowReqCode(), broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationUtils.showIntentNotification(context, title, content, title, pendingIntent, R.mipmap.ic_launcher, R.mipmap.ic_launcher);
}
dealNotifyMessage()方法是對提醒類的透傳消息的處理:
/**
* 處理提醒消息
*
* @param context
* @param jsonObject
*/
private void dealNotifyMessage(final Context context, JSONObject jsonObject) {
int notifyType = jsonObject.optInt(PushConstants.NOTIFY_TYPE, PushConstants.MESSAGE_CENTER_NOTIFY);
//判斷提醒信息的類型,做相應(yīng)的UI操作,由于此處處于IntentService中,做UI操作需要進(jìn)行線程的切換,這里使用了handler的post()方法切換
switch (notifyType) {
case PushConstants.MESSAGE_CENTER_NOTIFY:
//消息中心的提醒
handler.post(new Runnable() {
public void run() {
/* if (MainActivity.instance != null){
MainActivity.instance.showTabNotify(2);//底部我的tab顯示提示點(diǎn)
}
if (MineFragment.ivMessage != null){
MineFragment.ivMessage.setImageResource(R.mipmap.img_message_unread);//設(shè)置消息中心圖標(biāo)為未讀的
}
//更新消息表的未讀數(shù)
UnReadDao.saveOrUpdate(UnReadDao.getUnreadCount() + 1);*/
Log.i(TAG,"收到消息提醒,顯示小紅點(diǎn)");
}
});
break;
}
}
通知的點(diǎn)擊處理
??關(guān)于通知的點(diǎn)擊處理,showNotification()在創(chuàng)建通知的時(shí)候,需要設(shè)置PendingIntent:
//設(shè)置點(diǎn)擊通知后是發(fā)送廣播,傳遞對應(yīng)的數(shù)據(jù)
Intent broadcastIntent = new Intent(context, NotificationReceiver.class);
Bundle bundle = new Bundle();
bundle.putInt(PushConstants.PAGE_NUMBER, pageNum);//所要打開的頁面的編號
bundle.putString(PushConstants.CONTENT_ID, contentId);//id
broadcastIntent.putExtras(bundle);
PendingIntent pendingIntent = PendingIntent.
getBroadcast(context, NotificationUtils.getRandowReqCode(), broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
??我們通過PendingIntent.getBroadcast()方法創(chuàng)建PendingIntent,需要傳遞的數(shù)據(jù):pageNum屬于頁面的編號,定義在PushConstants中,用于判斷跳轉(zhuǎn)哪個(gè)頁面,如果是訂單的通知,則contentId則是對應(yīng)訂單的id,用于訂單詳情頁獲取訂單數(shù)據(jù)。
??當(dāng)點(diǎn)擊通知的時(shí)候,將會發(fā)送廣播并傳值給NotificationReceiver,具體的跳轉(zhuǎn)操作交由它進(jìn)行處理,接下來介紹最重要的內(nèi)容。
??NotificationReceiver接收到點(diǎn)擊通知后發(fā)出的廣播,在onReceive()方法中回調(diào),獲取傳過來的數(shù)據(jù)并進(jìn)行相應(yīng)的處理。
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
int pageNum = bundle.getInt(PushConstants.PAGE_NUMBER, 0);
String contentId = bundle.getString(PushConstants.CONTENT_ID, "");
Intent destinationIntent = null;//目標(biāo)intent
switch (pageNum) {
case PushConstants.PAGE_ORDER_DETAIL:
//訂單詳情頁
destinationIntent = new Intent(context, OrderDetailActivity.class);
destinationIntent.putExtra(OrderDetailActivity.ORDER_ID, contentId);//傳訂單的id
break;
case PushConstants.PAGE_MESSAGE_CENTER:
//消息中心
destinationIntent = new Intent(context, MessageCenterActivity.class);
break;
}
if (SystemUtils.isAppAlive(context, context.getPackageName())) {
//如果存活的話,就直接啟動(dòng)目標(biāo)Activity,但要考慮一種情況,就是app的進(jìn)程雖然仍然在
//但Task棧已經(jīng)空了,比如用戶點(diǎn)擊Back鍵退出應(yīng)用,但進(jìn)程還沒有被系統(tǒng)回收,如果直接啟動(dòng)
//目標(biāo)Activity,再按Back鍵就不會返回MainActivity了。所以在啟動(dòng) 目標(biāo)Activity前,要先啟動(dòng)MainActivity。
//將MainAtivity的launchMode設(shè)置成SingleTask, 或者在下面flag中加上Intent.FLAG_CLEAR_TOP,
//如果Task棧中有MainActivity的實(shí)例,就會把它移到棧頂,把在它之上的Activity都清理出棧,
//如果Task棧不存在MainActivity實(shí)例,則在棧頂創(chuàng)建
Log.i(TAG, "the app process is alive");
Intent mainIntent = new Intent(context, MainActivity.class);
mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (intentList == null){
intentList = new ArrayList<Intent>();
}
intentList.add(mainIntent);
//如果目標(biāo)intent不為空,一起打開
if (destinationIntent != null){
intentList.add(destinationIntent);
}
context.startActivities(intentList.toArray(new Intent[intentList.size()]));
} else {
//如果app進(jìn)程已經(jīng)被殺死,先重新啟動(dòng)app,將目標(biāo)Activity的啟動(dòng)參數(shù)傳入Intent中,參數(shù)經(jīng)過
//SplashActivity傳入MainActivity,此時(shí)app的初始化已經(jīng)完成,在MainActivity中就可以根據(jù)傳入
// 參數(shù)跳轉(zhuǎn)到目標(biāo)Activity中去了
Log.i(TAG, "the app process is dead");
Intent launchIntent = context.getPackageManager().
getLaunchIntentForPackage(context.getPackageName());
launchIntent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
launchIntent.putExtras(bundle);
context.startActivity(launchIntent);
}
}
??獲取到傳過來的數(shù)據(jù),通過pageNum(頁面編號)判斷要打開哪個(gè)activity,如果是訂單詳情頁的activity則需要傳遞一個(gè)contentId(此時(shí)則為訂單的id),作為詳情頁獲取數(shù)據(jù)的一個(gè)參數(shù)。
主要分為兩種情況考慮:
App進(jìn)程存在和App進(jìn)程不存在兩種情況。判斷App進(jìn)程是否存在,可以通過遍歷當(dāng)前手機(jī)系統(tǒng)中的所有運(yùn)行的進(jìn)程,通過判斷運(yùn)行的進(jìn)程的包名是否與當(dāng)前App的包名一致從而知道App進(jìn)程是否存在。
`/**
* 判斷應(yīng)用是否已經(jīng)啟動(dòng)
* @param context 一個(gè)context
* @param packageName 要判斷應(yīng)用的包名
* @return boolean
*/
public static boolean isAppAlive(Context context, String packageName){
ActivityManager activityManager =
(ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> processInfos
= activityManager.getRunningAppProcesses();
for(int i = 0; i < processInfos.size(); i++){
if(processInfos.get(i).processName.equals(packageName)){
Log.i("NotificationLaunch",
String.format("the %s is running, isAppAlive return true", packageName));
return true;
}
}
Log.i("NotificationLaunch",
String.format("the %s is not running, isAppAlive return false", packageName));
return false;
}`
??一、當(dāng)APP進(jìn)程存在的時(shí)候,在這種情況下,進(jìn)程雖然存在,但是任務(wù)??赡芤呀?jīng)為空,所在無論MainActivity是否已經(jīng)在棧內(nèi),都要打開MainActivity和目標(biāo)activity,必須注意的是,要將MainActivity的啟動(dòng)模式設(shè)置為SingleTask,即棧內(nèi)復(fù)用并且會清除位于其頂部的所有已經(jīng)開啟的activity,只留下MainActivity(一般App中MainActivity位于棧低)和目標(biāo)activity。
??二、當(dāng)App進(jìn)程已經(jīng)被殺死,點(diǎn)擊通知后啟動(dòng)App,將要傳遞的數(shù)據(jù)通過SplashActivity傳入MainActivity,在MainActivity根據(jù)數(shù)據(jù)判斷要打開的目標(biāo)activity,從而進(jìn)行跳轉(zhuǎn),SplashActivity中,通過判斷getIntent().getExtras()是否為空,如果不為空,則傳遞給MainActivity。
handler.postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = null;
if (PreUtils.getBoolean(SplashActivity.this, Constants.IS_LOGIN,false)){
//如果已經(jīng)處于登錄狀態(tài),直接進(jìn)入主界面
intent = new Intent(SplashActivity.this, MainActivity.class);
Bundle bundle = getIntent().getExtras();
if (bundle != null){
//如果是App沒開啟,點(diǎn)擊通知打開App,傳遞的bundle就可以到達(dá)MainActivity
intent.putExtras(bundle);
}
}else{
//未登錄,跳轉(zhuǎn)登錄界面
intent = new Intent(SplashActivity.this,LoginActivity.class);
}
startActivity(intent);
}
},3000);
MainActivity中,做了以下處理:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
jumpToOtherPage();//做相應(yīng)的跳轉(zhuǎn)
}
/**
* 如果有接收到傳遞過來的消息,則是點(diǎn)擊通知打開的,做相應(yīng)的跳轉(zhuǎn)
*/
private void jumpToOtherPage() {
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
//如果bundle不為空,則是點(diǎn)擊通知欄打開App進(jìn)來的,獲取傳過來的數(shù)據(jù)
int pageNum = bundle.getInt(PushConstants.PAGE_NUMBER, 0);
String id = bundle.getString(PushConstants.CONTENT_ID, "");
Intent destinationIntent = null;//目標(biāo)intent
switch (pageNum) {
case PushConstants.PAGE_ORDER_DETAIL:
//訂單詳情頁
destinationIntent = new Intent(this, OrderDetailActivity.class);
destinationIntent.putExtra(OrderDetailActivity.ORDER_ID, id);//傳對應(yīng)訂單的id
break;
case PushConstants.MESSAGE_CENTER_NOTIFY:
//消息中心頁面
destinationIntent = new Intent(this, MessageCenterActivity.class);
break;
}
if (destinationIntent != null) {
startActivity(destinationIntent);
}
}
}
??到此為止,關(guān)于App推送通知的處理介紹就結(jié)束了,至于數(shù)據(jù)的定義和相關(guān)頁面跳轉(zhuǎn)的判斷,每個(gè)人有各自的規(guī)則,我這里是通過在PushConstant中定義json對應(yīng)的key的常量以及頁面編號的常量,后臺和App端都定義了相同的常量,同時(shí)封裝好了對應(yīng)的推送工具類,適用于安卓和IOS端,需要提醒的是,IOS需要在個(gè)推管理后臺中配置推送證書。關(guān)于演示的App的demo和后臺的推送demo,大家可以通過下面的github地址進(jìn)行clone,希望可以幫助到大家。