背景:最近的項目有個新需求,需要實現自定義通知樣式和內容,樣式如下
項目裏已經集成極光,只不過沒有做定製,採用的是極光api提供的BasicPushNotificationBuilder樣式,CustomPushNotificationBuilder倒是可以實現小改動的部分定製,但是需求這樣的變動不能實現,所以放棄通知,改用極光的自定義消息來處理,期間遇到一些問題,所以在此記錄下。
大的原則不變,還是採用極光推送,極光的集成配置這裏就不描述了。
主要的邏輯處理是在自定義的Receiver裏
1.首先設置全局的NotificationManager對象,來處理接收到自定義消息本地發通知
private NotificationManager nm;
2.onReceive方法裏處理,自定義消息、通知和點擊通知的跳轉邏輯,代碼如下
@Override
public void onReceive(Context context, Intent intent) {
try {
if (null == nm) {
nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
Bundle bundle = intent.getExtras();
Log.d(TAG, "[MyReceiver] onReceive - " + intent.getAction() + ", extras: " + printBundle(bundle));
if (JPushInterface.ACTION_REGISTRATION_ID.equals(intent.getAction())) {
String regId = bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID);
Log.d(TAG, "[MyReceiver] 接收Registration Id : " + regId);
//send the Registration Id to your server...
} else if (JPushInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
String title = bundle.getString(JPushInterface.EXTRA_TITLE);
String message = bundle.getString(JPushInterface.EXTRA_MESSAGE);
String extras = bundle.getString(JPushInterface.EXTRA_EXTRA);
Log.d(TAG, "[MyReceiver] 接收到推送下來的自定義消息: " + bundle.getString(JPushInterface.EXTRA_MESSAGE));
try {
PushJump push = new Gson().fromJson(extras, PushJump.class);
setCustomerMessage(context, push, title, message);
} catch (Exception e) {
e.printStackTrace();
}
} else if (JPushInterface.ACTION_NOTIFICATION_RECEIVED.equals(intent.getAction())) {
Log.d(TAG, "[MyReceiver] 接收到推送下來的通知");
//通知id
int notifactionId = bundle.getInt(JPushInterface.EXTRA_NOTIFICATION_ID);
//通知內容,對應 API 通知內容的 alert 字段
String alert = bundle.getString(JPushInterface.EXTRA_ALERT);
//通知的標題,對應 API 通知內容的 title 字段
String title = bundle.getString(JPushInterface.EXTRA_NOTIFICATION_TITLE);
//附加字段。這是個 JSON 字符串,對應 API 通知內容的 extras 字段
String extras = bundle.getString(JPushInterface.EXTRA_EXTRA);
Log.d(TAG, "[MyReceiver] 接收到推送下來的通知的ID: " + notifactionId);
} else if (JPushInterface.ACTION_NOTIFICATION_OPENED.equals(intent.getAction())) {
Log.d(TAG, "[MyReceiver] 用戶點擊打開了通知");
String extras = bundle.getString(JPushInterface.EXTRA_EXTRA);
//跳轉到消息列表
Intent i = new Intent(context, MessageListActivity.class);
i.putExtras(bundle);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(i);
} else if (JPushInterface.ACTION_RICHPUSH_CALLBACK.equals(intent.getAction())) {
Log.d(TAG, "[MyReceiver] 用戶收到到RICH PUSH CALLBACK: " + bundle.getString(JPushInterface.EXTRA_EXTRA));
//在這裏根據 JPushInterface.EXTRA_EXTRA 的內容處理代碼,比如打開新的Activity, 打開一個網頁等..
} else if (JPushInterface.ACTION_CONNECTION_CHANGE.equals(intent.getAction())) {
boolean connected = intent.getBooleanExtra(JPushInterface.EXTRA_CONNECTION_CHANGE, false);
Log.w(TAG, "[MyReceiver]" + intent.getAction() + " connected state change to " + connected);
} else {
Log.d(TAG, "[MyReceiver] Unhandled intent - " + intent.getAction());
}
} catch (Exception e) {
e.printStackTrace();
}
}
Action爲JPushInterface.ACTION_MESSAGE_RECEIVED分支,對應極光自定義消息,我們的邏輯就在這個分支處理。
JPushInterface.EXTRA_TITLE
JPushInterface.EXTRA_MESSAGE
JPushInterface.EXTRA_EXTRA
分別對應對應 API 消息內容的 title 字段、 message 字段、extras 字段
極光api地址https://docs.jiguang.cn/jpush/client/Android/android_api/
接收到自定義消息之後,由於我的項目需求裏自定義通知需要顯示網絡圖片,在彈出自定義通知之前,需要首先下載處理好網絡圖片(異步),否則通知的圖片不能加載顯示,可以增加對圖片大小判斷處理,圖片過大需要壓縮否則會造成通知卡頓,由於接口已經對圖片做過處理,這裏我就沒有對圖片處理。
下載圖片代碼
public void setCustomerMessage(final Context context, final PushJump push, final String title, final String message) {
new AsyncTask<String, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(String... params) {
return getBitmap(params[0]);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onPostExecute(final Bitmap result) {
super.onPostExecute(result);
showCustomerMessageNotify(context, result, title, message, push);
}
}.execute(push.getCustomImageUrl());
}
private Bitmap getBitmap(String urlStr) {
try {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
// 設置超時
conn.setConnectTimeout(6000);
conn.setDoInput(true);
// 緩存
conn.setUseCaches(true);
conn.connect();
int code = conn.getResponseCode();
Bitmap bitmap = null;
if (code == 200) {
InputStream is = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
}
return bitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
處理完圖片之後,可以做自定義通知的處理了,先貼上代碼
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void showCustomerMessageNotify(final Context context, Bitmap bitmap, String title, String message, final PushJump push) {
Intent intent2 = new Intent(context, CustomerDetailActivity.class);
intent2.putExtra("cus_id", push.getCustomId());
if (mCountIndex != 0) {
mCountIndex++;
} else {
mCountIndex = new Random().nextInt(5000) + 1;
}
PendingIntent pendingIntent2 = PendingIntent.getActivity(context, mCountIndex, intent2, PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder2 = new Notification.Builder(context);
//解決Android8.0以上版本收不到消息問題
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel mChannel = new NotificationChannel(NOTIFY_CHANNEL_ID, NOTIFY_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
//是否顯示通知指示燈
mChannel.enableLights(true);
//是否振動
mChannel.enableVibration(true);
nm.createNotificationChannel(mChannel);
builder2.setChannelId(NOTIFY_CHANNEL_ID);
}
builder2.setContentIntent(pendingIntent2)
.setSmallIcon(R.mipmap.app_logo)
.setAutoCancel(true)
.setContentTitle(title)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setDefaults(Notification.DEFAULT_ALL);
Notification notification = builder2.build();
final RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.push_notify);
remoteViews.setTextViewText(R.id.tv_type, TextUtils.isEmpty(title) ? "客戶到訪提醒" : title);
remoteViews.setTextViewText(R.id.tv_time, DateFormatUtils.getCurrentDate());
remoteViews.setTextViewText(R.id.tv_top, message);
remoteViews.setTextViewText(R.id.tv_middle, push.getGrade());
remoteViews.setTextViewText(R.id.tv_bottom, push.getLastFollow());
if (bitmap == null) {
remoteViews.setImageViewResource(R.id.img_head, R.mipmap.default_image);
} else {
remoteViews.setImageViewBitmap(R.id.img_head, bitmap);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
notification.bigContentView = remoteViews;
}
final RemoteViews remoteViewsSmall = new RemoteViews(context.getPackageName(), R.layout.push_notify_snamll);
remoteViewsSmall.setTextViewText(R.id.tv_title_small, TextUtils.isEmpty(title) ? "客戶到訪提醒" : title);
remoteViewsSmall.setTextViewText(R.id.tv_top, message);
remoteViewsSmall.setTextViewText(R.id.tv_middle, push.getGrade());
remoteViewsSmall.setTextViewText(R.id.tv_bottom, push.getLastFollow());
if (bitmap == null) {
remoteViewsSmall.setImageViewResource(R.id.iv_small, R.mipmap.default_image);
} else {
remoteViewsSmall.setImageViewBitmap(R.id.iv_small, bitmap);
}
notification.contentView = remoteViewsSmall;
nm.notify(mCountIndex, notification);
}
構建自定義通知,主要處理PendingIntent和RemoteViews
PendingIntent的構造四個參數,需要注意2、4參數,參數2保證每次構建的id不同(相同的話多個通知的情況下,傳值最近的通知會覆蓋之前的值)
加載自定義通知佈局,我採用了兩個佈局,最近的通知設置notification.bigContentView,其他的通知正常設置notification.contentView,
不可以全部設置bigContentView,因爲bigContentView本身需要觸摸滑動纔會顯示全部內容,全部設置的話,多條通知會顯示異常。
在構建通知的時候,保證通知的id不同即可
nm.notify(mCountIndex, notification);
由於是自定義的通知,需要設配8.0以上版本,手動設置NotificationChannel,否則8.0以上版本,接收不到通知。
//解決Android8.0以上版本收不到消息問題
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel mChannel = new NotificationChannel(NOTIFY_CHANNEL_ID, NOTIFY_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
//是否顯示通知指示燈
mChannel.enableLights(true);
//是否振動
mChannel.enableVibration(true);
nm.createNotificationChannel(mChannel);
builder2.setChannelId(NOTIFY_CHANNEL_ID);
}
通知的ChannelId和ChannelName設置全局即可,不要設置過長,避免系統截取。
安裝app之後,在手機設置-通知管理,設置app的通知渠道,可以鎖屏顯示。
以上操作是對極光的自定義消息處理,實現自定義通知效果。
對於正常的極光通知,在JPushInterface.ACTION_NOTIFICATION_RECEIVED分支下處理即可。
正常通知的樣式設置,在初始化極光之後操作
public static void init(Context context) {
JPushInterface.setDebugMode(true);
JPushInterface.init(context);
BasicPushNotificationBuilder builder = new BasicPushNotificationBuilder(context);
builder.statusBarDrawable = R.mipmap.app_logo;
builder.notificationFlags = Notification.FLAG_AUTO_CANCEL
| Notification.FLAG_SHOW_LIGHTS; //設置爲自動消失和呼吸燈閃爍
builder.notificationDefaults = Notification.DEFAULT_SOUND
| Notification.DEFAULT_VIBRATE
| Notification.DEFAULT_LIGHTS; // 設置爲鈴聲、震動、呼吸燈閃爍都要
JPushInterface.setPushNotificationBuilder(0, builder);
}
正常通知的跳轉在Action
JPushInterface.ACTION_NOTIFICATION_OPENED分支下處理