#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. для меня это работает так же, но разве вы не использовали запланированные уведомления? код, который вы отправили туда, просто показывает, как использовать уведомления, а не как планировать их после закрытия приложения.