Android 8.1'e yükselttikten sonra topraklama başarısız


193

Telefonumu 8.1 Geliştirici Önizlemesine yükselttikten sonra arka plan servisim artık düzgün başlamıyor.

Uzun süredir devam eden hizmetimde, oluşturmaya çağrılan devam eden bildirimi başlatmak için bir startForeground yöntemi uyguladım.

    @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)
}

Hata mesajı:

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)

Servis bildirimi için geçersiz kanal , görünüşe göre eski kanalım DEFAULT_CHANNEL_ID API 27 için artık uygun değil. Uygun kanal ne olurdu? Belgelere bakmaya çalıştım


1
Bu cevap benim çözümümdü.
Alex Jolig

Yanıtlar:


230

Bir süre farklı çözümlerle uğraştıktan sonra, Android 8.1 ve üstü bir bildirim kanalı oluşturması gerektiğini öğrendim.

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
}

Anladığım kadarıyla, arka plan hizmetleri artık kullanıcının bildirim kanalının seçimini kaldırarak gösterilmemeyi seçebileceği normal bildirimler olarak görüntüleniyor.

Güncelleme : Ayrıca Android P için ön plan iznini eklemeyi unutmayın:

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

JobIntentService durumunda bu değişiklikleri yapmamız gerekiyor mu? Yoksa dahili olarak mı işlemektedir?
Amrut

1
neden olmasın IMPORTANCE_DEFAULTyerine IMPORTANCE_NONE?
user924

1
@ user924 Kotlin aslında Swift'ten daha yeni bir dildir. Kotlin Java'nın yerini almaz, sadece Android geliştirme için Java'ya bir alternatiftir. Eğer denerseniz, aslında sözdiziminde Swift ile oldukça benzer olduğunu göreceksiniz. Ben şahsen Tiobe endeksinin söylediklerine rağmen Java'dan daha iyi olduğuna inanıyorum (indeks biraz yanıt vermeme eğilimine maruz). Korkunç NullPointerException, ayrıntı düzeyi ve diğer birçok şey de dahil olmak üzere Java'nın sahip olduğu birçok sorunu giderir. En son Google I / O verilerine göre, Android için Kotlin'i kullanan geliştiricilerin% 95'i bundan memnun.
Alt 6 Kaynaklar

3
Bu, hizmetinizin onCreate ()
işlevinden çağrılmalıdır

1
@Rawa Peki bile Belgeler yalan değil çünkü app Ön Plan hizmet ile ne yaptığını emin değilim. Açıkça belirtilir, bildirimde izin olmadan ön plan hizmeti oluşturmaya çalışırsanız SecurityException'ı alırsınız.
CopsOnRoad

134

Java Çözümü (Android 9.0, API 28)

Senin içinde Servicesınıf, bu ekleyin:

@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);
}

GÜNCELLEME: ANDROID 9.0 PIE (API 28)

Bu izni AndroidManifest.xmldosyanıza ekleyin :

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

İki startForeground () çağrısında benzersiz kimlikler kullanmanın bir nedeni var mı? Aynı bildirimden beri burada aynı olamazlar mı?
Cody

@CopsOnRoad, O için bildirim kanalına gerek yok mu?
Shruti

2
@Shruti Android 9.0 koduyla birlikte izin eklemeniz gerekir. Her ikisi de gereklidir.
CopsOnRoad

1
@CopsOnRoad Bu durum 'Ölümcül İstisna: android.app.RemoteServiceException: Context.startForegroundService () daha sonra Service.startForeground ()'
Shruti

2
Hizmet çalışırken bildirimin görüntülenmesini önlemek mümkün müdür?
matdev

29

İlk cevap sadece kotlin'i bilenler için harika, burada hala java kullananlar için ilk cevabı tercüme ediyorum

 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 için P, bu izni eklemeyi unutmayın

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

Kodu Java'ya çevirdiğiniz için teşekkür ederiz. Java projeleri için büyük bir yardım!
Ray Li

17

Andorid 8.1 üzerinde düzgün çalışır:

Güncellenmiş örnek (kullanımdan kaldırılmış kod olmadan):

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);
    }
}

Eski snipped (farklı bir uygulama - yukarıdaki kodla ilgili değil ):

@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;
}

2
Yukarıdaki kod parçası şimdi değiştirerek üstesinden gelebilir ki itiraz edildi, Notification.Builder(getApplicationContext()).setChannelId(CHANNEL_ONE_ID)...içinNotification.Builder(getApplicationContext(), CHANNEL_ONE_ID)...
yasağının-geoengineering

1
@ ban-geoengineering kesinlikle haklısınız ... Yeni örnek kod ekledim. Teşekkürler.
Martin Pfeffer

neden PRIORITY_MAXkullanmak daha iyi?
user924

7

Benim durumumda, bunun nedeni aşağıdakileri belirtmeden bildirim göndermeye çalışmamızdır 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));
    }
}

Yukarıdaki kodu koymak için en iyi yer sınıfta onCreate()yöntemdir Application, böylece sadece bir kez herkes için ilan etmeliyiz:

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        initChannel();
    }
}

Bunu ayarladıktan sonra, channelIdaz önce belirttiğimiz bildirimi kullanabiliriz :

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);

Ardından, bildirim göndermek için kullanabiliriz:

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

Ön plan hizmetinin bildirimi olarak kullanmak istiyorsanız:

startForeground(notifId, builder.build());

1
Sabit NOTIFICATION_CHANNEL_ID_TASK (2. satır) NOTIFICATION_CHANNEL_ID_INFO olmalı mı?
Timores

@ Zamanlayıcılar, hayır. Kendi sabitinizle değiştirebilirsiniz.
Anggrayudi H

4

@CopsOnRoad sayesinde çözümü büyük bir yardımcı oldu, ancak sadece SDK 26 ve üstü için çalışıyor. Uygulamam 24 ve üstünü hedefliyor.

Android Studio'nun şikayet etmesini önlemek için doğrudan bildirimin etrafında bir koşul olması gerekir. Kodun VERSION_CODE.O koşullu bir yöntemde olduğunu bilmek yeterince akıllı değildir.

@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);
    }
}

Lütfen bu kodda yaptığınız değişiklikleri açıklığa kavuşturabilir misiniz, bunu anlamadım.
CopsOnRoad

Sürüm 8.0 ve Android Pie mükemmel çalışıyor. Ancak neden yalnızca 8.1 sürümü için Bildirim kanalına ihtiyacımız var?
Thamarai T

2

Bu benim için çalıştı. Hizmet sınıfımda, android 8.1 için bildirim kanalını aşağıdaki gibi oluşturdum:

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);
        }
    }
}

Not: Bildirimini oluşturduğunuz kanalı oluşturun Build.VERSION.SDK_INT >= Build.VERSION_CODES.O


-1

İşte benim çözümüm

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);

    }

Umarım yardımcı olur :)


1
Lütfen çözümünüzün gerekçesini açıklamak için biraz zaman ayırın.
straya
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.