एंड्रॉइड 8.1 पर अपग्रेड के बाद startForeground विफल


192

8.1 डेवलपर के लिए मेरे फोन का उन्नयन पूर्वावलोकन मेरी पृष्ठभूमि सेवा अब ठीक तरह से शुरू होने के बाद।

मेरे लंबे समय तक चलने वाली सेवा में मैंने चालू अधिसूचना को शुरू करने के लिए एक स्टार्टफोरग्राउंड पद्धति लागू की है जिसे बनाने पर कहा जाता है।

    @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 अब एपीआई 27 के लिए उपयुक्त नहीं है। उचित माध्यम हो सकता है? मैंने प्रलेखन के माध्यम से देखने की कोशिश की है


1
यह उत्तर मेरा समाधान था।
एलेक्स जोलीग

जवाबों:


228

कुछ समय के लिए अलग-अलग समाधानों के साथ कुछ छेड़छाड़ करने के बाद मुझे पता चला कि किसी को एंड्रॉइड 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
}

से मेरी समझ पृष्ठभूमि सेवाओं अब सामान्य सूचनाओं के रूप में प्रदर्शित किए जाते हैं उपयोगकर्ता तो अधिसूचना चैनल का चयन हटा कर नहीं दिखाने के लिए चुन सकते हैं कि।

अपडेट : इसके अलावा आवश्यक एंड्रॉयड पी के रूप में अग्रभूमि अनुमति जोड़ने के लिए भूल नहीं है:

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

हम JobIntentService के मामले में ये परिवर्तन करने की जरूरत है? या यह आंतरिक रूप से इसे संभाल रहा है?
अमृत

1
IMPORTANCE_DEFAULTइसके बजाय क्यों नहीं IMPORTANCE_NONE?
user924

1
@ user924 कोटलिन वास्तव में स्विफ्ट की तुलना में एक नई भाषा है। Kotlin Java को प्रतिस्थापित नहीं करता है, यह केवल Android विकास के लिए जावा का एक विकल्प है। यदि आप इसे आज़माते हैं, तो आप देखेंगे कि यह वास्तव में स्विफ्ट के सिंटैक्स में काफी समान है। मेरा व्यक्तिगत रूप से मानना ​​है कि यह जावा से बेहतर है, इसके बावजूद कि टियोबे इंडेक्स क्या कहता है (सूचकांक थोड़ा गैर-प्रतिक्रिया पूर्वाग्रह के अधीन है)। यह मुद्दों जावा खतरनाक NullPointerException शब्दाडंबर, और कई अन्य चीजें शामिल हैं, है कि के कई ठीक करता है। नवीनतम Google I / O के अनुसार, Android के लिए कोटलिन का उपयोग करने वाले 95% डेवलपर्स इससे खुश हैं।
उप 6 संसाधन

3
यह आपकी सेवा के OnCreate () से बुलाया जाना चाहिए
Evgenii Vorobei

1
@ रवा वैसे भी मुझे यकीन नहीं है कि आप ऐप में अपनी फोरग्राउंड सेवा के साथ क्या कर रहे हैं क्योंकि दस्तावेज़ीकरण झूठ नहीं बोलता है। यह स्पष्ट रूप से बताता है, यदि आप प्रकट में अनुमति के बिना अग्रभूमि सेवा बनाने की कोशिश करते हैं, तो आपको SecurityException मिल जाएगी।
कोप्सऑनरॉड

134

जावा सॉल्यूशन (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 पाई (एपीआई 28)

इस अनुमति को अपनी AndroidManifest.xmlफ़ाइल में जोड़ें :

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

वहाँ दो startForeground () कॉल में एक अद्वितीय आईडी का उपयोग करने के लिए एक कारण है? क्या वे एक ही अधिसूचना के बाद से यहां नहीं हो सकते?
कोड़ी

@CopsOnRoad तो वहाँ हे के लिए अधिसूचना चैनल की कोई जरूरत नहीं है?
श्रुति

2
@Shruti आप Android 9.0 के लिए कोड के साथ अनुमति जोड़नी होगी। दोनों की जरूरत है।
CopsOnRoad

1
@CopsOnRoad यह अपवाद है 'घातक अपवाद: android.app.RemoteServiceException: Context.startForegroundService () ने तब Service.startForeground ()'
श्रुति

2
क्या सेवा चालू होने के दौरान अधिसूचना प्रदर्शित करने से बचना संभव है?
मटदेव

29

पहला उत्तर केवल उन लोगों के लिए बहुत अच्छा है जो कोटलिन को जानते हैं, उन लोगों के लिए जो अभी भी यहां जावा का उपयोग कर रहे हैं, मैं पहले उत्तर का अनुवाद करता हूं

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

एंड्रॉयड के लिए, पी मत भूलना इस अनुमति शामिल करने के लिए

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

जावा में कोड का अनुवाद करने के लिए धन्यवाद। यह जावा परियोजनाओं के लिए एक बड़ी मदद है!
रे ली

17

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

2
उपर्युक्त कोड का एक भाग अब हटा दिया गया है, जिसे आप बदलकर इस Notification.Builder(getApplicationContext()).setChannelId(CHANNEL_ONE_ID)...पर काबू पा सकते हैंNotification.Builder(getApplicationContext(), CHANNEL_ONE_ID)...
प्रतिबंध-जियोइंजीनियरिंग

1
@ प्रतिबंध-जियोइंजीनियरिंग आप बिल्कुल सही हैं ... मैंने नया नमूना कोड जोड़ा है। धन्यवाद।
मार्टिन फेफर

क्यों PRIORITY_MAXबेहतर है उपयोग करने के लिए?
user924

7

मेरे मामले में, ऐसा इसलिए है क्योंकि हमने एक अधिसूचना पोस्ट करने की कोशिश की है 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));
    }
}

ऊपर कोड डालने के लिए सबसे अच्छी जगह कक्षा में onCreate()विधि है Application, इसलिए हमें बस इसे एक बार सभी के लिए घोषित करने की आवश्यकता है:

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

1
क्या लगातार NOTIFICATION_CHANNEL_ID_TASK (दूसरी पंक्ति) NOTIFICATION_CHANNEL_ID_ASFO होना चाहिए?
टाइमोरस

@ तैमूर, नहीं। आप इसे अपने स्वयं के स्थिरांक से बदल सकते हैं।
अंगरेजुडी एच।

4

@CopsOnRoad के लिए धन्यवाद, उनका समाधान एक बड़ी मदद थी, लेकिन केवल एसडीके 26 और उच्चतर के लिए काम करता है। मेरा ऐप 24 और उच्चतर लक्ष्य करता है।

एंड्रॉइड स्टूडियो को शिकायत से दूर रखने के लिए आपको अधिसूचना के चारों ओर एक सशर्त की आवश्यकता होती है। यह जानने के लिए पर्याप्त स्मार्ट नहीं है कि कोड 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);
    }
}

क्या आप स्पष्ट कर सकते हैं कि इस कोड में आपने क्या बदलाव किए हैं, मुझे यह नहीं मिला।
कोप्सऑनरॉड

संस्करण 8.0 और एंड्रॉइड पाई पूरी तरह से काम करता है। लेकिन हमें केवल 8.1 संस्करण के लिए अधिसूचना चैनल की आवश्यकता क्यों है?
थामराई टी

2

इसने मेरे लिए काम किया। अपनी सेवा श्रेणी में, मैंने नीचे दिए गए 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


-1

यहाँ मेरा समाधान है

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

    }

आशा है कि यह मदद करेगा :)


1
कृपया अपने समाधान के लिए औचित्य समझाने के लिए कुछ समय लें।
स्ट्रै
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.