2018-08-18 组长安排我解决一下app的notification为什么有的手机可以展示,有的手机展示不出来的问题,解决方案如下(此处是在kotlin的Service中执行操作):
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.os.Build
import android.os.IBinder
import android.support.v7.app.NotificationCompat
class BuySellService : Service() {
private var manager: NotificationManager? = null // 通知管理器
private val notificationChannelId = "channel_1" // Android8.0以后展示通知需要注册的通道编号
private val notificationChannelName = "channel_name_1" // Android8.0以后展示通知需要注册的通道名称
override fun onCreate() {
super.onCreate()
showNotification()
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
// 展示Notification的方法
private fun showNotification() {
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
// 创建渠道
createNotificationChannel()
// 展示通知
showHighNotification()
} else {
// 展示通知
showSimpleNotification()
}
}
// Android8.0以前的版本使用此方法启动Notification
private fun showSimpleNotification() {
val notification = NotificationCompat.Builder(this@BuySellService)
.setContentTitle("买卖客户端")
.setContentText("正在运行中...")
/**点击事件监听器**/
.setContentIntent(PendingIntent.getBroadcast(this@BuySellService, 0, Intent(Constant.FOREGROUND_NOTIFICATION_CLICKED), 0))
/**消失的监听器**/
.setDeleteIntent(PendingIntent.getBroadcast(this@BuySellService, 0, Intent(Constant.FOREGROUND_NOTIFICATION_DISMISS), 0))
.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.logo_buy_sell))
.setSmallIcon(R.drawable.logo_buy_sell) //设置小图标,4.x在右边,5.x在左边
/**通知产生的时间,会在通知信息里显示**/
.setWhen(System.currentTimeMillis())
/**设置该通知优先级**/
.setPriority(if (Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
NotificationCompat.PRIORITY_MAX
} else {
Notification.PRIORITY_MAX
})
/**设置这个标志当用户单击面板就可以让通知将自动取消**/
.setAutoCancel(false)
/**设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)**/
.setOngoing(true)
/**向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合:**/
/**.setDefaults(Notification.DEFAULT_VIBRATE or Notification.DEFAULT_SOUND)**/
.build().apply {
flags = flags or Notification.FLAG_ONGOING_EVENT or Notification.FLAG_NO_CLEAR or Notification.FLAG_FOREGROUND_SERVICE or Notification.FLAG_HIGH_PRIORITY
}
startForeground(1, notification)
}
// Android8.0以后展示Notification需要创建渠道
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
// 如果设置成 NotificationManager.IMPORTANCE_HIGH 或者 NotificationManager.IMPORTANCE_DEFAULT 的话会出现提示音,而且无法取消
val channel = NotificationChannel(notificationChannelId, notificationChannelName, NotificationManager.IMPORTANCE_LOW)
getNotificationManager().createNotificationChannel(channel)
}
}
// Notification管理器
private fun getNotificationManager(): NotificationManager {
if (manager == null) {
manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
return manager!!
}
// Android8.0以后的版本使用此方法启动Notification
private fun showHighNotification() {
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val notification = Notification.Builder(this@BuySellService, notificationChannelId)
.setContentTitle("买卖客户端")
.setContentText("正在运行中...")
/**点击事件监听器**/
.setContentIntent(PendingIntent.getBroadcast(this@BuySellService, 0, Intent(Constant.FOREGROUND_NOTIFICATION_CLICKED), 0))
/**消失的监听器**/
.setDeleteIntent(PendingIntent.getBroadcast(this@BuySellService, 0, Intent(Constant.FOREGROUND_NOTIFICATION_DISMISS), 0))
.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.logo_buy_sell))
.setSmallIcon(R.drawable.logo_buy_sell) //设置小图标,4.x在右边,5.x在左边
/**通知产生的时间,会在通知信息里显示**/
.setWhen(System.currentTimeMillis())
.setShowWhen(true)
/**设置该通知优先级**/
.setPriority(Notification.PRIORITY_MAX)
/**设置这个标志当用户单击面板就可以让通知将自动取消**/
.setAutoCancel(false)
/**设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)**/
.setOngoing(true)
/**向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合:**/
/**.setDefaults(Notification.DEFAULT_VIBRATE or Notification.DEFAULT_SOUND)**/
.build().apply {
flags = flags or Notification.FLAG_ONGOING_EVENT or Notification.FLAG_NO_CLEAR or Notification.FLAG_FOREGROUND_SERVICE or Notification.FLAG_HIGH_PRIORITY
}
startForeground(1, notification)
}
}
override fun onDestroy() {
super.onDestroy()
// 删除通道
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
getNotificationManager().deleteNotificationChannel(notificationChannelId)
}
}
}
java版本:
import android.app.NotificationChannel;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
public class BuySellService extends Service {
private NotificationManager manager = null; // 通知管理器
private String notificationChannelId = "channel_1"; // Android8.0以后展示通知需要注册的通道编号
private String notificationChannelName = "channel_name_1"; // Android8.0以后展示通知需要注册的通道名称
private Context context; // 上下文对象
@Override
public void onCreate() {
super.onCreate();
context = BuySellService.this;
if (NotificationUtil.isNotificationEnabled(context)) {
showNotification();
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
// 展示Notification的方法
private void showNotification() {
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
// 创建渠道
createNotificationChannel();
// 展示通知
showHighNotification();
} else {
// 展示通知
showSimpleNotification();
}
}
// Android8.0以前的版本使用此方法启动Notification
private void showSimpleNotification() {
Notification notification = new NotificationCompat.Builder(context, notificationChannelId)
.setContentTitle("买卖客户端")
.setContentText("正在运行中...")
/**点击事件监听器**/
.setContentIntent(PendingIntent.getBroadcast(context, 0, new Intent(Constant.FOREGROUND_NOTIFICATION_CLICKED), 0))
/**消失的监听器**/
.setDeleteIntent(PendingIntent.getBroadcast(context, 0, new Intent(Constant.FOREGROUND_NOTIFICATION_DISMISS), 0))
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher))
.setSmallIcon(R.mipmap.ic_launcher) //设置小图标,4.x在右边,5.x在左边
/**通知产生的时间,会在通知信息里显示**/
.setWhen(System.currentTimeMillis())
/**设置该通知优先级**/
.setPriority(Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN ? NotificationCompat.PRIORITY_MAX : Notification.PRIORITY_MAX)
/**设置这个标志当用户单击面板就可以让通知将自动取消**/
.setAutoCancel(false)
/**设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)**/
.setOngoing(true)
/**向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合:**/
/**.setDefaults(Notification.DEFAULT_VIBRATE or Notification.DEFAULT_SOUND)**/
.build();
notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR | Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_HIGH_PRIORITY;
startForeground(1, notification);
}
// Android8.0以后展示Notification需要创建渠道
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
// 如果设置成 NotificationManager.IMPORTANCE_HIGH 或者 NotificationManager.IMPORTANCE_DEFAULT 的话会出现提示音,而且无法取消
NotificationChannel channel = new NotificationChannel(notificationChannelId, notificationChannelName, NotificationManager.IMPORTANCE_LOW);
getNotificationManager().createNotificationChannel(channel);
}
}
// Notification管理器
private NotificationManager getNotificationManager() {
if (manager == null) {
manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return manager;
}
// Android8.0以后的版本使用此方法启动Notification
private void showHighNotification() {
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
Notification notification = new Notification.Builder(context, notificationChannelId)
.setContentTitle("买卖客户端")
.setContentText("正在运行中...")
/**点击事件监听器**/
.setContentIntent(PendingIntent.getBroadcast(context, 0, new Intent(Constant.FOREGROUND_NOTIFICATION_CLICKED), 0))
/**消失的监听器**/
.setDeleteIntent(PendingIntent.getBroadcast(context, 0, new Intent(Constant.FOREGROUND_NOTIFICATION_DISMISS), 0))
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher))
.setSmallIcon(R.mipmap.ic_launcher) //设置小图标,4.x在右边,5.x在左边
/**通知产生的时间,会在通知信息里显示**/
.setWhen(System.currentTimeMillis())
.setShowWhen(true)
/**设置该通知优先级**/
.setPriority(Notification.PRIORITY_MAX)
/**设置这个标志当用户单击面板就可以让通知将自动取消**/
.setAutoCancel(false)
/**设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)**/
.setOngoing(true)
/**向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合:**/
/**.setDefaults(Notification.DEFAULT_VIBRATE or Notification.DEFAULT_SOUND)**/
.build();
notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR | Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_HIGH_PRIORITY;
startForeground(1, notification);
}
}
@Override
public void onDestroy() {
super.onDestroy();
// 删除通道
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
getNotificationManager().deleteNotificationChannel(notificationChannelId);
}
// 停止前台服务--参数:表示是否移除之前的通知
stopForeground(true);
}
}
Notification工具类:
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class NotificationUtil {
/**
* 打开通知的方式
*
* @param context
*/
public static void openNotification(Context context) {
Intent localIntent = new Intent();
String packName = context.getPackageName();
int uid = context.getApplicationInfo().uid;
// 直接跳转到应用通知设置的代码:
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
localIntent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
localIntent.putExtra("app_package", packName);
localIntent.putExtra("app_uid", uid);
} else if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
localIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
localIntent.addCategory(Intent.CATEGORY_DEFAULT);
localIntent.setData(Uri.parse("package:" + packName));
} else {
// 4.4以下没有从app跳转到应用通知设置页面的Action,可考虑跳转到应用详情页面,
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 9) {
localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
localIntent.setData(Uri.fromParts("package", packName, null));
} else if (Build.VERSION.SDK_INT <= 8) {
localIntent.setAction(Intent.ACTION_VIEW);
localIntent.setClassName("com.android.settings", "com.android.setting.InstalledAppDetails");
localIntent.putExtra("com.android.settings.ApplicationPkgName", packName);
}
}
context.startActivity(localIntent);
}
/**
* 8.0以上获取通知栏状态
* @param context
* @return
*/
private static boolean isNotificationEnableV26(Context context) {
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
try {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Method sServiceField = notificationManager.getClass().getDeclaredMethod("getService");
sServiceField.setAccessible(true);
Object sService = sServiceField.invoke(notificationManager);
Method method = sService.getClass().getDeclaredMethod("areNotificationsEnabledForPackage", String.class, Integer.TYPE);
method.setAccessible(true);
return (boolean) method.invoke(sService, pkg, uid);
} catch (Exception e) {
return true;
}
}
/**
* 6.0以下通过反射获取通知的开关状态
*
* @param context
* @return
*/
private static boolean isNotificationEnableV24(Context context) {
AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
Class appOpsClass = null; /* Context.APP_OPS_MANAGER */
try {
appOpsClass = Class.forName(AppOpsManager.class.getName());
Method checkOpNoThrowMethod = appOpsClass.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE, String.class);
Field opPostNotificationValue = appOpsClass.getDeclaredField("OP_POST_NOTIFICATION");
int value = (int) opPostNotificationValue.get(Integer.class);
return ((int) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg) == AppOpsManager.MODE_ALLOWED);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
/**
* 获取通知栏的状态
*
* @param context
* @return
*/
public static boolean isNotificationEnabled(Context context) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return isNotificationEnableV26(context);
} else {
return isNotificationEnableV24(context);
}
}
}
判定Service是否正在后台运行的工具类:
import android.app.ActivityManager;
import android.content.Context;
public class ServiceUtil {
/**
* @param context
* @return
*/
public static boolean isServiceRunning(String strServiceName, Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (strServiceName.equals(service.service.getClassName())) {
return true;
}
}
return false;
}
}
特别鸣谢:https://github.com/linglongxin24/NotificationUtil
https://blog.csdn.net/z642385985/article/details/78583980?locationNum=9&fps=1
网友评论