Разработка Android: уведомления не отображаются при закрытии приложения

#android #android-studio #android-service #android-notifications #android-alarms

#Android #android-студия #android-сервис #android-уведомления #android-сигналы тревоги

Вопрос:

Как я могу отправлять запланированные уведомления в Android Studio? Я получил эту задачу: я хочу получать уведомления в определенное выбранное время суток каждый день. Я могу легко получить их, когда приложение работает, но когда его закрытые уведомления не появляются.

Я уже пробовал JobScheduler, AlarmManager и WorkManager, и ни один из них не работал хорошо. Мой проект работает с минимальным SDK 26 (Android Oreo). Целевой SDK равен 30. Последняя версия кода выглядит так:

AlertReceiver.java

 public class AlertReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        intent = new Intent(context, NotificationService.class);
        context.startService(intent);
    }
}
 

NotificationService.java

 public class NotificationService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        showNotifications();

        return Service.START_STICKY;
    }

    private void showNotifications(){
        NotificationHelper notificationHelper = new NotificationHelper(getApplicationContext());

        NotificationCompat.Builder nb;

        nb = notificationHelper.getChannelNotification("Title", "Description");
        notificationHelper.getManager().notify(123, nb.build());
    }

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }
}
 

Я планирую оповещение следующим образом:

         ...
        Calendar calendar = Calendar.getInstance();

        calendar.set(Calendar.HOUR_OF_DAY, hours);
        calendar.set(Calendar.MINUTE, minutes);

        AlarmManager alarm_manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(this, AlertReceiver.class);
        PendingIntent pending_intent = PendingIntent.getBroadcast(this, 0, intent, 0);

        alarm_manager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pending_intent);
```
 

Комментарии:

1. почему вы используете NotificationHelper ?

2. @isthemartin Я перепробовал много разных предложений для своей задачи. Одно из этих предложений (последнее, которое я уже пробовал) содержало этот код. Также я попытался вызывать уведомления непосредственно из alarm manager, без каких-либо перенаправлений на другие классы (например, помощник по уведомлениям и т.д.).

3. Привет, Dkezling. Что вы получаете в logcat? Я ожидал бы увидеть что-то похожее на: «java.lang. Исключение IllegalStateException: не разрешено запускать намерение службы, приложение находится в фоновом режиме «.

4. @Elletlar да, я получал это пару раз. Я погуглил эту проблему и получил несколько предложений, таких как «использовать планировщик заданий», чтобы исправить это. Но планировщик заданий не работал так же, как и другие методы — планировщик заданий не работал после закрытия приложения.

5. Я внес изменения в вашу текущую реализацию ниже. По крайней мере, это должно позволить службе работать в фоновом режиме, но является ли это решением, которое вам нужно, я не знаю. Похоже, что вы действительно хотите использовать Firebase Cloud Messaging, если это возможно.

Ответ №1:

Проблема в том, что с Android 8 вызов startService из фона не разрешен:

В этом коде:

 public class AlertReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        intent = new Intent(context, NotificationService.class);
        context.startService(intent);
    }
}
 

Вы можете изменить:

 context.startService(intent);
 

Для:

 ContextCompat.startForegroundService(getApplicationContext(), intent)
 

И внесите это в свой Манифест:

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

Это, по крайней мере, позволит вашей службе работать в фоновом режиме в вашей текущей реализации.

Затем у вас есть 5 секунд, чтобы позвонить startForeground() в свой Service и опубликовать Notification сообщение, которое позволяет пользователю узнать, что ваша служба запущена, или она будет прекращена.

Кроме того, за то, что вы пытаетесь сделать, я думаю, вы получите лучший результат с:

 setExactAndAllowWhileIdle
 

Или

 setAlarmClock
 

setExact работает не так, как следует из названия. Необходимо внимательно прочитать всю документацию AlarmManager, и я бы посоветовал подробно изучить «doze», прежде чем пытаться какие-либо Service реализации, которые зависят от времени.

Комментарии:

1. У него есть уведомление службы переднего плана — есть ли какой-нибудь способ скрыть его / не показывать? Я прочитал несколько предложений по этому поводу, и в некоторых из них говорится, что это незаконно из-за политики Google Market.

2. Боюсь, что нет. Google сделал это обязательным, чтобы пользователь всегда знал, когда служба работает в фоновом режиме. Однако есть некоторые хитрости, такие как установка для него низкого приоритета и настройка его как частного, чтобы он никогда не отображался на экране блокировки.

3. Если вашей службе нужно работать только очень короткое время, возможно, вам удастся избежать вызова startForeground() и вместо этого вызвать stopSelf() до истечения 5-секундного лимита. Я никогда не пробовал этого, так как у меня нет таких тривиальных сервисов. Это кажется нецелесообразным, но, возможно, технически возможно. Но в целом, лучше всего использовать одно из готовых решений, таких как WorkManager, если это вообще возможно, даже если они, как правило, не соответствуют исходной спецификации, которую разработчики имеют в виду с точки зрения времени.

4. В 2022 году я могу подтвердить, что это больше не работает. Google необходимо исправить свои уведомления. Это основная причина, по которой мы создаем мобильные приложения для уведомлений! Если нет, веб-приложения в порядке.

Ответ №2:

Это уведомление, которое я использовал в проекте (извините за язык Kotlin):

 private fun createNotification() {
    val notification = getServiceNotification("")
    notificationManager.notify(NOTIFICATION_ID, notification.build())
}

private fun getServiceNotification(contentText: String): Notification.Builder {
    var notification = Notification.Builder(this)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        createNotificationChannel()
        notification = Notification.Builder(this, NOTIFICATION_SERVICE_CHANNEL_ID)
    }

    val openAppPendingIntent = PendingIntent.getActivity(
        this,
        0,
        Intent(this, MainActivity::class.java),
        PendingIntent.FLAG_UPDATE_CURRENT
    )

    return notification
        .setContentTitle("Notification Title")
        .setContentText(contentText)
        .setSmallIcon(R.drawable.ic_main)
        .setContentIntent(openAppPendingIntent)
}

@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel() {
    val notificationChannel = NotificationChannel(
        NOTIFICATION_SERVICE_CHANNEL_ID,
        NOTIFICATION_SERVICE_CHANNEL_NAME,
        NotificationManager.IMPORTANCE_HIGH
    ).apply {
        lightColor = Color.GREEN
        lockscreenVisibility = Notification.VISIBILITY_PRIVATE
    }

    notificationManager.createNotificationChannel(notificationChannel)
}
 

Комментарии:

1. для меня это работает так же, но разве вы не использовали запланированные уведомления? код, который вы отправили туда, просто показывает, как использовать уведомления, а не как планировать их после закрытия приложения.