Android 8.1升级后start前台失败

升级我的手机到8.1开发者预览后,我的后台服务不再正常启动。

在我的长时间运行的服务中,我实现了一个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)

invalid channel for service notification,显然我的旧通道DEFAULT_CHANNEL_ID不再适合API 27我假设。合适的渠道是什么?我试着看了一下文件

150862 次浏览

在对不同的解决方案进行了一段时间的修补后,我发现必须在Android 8.1及以上版本中创建通知通道。

private fun startForeground() {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel("my_service", "My Background Service")
} 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(channelId: String, channelName: String): String{
val chan = NotificationChannel(channelId,
channelName, NotificationManager.IMPORTANCE_NONE)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
service.createNotificationChannel(chan)
return channelId
}

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

< p > 更新: 另外,不要忘记根据需要添加前台权限Android P:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

在andorid8.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());

这对我很管用。在我的服务类中,我为android 8.1创建了通知通道,如下所示:

public class Service extends Service {


public static final String NOTIFICATION_CHANNEL_ID_SERVICE = "com.package.MyService";
public static final String NOTIFICATION_CHANNEL_ID_INFO = "com.package.download_info";


@Override
public void onCreate() {


super.onCreate();
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));
} else {
Notification notification = new Notification();
startForeground(1, notification);
}
}
}

注意:创建你为Build.VERSION.SDK_INT >= Build.VERSION_CODES.O创建通知的通道

Java解决方案(Android 9.0, API 28)

在你的Service类中,添加这个:

@Override
public void onCreate(){
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startMyOwnForeground();
else
startForeground(1, new Notification());
}


private void startMyOwnForeground(){
String NOTIFICATION_CHANNEL_ID = "com.example.simpleapp";
String channelName = "My Background Service";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(chan);


NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
Notification notification = notificationBuilder.setOngoing(true)
.setSmallIcon(R.drawable.icon_1)
.setContentTitle("App is running in background")
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
startForeground(2, notification);
}

更新:android 9.0馅饼(api 28)

将这个权限添加到你的AndroidManifest.xml文件中:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

第一个答案只适合那些知道kotlin的人,对于那些仍然在使用java的人,我在这里翻译第一个答案

 public Notification getNotification() {
String channel;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
channel = createChannel();
else {
channel = "";
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, channel).setSmallIcon(android.R.drawable.ic_menu_mylocation).setContentTitle("snap map fake location");
Notification notification = mBuilder
.setPriority(PRIORITY_LOW)
.setCategory(Notification.CATEGORY_SERVICE)
.build();




return notification;
}


@NonNull
@TargetApi(26)
private synchronized String createChannel() {
NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);


String name = "snap map fake location ";
int importance = NotificationManager.IMPORTANCE_LOW;


NotificationChannel mChannel = new NotificationChannel("snap map channel", name, importance);


mChannel.enableLights(true);
mChannel.setLightColor(Color.BLUE);
if (mNotificationManager != null) {
mNotificationManager.createNotificationChannel(mChannel);
} else {
stopSelf();
}
return "snap map channel";
}

对于android, P不要忘记包含这个权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
感谢@CopsOnRoad,他的解决方案是一个很大的帮助,但只适用于SDK 26或更高。我的应用目标是24岁以上。< / p >

为了避免Android Studio抱怨,你需要在通知周围设置条件。知道代码在一个以VERSION_CODE.O为条件的方法中是不够聪明的。

@Override
public void onCreate(){
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startMyOwnForeground();
else
startForeground(1, new Notification());
}


private void startMyOwnForeground(){


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){


String NOTIFICATION_CHANNEL_ID = "com.example.simpleapp";
String channelName = "My Background Service";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(chan);


NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
Notification notification = notificationBuilder.setOngoing(true)
.setSmallIcon(AppSpecific.SMALL_ICON)
.setContentTitle("App is running in background")
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
startForeground(2, notification);
}
}

这是我的解决方案

private static final int NOTIFICATION_ID = 200;
private static final String CHANNEL_ID = "myChannel";
private static final String CHANNEL_NAME = "myChannelName";


private void startForeground() {


final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
getApplicationContext(), CHANNEL_ID);


Notification notification;






notification = mBuilder.setTicker(getString(R.string.app_name)).setWhen(0)
.setOngoing(true)
.setContentTitle(getString(R.string.app_name))
.setContentText("Send SMS gateway is running background")
.setSmallIcon(R.mipmap.ic_launcher)
.setShowWhen(true)
.build();


NotificationManager notificationManager = (NotificationManager) getApplication().getSystemService(Context.NOTIFICATION_SERVICE);


//All notifications should go through NotificationChannel on Android 26 & above
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);


}
notificationManager.notify(NOTIFICATION_ID, notification);


}

希望对大家有所帮助:)

另一种答案:如果它是华为设备,你已经实现了奥利奥8 Android所需的需求,并且只有华为设备仍然存在问题,而不仅仅是设备问题,你可以读取https://dontkillmyapp.com/huawei

我也有同样的问题。当我为两个应用程序使用相同的通道id和通知id时,出现了问题。所以请尝试使用唯一的通知id和通道id。

这可能是旧的,但是,以防有人有和我一样的情况。 由于某些原因,在Android 11 OnePlus Nord

Notification.Builder().Build()

崩溃了,

NotificationCompat.Builder().Build()
< p >工作好。 考虑迁移到androidx.core.app.NotificationCompat.

如果你正在使用VPN库,这些代码将会有所帮助,我把它放在onCreate(savedInstanceState: Bundle?)

 NotificationChannelManager.createNotificationChannelIfNeeded(
activity,
channelName = "Chanel Name",
channelDescription = "Channel description"
)