问题
I have a service that will run a upload task in foreground then showing a progress in the notification. Since a user may upload multiple times with different id request then there will be two or more foreground service that will run. Everything works fine but what I want is to show all task's notification with progress with this code.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID_DEFAULT)
.setSmallIcon(R.drawable.ic_file_upload_white_24dp)
.setContentTitle(getString(R.string.app_name))
.setContentText(caption)
.setProgress(100, percentComplete, false)
.setContentInfo(String.valueOf(percentComplete +"%"))
.setOngoing(true)
.setAutoCancel(false);
NotificationManager manager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(code, builder.build());
startForeground(code,builder.build());
But unfortunately the notification just get overwrites, but if I removed the startForeground() then everything works fine as it shows multiple progress notification according to how many task is ongoing but the system may kill the process if gets low memory that is why I wanted it to run in foreground. So how am I able to show a number of notification that is equal to the number of on going task in Foreground?
回答1:
Before you call startForeground, you need to call ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_DETACH)
to keep the old notification(s).
The following is a utility class that you can use in the foreground service to manage the notifications:
class ForegroundNotifications(
private val context: Context,
private val foregroundServiceHelper: ForegroundServiceHelper,
private val notificationService: NotificationService
) {
var activeNotificationId: Int? = null
private set
val notificationIds: Set<Int>
get() = entries.keys
val isEmpty: Boolean
get() = entries.isEmpty()
private val entries: LinkedHashMap<Int, Notification> = LinkedHashMap()
fun notifyAndDetachPrevious(notificationId: Int, notification: Notification) {
synchronized(this) {
foregroundServiceHelper.startForegroundAndDetachPreviousNotification(
notificationId,
notification
)
entries[notificationId] = notification
activeNotificationId = notificationId
}
}
fun cancel(notificationId: Int) {
synchronized(this) {
if (notificationId == activeNotificationId) {
val newActiveNotificationId = entries.keys.findLast { it != activeNotificationId }
val notification = entries[newActiveNotificationId]
if (newActiveNotificationId != null && notification != null) {
notifyAndDetachPrevious(newActiveNotificationId, notification)
}
}
entries.remove(notificationId)
if (isEmpty) {
foregroundServiceHelper.stopForeground()
} else {
notificationService.cancel(context, id = notificationId)
}
}
}
}
interface NotificationService {
fun createDefaultChannel(context: Context)
fun notify(context: Context, tag: String? = null, id: Int, notification: Notification)
fun cancel(context: Context, tag: String? = null, id: Int)
}
interface ForegroundServiceHelper {
fun startForegroundAndDetachPreviousNotification(
notificationId: Int,
notification: Notification
)
fun stopForeground()
}
class ForegroundServiceHelperImpl(
private val service: Service
) : ForegroundServiceHelper {
override fun startForegroundAndDetachPreviousNotification(
notificationId: Int,
notification: Notification
) {
ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_DETACH)
service.startForeground(notificationId, notification)
}
override fun stopForeground() {
ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_REMOVE)
}
}
回答2:
Use different id for different notification. In this code, you are using same default id so new notification is replacing old one.
回答3:
You should pass different Id to get different notification.
In Your Code:
Use getId()
method to get different Id for each notification:
private static final AtomicInteger c = new AtomicInteger(0);
public static int getID() {
return c.incrementAndGet();
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID_DEFAULT)
.setSmallIcon(R.drawable.ic_file_upload_white_24dp)
.setContentTitle(getString(R.string.app_name))
.setContentText(caption)
.setProgress(100, percentComplete, false)
.setContentInfo(String.valueOf(percentComplete +"%"))
.setOngoing(true)
.setAutoCancel(false);
NotificationManager manager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(getId(), builder.build());
startForeground(getId(),builder.build()); // you need to pass different id here. Or i think this method is not there.
For android O you need notification channel:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "My Notifications", NotificationManager.IMPORTANCE_HIGH);
// Configure the notification channel.
notificationChannel.setDescription("Channel description");
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
notificationChannel.enableVibration(true);
notificationChannel.setImportance(NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setSound(notification, Notification.AUDIO_ATTRIBUTES_DEFAULT);
notificationManager.createNotificationChannel(notificationChannel);
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
notificationBuilder.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.setWhen(System.currentTimeMillis())
.setSound(notification)
.setContentIntent(pendingIntent)
.setContentText(message)
.setContentTitle(getResources().getString(R.string.app_name))
.setSmallIcon(R.drawable.ic_visualogyx_notification)
.setStyle(new NotificationCompat.BigTextStyle().bigText(message).setBigContentTitle(getResources().getString(R.string.app_name)))
.setTicker("Hearty365")
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher_round));
notificationManager.notify(getID(),notificationBuilder.build());
回答4:
At last I figure it out how to do it. First I needed to run it inside a onCreate with a little delay.
@Override
public void onCreate (){
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
startForeground(requestCode, getMyActivityNotification("",completedParts,totalSize));
}
},500);
}
Then create a notification provider method.
//Notification provider
private Notification getMyActivityNotification(String caption, long completedUnits, long totalUnits){
int percentComplete = 0;
if (totalUnits > 0) {
percentComplete = (int) (100 * completedUnits / totalUnits);
}
//Return the latest progress of task
return new NotificationCompat.Builder(this, CHANNEL_ID_DEFAULT)
.setSmallIcon(R.drawable.ic_file_upload_white_24dp)
.setContentTitle(getString(R.string.app_name))
.setContentText(caption)
.setProgress(100, percentComplete, false)
.setContentInfo(String.valueOf(percentComplete +"%"))
.setOngoing(true)
.setAutoCancel(false)
.build();
}
Then updating notification progress should be separated from calling a foreground.
/**
* Show notification with a progress bar.
* Updating the progress happens here
*/
protected void showProgressNotification(String caption, long completedUnits, long totalUnits, int code) {
createDefaultChannel();
mCaption = caption;
requestCode = code;
completedParts = completedUnits;
totalSize = totalUnits;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager != null) {
//Update the notification bar progress
mNotificationManager.notify(requestCode, getMyActivityNotification(caption,completedUnits,totalUnits));
}
}
来源:https://stackoverflow.com/questions/51243494/two-or-more-foreground-notification-with-progress-is-replacing-each-other-while