2

In my app, I use AlarmManager to start IntentService that accesses internal database of quotes and selects one randomly, then fires a notification with that quote.

It is working perfectly on my phone which is Android 9, OnePlus6. But I receive lots of crashlytics data from other users stating:

Fatal Exception: java.lang.RuntimeException: Unable to start receiver com.myapp.receiver.AlarmReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.myapp/.service.NotificationQuoteService }: app is in background uid UidRecord{e3c2e3d u0a203 TRNB idle procs:1 proclist:23491, seq(0,0,0)}
       at android.app.ActivityThread.handleReceiver(ActivityThread.java:3606)
       at android.app.ActivityThread.access$1300(ActivityThread.java:237)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1796)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loop(Looper.java:214)
       at android.app.ActivityThread.main(ActivityThread.java:7045)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)

I presume, it is being caused from the restriction that Oreo has, that every background task should notify itself via foreground notification. However, wouldn't it be absurd to show foreground notification to query quotes and then show a quote notification?! There are lots of apps out there that do similar thing, they query internal data and show it in the notification without showing any foreground notification while data is being queried.

How would I do it, can someone point me in the right direction?

EDIT Codes are added:

AlarmManager to go off at 9 A.M. in the morning.

    private void prepareAlarmManager() {

        Intent intent = new Intent(context, AlarmReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, AlarmReceiver.REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        if (alarmManager != null) {

            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(System.currentTimeMillis());
            calendar.set(Calendar.HOUR_OF_DAY, 9);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);

            alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 24 * 60 * 60 * 1000, pendingIntent);
        }
    }

BroadcastReceiver to handle AlarmManager

public class AlarmReceiver extends BroadcastReceiver {

    public static final int REQUEST_CODE = 10;

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent serviceIntent = new Intent(context, NotificationQuoteService.class);
        context.startService(serviceIntent);
    }
}

IntentService to query internal SQLite database and fire notification

public class NotificationQuoteService extends IntentService {

    private Context mContext;
    private DatabaseHelper mDatabaseHelper;

    public NotificationQuoteService() {
        super("quote service");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
        mDatabaseHelper = DatabaseHelper.getInstance(mContext);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        List<Quote> quoteList = mDatabaseHelper.getAllQuotes();
        int size = quoteList.size();
        int randomIndex = (int) (Math.random() * size);
        Quote quote = quoteList.get(randomIndex);
        NotificationHelper.showDefaultNotification(mContext, quote);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        L.e("hop", "service is destroyed");
    }
}
amira
  • 416
  • 1
  • 7
  • 24

2 Answers2

1

in onReceive() start service:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
   context.startForegroundService(intent);
 } else {
  context.startService(intent); 
 }

And in service onCreate() make something like that:

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  startForeground(1, new Notification());
 }
haresh
  • 1,424
  • 2
  • 12
  • 18
  • Thanks for the reply, but still I am showing Foreground notification while doing database query right ? Whatever I do, I need to show foreground notification eh? – amira Jan 26 '20 at 11:43
  • 1
    see after nougat whenever you are using any background service you need to show notification until service is running. All you have to do is use startForgroundService() method and pass notification is that simple – haresh Jan 26 '20 at 11:47
  • No I got it. It turns out I will have to do it the other way. Without background service. Thanks mate for the help – amira Jan 26 '20 at 11:50
  • accept and upvote if it help you also let me know if stuck anywhere – haresh Jan 26 '20 at 11:52
0

you cant start service in background in android 8 or above.

context.startService(serviceIntent);

You can start a service as startForegroundService().

Rasel
  • 5,488
  • 3
  • 30
  • 39
  • with `startForegroundService()` shouldn't I show foreground notification?! I don't want that. – amira Jan 26 '20 at 11:38