Notification Listener Service does not work after app is crashed
Asked Answered
M

3

4

I have a problem on my app and I want to report this bug.

I develope the app which can crawls notifications using NotificationListenerService.

It works well.

But NotificationListenerService class has the problem I think.

Because, If the app is crashed, app can't crawl the notification at all, UNTIL the phone reboots.

Is anyone who can solve this problem??

Please help me.

The bug is very clear!! But It is not easy to find the solution ....

Mercury answered 16/7, 2017 at 3:25 Comment(0)
B
11

If do you have already permissions then:

In your service class or another service/activity you can switch the "component hability" to listen notifications:

    public void tryReconnectService() {
        toggleNotificationListenerService();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            ComponentName componentName =
                    new ComponentName(getApplicationContext(), NotificationReaderV2Service.class);

            //It say to Notification Manager RE-BIND your service to listen notifications again inmediatelly!
            requestRebind(componentName);
        }
    }

/**
* Try deactivate/activate your component service
*/
    private void toggleNotificationListenerService() {
        PackageManager pm = getPackageManager();
        pm.setComponentEnabledSetting(new ComponentName(this, NotificationReaderV2Service.class),
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
        pm.setComponentEnabledSetting(new ComponentName(this, NotificationReaderV2Service.class),
                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
    }

Your notification listener, is a SERVICE, it can be killed by System, you can do your service as FOREGROUND to drastically decrease the probability that the system will kill your service.

@Override
    public void onListenerConnected() {
        super.onListenerConnected();
        Log.d(TAG, "Service Reader Connected");
    Notification not = createNotification();
    NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    if (mNotificationManager != null) {
        mNotificationManager.notify(NOTIFICATION_ID, not);
    }

    startForeground(NOTIFICATION_ID, not);

    //Alarm to auto - send Intents to Service to reconnect, you can ommit next line.
    alarmIt();
}

If do you like so more "safe", you can to programming not-friendly battery alarms, try to use inexact alarms please, the user's battery will be happy:

private void alarmIt() {
    Log.d(TAG, "ALARM PROGRAMMATED at"+HotUtils.formatDate(new Date()));
    Calendar now = Calendar.getInstance();
    now.setTimeInMillis(System.currentTimeMillis());
    now.set(Calendar.MINUTE, now.get(Calendar.MINUTE) + 1);

    Intent intent = new Intent(this, NotificationReaderV2Service.class);
    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    intent.setAction(REBIND_ACTION);

    PendingIntent pendingIntent = PendingIntent.getService(this, 0,
            intent, 0);

    AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

    //The alarms that are repeated are inaccurate by default, use RTC_WAKE_UP at your convenience.
    //Alarm will fire every minute, CHANGE THIS iF DO YOU CAN, you can't use less than 1 minute to repeating alarms.
    manager.setRepeating(AlarmManager.RTC_WAKEUP, now.getTimeInMillis(), 1000 * 60 * 1, pendingIntent);
}

and next read the Intent to reconnect service binding:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d(TAG, "Notification service onStartCommandCalled");
    if (intent!=null && !HotUtils.isNullOrEmpty(intent.getAction()) && intent.getAction().equals(REBIND_ACTION)){
        Log.d(TAG, "TRYING REBIND SERVICE at "+HotUtils.formatDate(new Date()));
        tryReconnectService();//switch on/off component and rebind
    }
    //START_STICKY  to order the system to restart your service as soon as possible when it was killed.
    return START_STICKY;
}

Keep in mind that doing all these steps you can sure that your service will be killed anyway by the system but this code will restart the service and make it harder to kill it.

Maybe, you should consider using PARTIAL_WAKE_LOCK with your service and execute it in a process independently (:remote) if you want even more certainty (Maybe this is useless)

I would like to add a common error that is often followed, NEVER override the onBind and onUnbind method or overwrite the INTENT ACTION. This will cause your service to not be connected and never run onListenerConnected Keep the Intent as it is, in most cases you do not need to edit it.

Bonanza answered 24/12, 2017 at 23:15 Comment(11)
Ended up on this answer issuetracker.google.com. Is this still working fine?Rojo
My service is running 37 days and surviving Playstore updates and similar events.Bonanza
It's surviving for me too. This is a great answer :)Rojo
so it has to be API24 and higher for requestRebind() to work, right? that is there is no way for it to work at lower APIs?Holds
Sadly not. But my service runs on many devices with >= Android 4.4 and this works without errors. In production with this code you will not need to worry about this problem, in development it is something that can bother and worry.Bonanza
Hello, would you please give additional explanation about your answer? especially the part of the package manager. Another question, can we skip the alarm services and stick to calling tryReconnectService without conditions in the onStartCommandKurtiskurtosis
I do not understand what difficulty the package manager code can have, about the alarms, you can forget them at your own risk (low) or you can call the alarms once a day or similar for security. In practice you only need to call tryReconnectService only in exceptional cases, either way you can create a service that is sending intents and listening by response from the notification listenerBonanza
I have tested this in my popular AutoResponder app. Here is what I noticed: Better don't use tryReconnectService() method as it leads to unpredictable behaviour on some devices (on Huawei the ongoing notification appears and disappears all the time) and I got a lot (really a lot!) more ANRs. Also don't use the AlarmManager as it leads to bad behaviour on Google Play because of the frequent device wakeups. The startForeground should be enough. It prevents the NotificationListener from getting stopped :)Steiger
@Panther, where you call startForeground? Is it on AppNotificationListenerService?Parmenter
@Parmenter Exactly.Steiger
@Steiger How do you deal with notification foreground (notification XXX is running... ), if call startForeground() on NotificationListenerService?Parmenter
T
0

I see exactly the same on this. The only "solution" I've found was to have the notification listener running in a separate process. Then if the rest of the app crashes it doesn't stop the listener. So it's only then specifically notification listener service crashes that require the reboot.

Seems a terrible and over complicated solution though.

Trapan answered 13/9, 2017 at 13:39 Comment(0)
U
0

I had the same problem. Here are few things that I did and now it works wonderfully for me.

  1. Override onStartCommand, call super and return START_STICKY;
  2. Override onNotificationRemoved, call super and add a toast so that you know in android itself that you service has not died yet whenever you swipe a notification.
  3. Exclude your app from Battery saving list (Settings-> Battery-> Power Saving Exclusion)

Post this the service never dies even after the main app's crash. I dont need to reboot now to restart it.

Unpin answered 4/9, 2019 at 2:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.