Bagaimana cara memperbarui teks pemberitahuan untuk layanan latar depan di Android?

133

Saya memiliki pengaturan layanan latar depan di Android. Saya ingin memperbarui teks notifikasi. Saya membuat layanan seperti yang ditunjukkan di bawah ini.

Bagaimana saya bisa memperbarui teks pemberitahuan yang diatur dalam layanan latar depan ini? Apa praktik terbaik untuk memperbarui notifikasi? Kode sampel apa pun akan dihargai.

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);

    }

Saya membuat layanan di aktivitas utama saya seperti yang ditunjukkan di bawah ini:

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

Jawaban:

61

Saya akan berpikir bahwa menelepon startForeground()lagi dengan ID unik yang sama dan Notificationdengan informasi baru akan berhasil, meskipun saya belum mencoba skenario ini.

Pembaruan: Berdasarkan pada komentar, Anda harus menggunakan NotifcationManager untuk memperbarui pemberitahuan dan layanan Anda tetap berada dalam mode latar depan. Lihatlah jawaban di bawah ini.

CommonsWare
sumber
1
Bisakah Anda memberi saya contoh bagaimana saya akan memanggil itu dari Aktivitas saya? Saya belum dapat menemukan sampel yang baik tentang cara memanggil metode di layanan latar depan saya.
Luke
1
@ Lukas: Ada sejumlah pola untuk menggunakan layanan, dan saya tidak tahu apa yang Anda ikuti. Jika Anda menelepon startService()untuk menyampaikan perintah ke layanan, maka cukup panggil startService()lagi untuk memberitahu itu untuk memperbarui teksnya. Atau, jika Anda menelepon bindService(), tambahkan metode ke API Anda agar layanan memperbarui teksnya. Atau, pertimbangkan apakah layanan itu sendiri yang harus memutuskan apakah akan memperbarui teks atau tidak. Atau, mungkin teksnya adalah SharedPeferencebahwa layanan tersebut memiliki pendengar. Tidak mungkin memberi Anda saran akurat dalam abstrak.
CommonsWare
9
untuk mengklarifikasi lebih lanjut: Anda tidak dapat cancel()Pemberitahuan yang ditetapkan oleh startForeground(). Anda harus menghapus status latar depan layanan itu sendiri (menggunakan stopForeground()jika Anda ingin membuat teks ticker muncul lagi. Saya kehilangan jam karena jawaban ini membuat saya percaya itu sebenarnya mungkin.
slinden77
4
Saya telah menurunkan jawaban ini karena jelas-jelas salah: developer.android.com/training/notify-user/managing.html Harap @CommonsWare pertimbangkan untuk menghapus jawaban ini, karena skor reputasi tinggi Anda menjadikan jawaban ini sebagai "kebenaran suci" untuk browser biasa. Terima kasih.
HYS
2
Tidak bekerja untuk saya (walaupun saya ingat menggunakan metode yang sama dalam proyek sebelumnya). Menggunakan NotificationManagerbekerja seperti yang saya harapkan.
user149408
224

Saat Anda ingin memperbarui Notifikasi yang diatur oleh startForeground (), cukup buat notifikasi baru dan kemudian gunakan NotificationManager untuk memberitahukannya.

Poin kuncinya adalah menggunakan id notifikasi yang sama.

Saya tidak menguji skenario berulang kali memanggil startForeground () untuk memperbarui Pemberitahuan, tapi saya pikir menggunakan NotificationManager.notify akan lebih baik.

Memperbarui Pemberitahuan TIDAK akan menghapus Layanan dari status latar depan (ini hanya dapat dilakukan dengan menelepon stopForground);

Contoh:

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);
}

The dokumentasi negara

Untuk mengatur pemberitahuan agar dapat diperbarui, terbitkan dengan ID pemberitahuan dengan menelepon NotificationManager.notify(). Untuk memperbarui pemberitahuan ini setelah Anda menerbitkannya, perbarui atau buat NotificationCompat.Builderobjek, buat Notificationobjek dari itu, dan terbitkan Notificationdengan ID yang sama dengan yang Anda gunakan sebelumnya. Jika pemberitahuan sebelumnya masih terlihat, sistem memperbaruinya dari konten Notificationobjek. Jika pemberitahuan sebelumnya telah ditolak, notifikasi baru dibuat sebagai gantinya.

Luca Manzo
sumber
35
INI ADALAH JAWABAN YANG BENAR! Jawaban di atas sangat salah dan menyesatkan. Anda tidak perlu memulai ulang layanan Anda hanya agar Anda memperbarui pemberitahuan konyol.
Radu
7
@ Radu Walaupun saya setuju bahwa ini adalah jawaban yang optimal (menghindari jalur kode yang sedikit lebih lama diambil oleh jawaban Commonsware) Anda salah tentang apa yang dijawab oleh Commonsware - start / stop. .
Stevie
@ Stevie Terima kasih untuk Stevie Anda mungkin benar. Tetap saja saya tidak akan mengacaukannya juga!
Radu
Memanggil notify()atau startForeground()keduanya mengarah ke panggilan onStartCommand().
M. Reza Nasirloo
10
Masalah dengan menggunakan NotificationManageruntuk memperbarui pemberitahuan yang ditampilkan startForegroundadalah bahwa panggilan stopForegroundtidak akan lagi menghapus pemberitahuan. Memperbaruinya dengan panggilan lain untuk startForegroundmenyingkirkan masalah itu.
Tad
21

Memperbaiki jawaban Luca Manzo di android 8.0+ saat memperbarui notifikasi itu akan membuat suara dan ditampilkan sebagai Heads-up.
untuk mencegah hal itu Anda perlu menambahkansetOnlyAlertOnce(true)

jadi kodenya adalah:

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);
}
humazed
sumber
Kamu menyelamatkan hariku. Terima kasih
Rubén Viguera
1
Kata kunci tidak ada, harus adanew NotificationChannel
7hny
5

inilah kode untuk melakukannya di layanan Anda . Buat pemberitahuan baru, tetapi minta manajer pemberitahuan untuk memberi tahu id notifikasi yang sama dengan yang Anda gunakan di startForeground.

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

notificationManager.notify(ONGOING_NOTIFICATION, notify);

untuk kode sampel lengkap, Anda dapat memeriksa di sini:

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

Daniel Kao
sumber
Saya tidak yakin ini akan mempertahankan status latar depan startService.
Martin Marconcini
@Daniel Kao Solusi Anda tidak memulai layanan latar depan
IgorGanapolsky
4
Koreksi saya jika saya salah, tetapi bisakah orang yang memberikan suara untuk jawaban ini lebih deskriptif tentang apa yang salah? Pertanyaannya bukan menanyakan bagaimana memulai layanan Foreground, tetapi bagaimana memperbarui notifikasi layanan foreground. Ini adalah jawaban yang sama efektifnya dengan Luca yang disetujui orang berfungsi dan mempertahankan status latar depan.
TheIT
@TheIT Itu Tidak berfungsi. Status pemberitahuan menjadi not foregrounduntuk foreground createdpesan.
Vyacheslav
1
Ini akan menghasilkan pemberitahuan rangkap, karena startForeground () sudah dipanggil.
IgorGanapolsky
2

Tampaknya tidak ada jawaban yang ada yang menunjukkan cara menangani kasing lengkap - untuk memulai Foreground jika ini adalah panggilan pertama tetapi perbarui pemberitahuan untuk panggilan berikutnya.

Anda dapat menggunakan pola berikut untuk mendeteksi case yang tepat:

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));
    }
}

Selain itu, Anda perlu membuat pemberitahuan dan saluran seperti pada jawaban lain:

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);
}
Nick Cardoso
sumber