Làm cách nào để cập nhật văn bản thông báo cho dịch vụ nền trước trong Android?


133

Tôi có một thiết lập dịch vụ nền tảng trong Android. Tôi muốn cập nhật văn bản thông báo. Tôi đang tạo ra dịch vụ như hình dưới đây.

Làm cách nào tôi có thể cập nhật văn bản thông báo được thiết lập trong dịch vụ nền trước này? Thực hành tốt nhất để cập nhật thông báo là gì? Bất kỳ mã mẫu sẽ được đánh giá cao.

public class NotificationService extends Service {

    private static final int ONGOING_NOTIFICATION = 1;

    private Notification notification;

    @Override
    public void onCreate() {
        super.onCreate();

        this.notification = new Notification(R.drawable.statusbar, getText(R.string.app_name), System.currentTimeMillis());
        Intent notificationIntent = new Intent(this, AbList.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
        this.notification.setLatestEventInfo(this, getText(R.string.app_name), "Update This Text", pendingIntent);

        startForeground(ONGOING_NOTIFICATION, this.notification);

    }

Tôi đang tạo dịch vụ trong hoạt động chính của mình như dưới đây:

    // Start Notification Service
    Intent serviceIntent = new Intent(this, NotificationService.class);
    startService(serviceIntent);

Câu trả lời:


61

Tôi nghĩ rằng việc gọi startForeground()lại với cùng một ID duy nhất và Notificationvới thông tin mới sẽ hoạt động, mặc dù tôi chưa thử kịch bản này.

Cập nhật: Dựa trên các nhận xét, bạn nên sử dụng NotifcationManager để cập nhật thông báo và dịch vụ của bạn tiếp tục ở chế độ nền trước. Hãy xem câu trả lời dưới đây.


1
Bạn có thể vui lòng cho tôi một ví dụ về cách tôi sẽ gọi nó từ Hoạt động của mình không? Tôi chưa thể tìm thấy một mẫu hay về cách gọi các phương thức trong dịch vụ nền trước của mình.
Luke

1
@Luke: Có bất kỳ số mẫu nào để sử dụng dịch vụ và tôi không biết bạn đang theo dõi mẫu nào. Nếu bạn đang gọi startService()để chuyển một lệnh cho dịch vụ, sau đó chỉ cần gọi startService()lại để nói với nó để cập nhật văn bản của nó. Hoặc, nếu bạn đang gọi bindService(), hãy thêm một phương thức vào API của bạn để dịch vụ cập nhật văn bản của nó. Hoặc, xem xét liệu chính dịch vụ có nên là người đưa ra quyết định có nên cập nhật văn bản hay không. Hoặc, có lẽ văn bản là một SharedPeferencedịch vụ có người nghe trên. Không thể cho bạn lời khuyên chính xác trong bản tóm tắt.
CommonsWare

9
để làm rõ hơn: bạn không thể cancel()đặt Thông báo theo startForeground(). Bạn phải xóa trạng thái nền trước của chính dịch vụ (sử dụng stopForeground()nếu bạn muốn làm cho văn bản đánh dấu xuất hiện trở lại. Tôi mất hàng giờ vì những câu trả lời này khiến tôi tin rằng nó thực tế là có thể.
slinden77

4
Tôi đã đánh giá thấp câu trả lời này vì nó hoàn toàn sai: developer.android.com/training/notify-user/managing.html Vui lòng @CommonsWare xem xét xóa câu trả lời này, vì điểm danh tiếng cao của bạn làm cho câu trả lời này trở thành "sự thật thần thánh" cho trình duyệt thông thường. Cảm ơn.
HYS

2
Không làm việc cho tôi (mặc dù tôi nhớ sử dụng phương pháp tương tự trong dự án trước đó). Sử dụng NotificationManagerlàm việc như tôi mong đợi.
user149408

224

Khi bạn muốn cập nhật một Thông báo được thiết lập bởi startForeground (), chỉ cần tạo một thông báo mới và sau đó sử dụng Thông báo quản lý để thông báo.

Điểm quan trọng là sử dụng cùng một id thông báo.

Tôi đã không kiểm tra kịch bản liên tục gọi startForeground () để cập nhật Thông báo, nhưng tôi nghĩ rằng sử dụng NotificationManager.notify sẽ tốt hơn.

Cập nhật Thông báo sẽ KHÔNG xóa Dịch vụ khỏi trạng thái nền trước (điều này chỉ có thể được thực hiện bằng cách gọi stopForground);

Thí dụ:

private static final int NOTIF_ID=1;

@Override
public void onCreate (){
    this.startForeground();
}

private void startForeground() {
    startForeground(NOTIF_ID, getMyActivityNotification(""));
}

private Notification getMyActivityNotification(String text){
    // The PendingIntent to launch our activity if the user selects
    // this notification
    CharSequence title = getText(R.string.title_activity);
    PendingIntent contentIntent = PendingIntent.getActivity(this,
            0, new Intent(this, MyActivity.class), 0);

    return new Notification.Builder(this)
            .setContentTitle(title)
            .setContentText(text)
            .setSmallIcon(R.drawable.ic_launcher_b3)
            .setContentIntent(contentIntent).getNotification();     
}

/**
 * This is the method that can be called to update the Notification
 */
private void updateNotification() {
    String text = "Some text that will update the notification";

    Notification notification = getMyActivityNotification(text);

    NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.notify(NOTIF_ID, notification);
}

Các tài liệu nêu

Để thiết lập thông báo để có thể cập nhật, hãy cấp thông báo bằng ID thông báo bằng cách gọi NotificationManager.notify(). Để cập nhật thông báo này sau khi bạn đã phát hành, cập nhật hoặc tạo một NotificationCompat.Builderđối tượng, xây dựng một Notificationđối tượng từ đó và đưa ra Notificationcùng một ID bạn đã sử dụng trước đó. Nếu thông báo trước vẫn hiển thị, hệ thống sẽ cập nhật nó từ nội dung của Notificationđối tượng. Nếu thông báo trước đó đã bị loại bỏ, thay vào đó, một thông báo mới sẽ được tạo.


35
ĐÂY LÀ CÂU TRẢ LỜI CHÍNH XÁC! Câu trả lời ở trên là rất sai và sai lệch. Bạn không cần phải khởi động lại dịch vụ của mình chỉ để bạn cập nhật một thông báo ngớ ngẩn.
Radu

7
@Radu Mặc dù tôi đồng ý rằng đây là câu trả lời tối ưu (nó tránh đường dẫn mã dài hơn một chút theo câu trả lời của Commonsware) nhưng bạn đã nhầm về câu trả lời của Commonsware - start / stopForegound không bắt đầu / dừng dịch vụ .
Stevie

@Stevie Cảm ơn vì điều đó Stevie có lẽ bạn đúng. Tuy nhiên, tôi cũng sẽ không gây rối với điều đó!
Radu

Gọi notify()hoặc startForeground()cả hai dẫn để gọi onStartCommand().
M. Reza Nasirloo 7/07/2015

10
Vấn đề với việc sử dụng NotificationManagerđể cập nhật thông báo hiển thị startForegroundlà việc gọi điện stopForegroundsẽ không còn xóa thông báo. Cập nhật nó với một cuộc gọi khác để khắc phục startForegroundvấn đề đó.
Tad

21

Cải thiện câu trả lời của Luca Manzo trong Android 8.0 trở lên khi cập nhật thông báo, nó sẽ phát ra âm thanh và hiển thị dưới dạng Heads-up.
để ngăn chặn rằng bạn cần thêmsetOnlyAlertOnce(true)

vì vậy mã là:

private static final int NOTIF_ID=1;

@Override
public void onCreate(){
        this.startForeground();
}

private void startForeground(){
        startForeground(NOTIF_ID,getMyActivityNotification(""));
}

private Notification getMyActivityNotification(String text){
        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
        ((NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(
        NotificationChannel("timer_notification","Timer Notification",NotificationManager.IMPORTANCE_HIGH))
}

        // The PendingIntent to launch our activity if the user selects
        // this notification
        PendingIntent contentIntent=PendingIntent.getActivity(this,
        0,new Intent(this,MyActivity.class),0);

        return new NotificationCompat.Builder(this,"my_channel_01")
        .setContentTitle("some title")
        .setContentText(text)
        .setOnlyAlertOnce(true) // so when data is updated don't make sound and alert in android 8.0+
        .setOngoing(true)
        .setSmallIcon(R.drawable.ic_launcher_b3)
        .setContentIntent(contentIntent)
        .build();
}

/**
 * This is the method that can be called to update the Notification
 */
private void updateNotification(){
        String text="Some text that will update the notification";

        Notification notification=getMyActivityNotification(text);

        NotificationManager mNotificationManager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
        mNotificationManager.notify(NOTIF_ID,notification);
}

Bạn đã cứu ngày của tôi. Cảm ơn bạn
Rubén Viguera

1
Thiếu từ khóa, nên cónew NotificationChannel
7hny

5

Đây là mã để làm như vậy trong dịch vụ của bạn . Tạo một thông báo mới, nhưng yêu cầu người quản lý thông báo thông báo cùng id thông báo mà bạn đã sử dụng trong startForeground.

Notification notify = createNotification();
final NotificationManager notificationManager = (NotificationManager) getApplicationContext()
    .getSystemService(getApplicationContext().NOTIFICATION_SERVICE);

notificationManager.notify(ONGOING_NOTIFICATION, notify);

để biết mã mẫu đầy đủ, bạn có thể kiểm tra tại đây:

https://github.com/plateaukao/AutoScreenOn Offer/blob/master/src/com/danielkao/autoscreenonoff/SensorMonitorService.java


Tôi không chắc chắn điều này sẽ duy trì trạng thái nền trước của startService.
Martin Marconcini

@Daniel Kao Giải pháp của bạn không bắt đầu dịch vụ tiền cảnh
IgorGanapolsky

4
Sửa lỗi cho tôi nếu tôi sai nhưng những người bình chọn câu trả lời này có thể mô tả rõ hơn về những gì sai với nó không? Câu hỏi không hỏi làm thế nào để bắt đầu dịch vụ Tiền cảnh, nhưng làm thế nào để cập nhật thông báo về dịch vụ tiền cảnh. Đây thực sự là câu trả lời giống như Luca mà mọi người đồng ý làm việc và duy trì trạng thái tiền cảnh.
TheIT 17/12/13

@TheIT Nó không hoạt động. Tình trạng của thông báo trở thành not foregroundcho foreground createdtin nhắn.
Vyacheslav

1
Điều này sẽ dẫn đến một thông báo trùng lặp, bởi vì startForeground () đã được gọi.
IgorGanapolsky

2

Dường như không có câu trả lời nào hiện có cho thấy cách xử lý trường hợp đầy đủ - để startForeground nếu đó là cuộc gọi đầu tiên nhưng cập nhật thông báo cho các cuộc gọi tiếp theo.

Bạn có thể sử dụng mẫu sau để phát hiện trường hợp đúng:

private void notify(@NonNull String action) {
    boolean isForegroundNotificationVisible = false;
    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    StatusBarNotification[] notifications = notificationManager.getActiveNotifications();
    for (StatusBarNotification notification : notifications) {
        if (notification.getId() == FOREGROUND_NOTE_ID) {
            isForegroundNotificationVisible = true;
            break;
        }
    }
    Log.v(getClass().getSimpleName(), "Is foreground visible: " + isForegroundNotificationVisible);
    if (isForegroundNotificationVisible){
        notificationManager.notify(FOREGROUND_NOTE_ID, buildForegroundNotification(action));
    } else {
        startForeground(FOREGROUND_NOTE_ID, buildForegroundNotification(action));
    }
}

Ngoài ra, bạn cần xây dựng thông báo và kênh như trong các câu trả lời khác:

private Notification buildForegroundNotification(@NonNull String action) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        createNotificationChannel();
    }
    //Do any customization you want here
    String title;
    if (ACTION_STOP.equals(action)) {
        title = getString(R.string.fg_notitifcation_title_stopping);
    } else {
        title = getString(R.string.fg_notitifcation_title_starting);
    }
    //then build the notification
    return new NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentTitle(title)
            .setOngoing(true)
            .build();
}

@RequiresApi(Build.VERSION_CODES.O)
private void createNotificationChannel(){
    NotificationChannel chan = new NotificationChannel(CHANNEL_ID, getString(R.string.fg_notification_channel), NotificationManager.IMPORTANCE_DEFAULT);
    chan.setLightColor(Color.RED);
    chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
    NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    assert manager != null;
    manager.createNotificationChannel(chan);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.