Как сделать разсылку уведомлений когда приложение закрыто и удалено с RAM
package com.frusty.cooking.services;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
import com.frusty.cooking.R;
import com.frusty.cooking.broadcasts.ClearNotificationPullBroadcast;
import com.frusty.cooking.broadcasts.CloseNotification;
import com.frusty.cooking.entity.Notification;
import com.frusty.cooking.entity.Time;
import com.frusty.cooking.entity.User;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
import com.google.firebase.database.ValueEventListener;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
public class NotificationService extends Service {
private static String TAG="NotificationServices";
User user;
String userID;
public NotificationService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("ServiceSS", "Started");
userID = intent.getStringExtra("user_id");
FirebaseDatabase.getInstance().getReference().addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
Query query = FirebaseDatabase.getInstance().getReference("Users").orderByChild("id").equalTo(userID);
query.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
Log.d("ServiceSS","Update, user id: "+userID);
for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
user = null;
user = dataSnapshot.getValue(User.class);
int id = 0;
ArrayList<Notification> notifications = user.getNotifications_pool();
for (Notification notification : notifications) {
id++;
createNotification(id, notification);
}
}
query.removeEventListener(this);
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
}
});
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
}
});
return START_STICKY;
}
private void createNotification(int id, Notification notification) {
String title = notification.getTitle();
String body = notification.getMessageBody();
String recipe_id = notification.getRecipe_id();
NotificationManager mNotificationManager= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name= "Cooking";
String description = "Cooking";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel("Cooking", name, importance);
channel.setDescription(description);
mNotificationManager.createNotificationChannel(channel);
}
Intent notificationIntent = new Intent( this, ClearNotificationPullBroadcast.class);
notificationIntent.putExtra("user_id", userID);
notificationIntent.putExtra("recipe_id", recipe_id);
notificationIntent.putExtra("notification_pull_id", notification.getId());
notificationIntent.putExtra("notification_id", id);
Intent closeNotification = new Intent(this, CloseNotification.class);
closeNotification.putExtra("user_id", userID);
closeNotification.putExtra("recipe_id", recipe_id);
closeNotification.putExtra("notification_pull_id", notification.getId());
closeNotification.putExtra("notification_id", id);
PendingIntent pendingIntent = PendingIntent. getBroadcast (this, 0 , notificationIntent , PendingIntent. FLAG_UPDATE_CURRENT ) ;
PendingIntent pClose = PendingIntent.getBroadcast(this, 0, closeNotification, PendingIntent.FLAG_UPDATE_CURRENT );
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext(), "Cooking")
.setSmallIcon(R.mipmap.ic_launcher_logo_round)
.setContentTitle(title)
.setSmallIcon(R.mipmap.ic_launcher_logo_round)
.setContentText(body)
.setDeleteIntent(pClose)
.setContentIntent(pendingIntent)
.setAutoCancel(false)
.setOngoing(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mBuilder.setSmallIcon(R.drawable.ic_launcher_logo_round);
mBuilder.setColor(getResources().getColor(R.color.main));
} else {
mBuilder.setSmallIcon(R.drawable.ic_launcher_logo_round);
}
mNotificationManager.notify(id, mBuilder.build());
}
@Override
public void onDestroy() {
Log.d("ServiceSS", "Stop");
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
}
}
С помощью Firebase Database я получаю пул уведомлений пользователя, если есть оповещения, тогда выводится это оповещение, через бродкаст я открываю или закрываю оповещения и удаляю это оповещение из пула.
Вопрос в следующем. Как сделать так же как в телеграмме или вайбере. Когда приложение закрыто и удалено в оперативной памяти, оповещения приходят. Что перепробовал:
startForeground();
startForegroundService();
Пробовал перезапускать сервис в onTaskRemoved(), или onDestroy(); Все варианти не работают, так как Андроид думает что сервис бесполезен и убивает полностю поток. Рылся в исходнике Телеграма, и нашол то что он использует кастомный NotificationCenter и я не понимаю как он работает. Больше ничево не нашол. Вот почему я здесь.
Ответы (1 шт):
Тут два варианта решения:
создать службу которая будет работать постоянно, даже после закрытия приложения. Главный минус - это не скрываемое уведомление в шторке, о том что служба работает без активити. Без нее у тебя в фоне служба работать не будет. Выглядит это примерно так.
private void startService_NOT_STICKY(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { String NOTIFICATION_CHANNEL_ID = getPackageName(); String channelName = "Service Channel name"; NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE); NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); manager.createNotificationChannel(chan); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID); Notification notification = notificationBuilder.setOngoing(true) .setSmallIcon(R.drawable.ic_info_black_24dp) .setContentTitle(getString(R.string.app_name)) .setContentText("Служба запущена") .setPriority(NotificationManager.IMPORTANCE_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build(); startForeground(2, notification); } else { createPersistentNotification( getApplicationContext(), this); } }Использовать FCM. Если тебе нужно чтобы андроид отображал уведомления без служб когда приложение не запущено, то Firebase Realtime Database это не то что тебе нужно, тебе нужен Firebase Cloud Messaging. И "серверная часть", которая будет рассылать сообщение в топик / или на устройство по идентификатору при возникновении определенного события в системе. На этапе тестирования можно рассылать уведомления через консоль FCM.
Если решишься то нужно build.gardle (app) добавить
implementation 'com.google.firebase:firebase-messaging:20.x.x'
В манифесте добавить обработку события от FCM
<service
android:name=".CloudMessageService"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Создать наследника FirebaseMessagingService
public class CloudMessageService extends FirebaseMessagingService{
public CloudMessageService() {
}
@Override
public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Intent intent;
boolean localMessageDelivered = false;
try {
if (remoteMessage.getNotification() != null) {
sendNotification(this, remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody()); // тут твое уведомление.
}
}
catch (Exception e)
{
Log.e(TAG, e.toString());
}
}
}