升级到Android 8.1后,startForeground将失败

将手机升级到8.1 Developer Preview后,我的后台服务不能再正常启动。

在我长时间运行的服务中,我实现了一个startForeground方法来启动在创建时调用的正在进行的通知。

@TargetApi(Build.VERSION_CODES.O) private fun startForeground() { // Safe call, handled by compat lib. val notificationBuilder = NotificationCompat.Builder(this, DEFAULT_CHANNEL_ID) val notification = notificationBuilder.setOngoing(true) .setSmallIcon(R.drawable.ic_launcher_foreground) .build() startForeground(101, notification) } 

错误信息:

 11-28 11:47:53.349 24704-24704/$PACKAGE_NAMEE/AndroidRuntime: FATAL EXCEPTION: main Process: $PACKAGE_NAME, PID: 24704 android.app.RemoteServiceException: Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification: Notification(channel=My channel pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x42 color=0x00000000 vis=PRIVATE) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1768) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6494) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 

无效的服务通知渠道 ,显然我的旧渠道DEFAULT_CHANNEL_ID不再适合API 27我假设。 什么是适当的渠道? 我试图查看文档

经过一些修改不同的解决方案后,你似乎在8.1必须创建自己的通知渠道。

 private fun startForeground() { val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel() } else { // If earlier version channel ID is not used // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) "" } val notificationBuilder = NotificationCompat.Builder(this, channelId ) val notification = notificationBuilder.setOngoing(true) .setSmallIcon(R.mipmap.ic_launcher) .setPriority(PRIORITY_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build() startForeground(101, notification) } @RequiresApi(Build.VERSION_CODES.O) private fun createNotificationChannel(): String{ val channelId = "my_service" val channelName = "My Background Service" val chan = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH) chan.lightColor = Color.BLUE chan.importance = NotificationManager.IMPORTANCE_NONE chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager service.createNotificationChannel(chan) return channelId } 

根据我的理解,后台服务现在显示为正常通知,用户可以通过取消选择通知通道来选择不显示。

更新:支持早于Oreo版本的更新解决方案

更新:增加了Kotlin标签

在Andorid 8.1上正常工作:

更新的示例(没有任何弃用的代码):

  public NotificationBattery(Context context) { this.mCtx = context; mBuilder = new NotificationCompat.Builder(context, CHANNEL_ID) .setContentTitle(context.getString(R.string.notification_title_battery)) .setSmallIcon(R.drawable.ic_launcher) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setChannelId(CHANNEL_ID) .setOnlyAlertOnce(true) .setPriority(NotificationCompat.PRIORITY_MAX) .setWhen(System.currentTimeMillis() + 500) .setGroup(GROUP) .setOngoing(true); mRemoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_view_battery); initBatteryNotificationIntent(); mBuilder.setContent(mRemoteViews); mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); if (AesPrefs.getBooleanRes(R.string.SHOW_BATTERY_NOTIFICATION, true)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(CHANNEL_ID, context.getString(R.string.notification_title_battery), NotificationManager.IMPORTANCE_DEFAULT); channel.setShowBadge(false); channel.setSound(null, null); mNotificationManager.createNotificationChannel(channel); } } else { mNotificationManager.cancel(Const.NOTIFICATION_CLIPBOARD); } } 

旧的剪辑(这是一个不同的应用程序 – 与上面的代码无关):

 @Override public int onStartCommand(Intent intent, int flags, final int startId) { Log.d(TAG, "onStartCommand"); String CHANNEL_ONE_ID = "com.kjtech.app.N1"; String CHANNEL_ONE_NAME = "Channel One"; NotificationChannel notificationChannel = null; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { notificationChannel = new NotificationChannel(CHANNEL_ONE_ID, CHANNEL_ONE_NAME, IMPORTANCE_HIGH); notificationChannel.enableLights(true); notificationChannel.setLightColor(Color.RED); notificationChannel.setShowBadge(true); notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); manager.createNotificationChannel(notificationChannel); } Bitmap icon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); Notification notification = new Notification.Builder(getApplicationContext()) .setChannelId(CHANNEL_ONE_ID) .setContentTitle(getString(R.string.obd_service_notification_title)) .setContentText(getString(R.string.service_notification_content)) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(icon) .build(); Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class); notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); notification.contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0); startForeground(START_FOREGROUND_ID, notification); return START_STICKY; } 

就我而言,这是因为我们试图发布通知而没有指定NotificationChannel

 public static final String NOTIFICATION_CHANNEL_ID_SERVICE = "com.mypackage.service"; public static final String NOTIFICATION_CHANNEL_ID_TASK = "com.mypackage.download_info"; public void initChannel(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID_SERVICE, "App Service", NotificationManager.IMPORTANCE_DEFAULT)); nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID_INFO, "Download Info", NotificationManager.IMPORTANCE_DEFAULT)); } } 

上面代码的最好的地方是在Application类的onCreate()方法中,所以我们只需要声明一次:

 public class App extends Application { @Override public void onCreate() { super.onCreate(); initChannel(); } } 

设置好之后,我们可以使用我们刚刚指定的channelId通知:

 Intent i = new Intent(this, MainActivity.class); i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent pi = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID_INFO); .setContentIntent(pi) .setWhen(System.currentTimeMillis()) .setContentTitle("VirtualBox.exe") .setContentText("Download completed") .setSmallIcon(R.mipmap.ic_launcher); 

然后,我们可以使用它来发布通知:

 int notifId = 45; NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); nm.notify(notifId, builder.build()); 

如果你想使用它作为前台服务的通知:

 startForeground(notifId, builder.build());