How to update Notification with RemoteViews?

后端 未结 4 601
感情败类
感情败类 2020-11-29 22:48

I\'m creating a notification with RemoteViews from a custom Service, which is running with notification in a foreground mode (that is, service will remain activ

相关标签:
4条回答
  • 2020-11-29 23:28

    You would have to call NotificationManager.notify(id, notification) to let Notification System know that you want to update the notification view. Here's the docs link http://developer.android.com/training/notify-user/managing.html.

    Have a method which returns Notification object.

    private Notification getNotification(NotificationCompat.Builder mBuilder) {
        RemoteViews mRemoteViews = new RemoteViews(getPackageName(), R.layout.notification_layout);
        // Update your RemoteViews
        mBuilder.setContent(mRemoteView);
        Notification mNotification = mBuilder.build();
        // set mNotification.bigContentView if you want to
        return mNotification;
    
    }
    
    private void refreshNotification() {
        mNotificationManager.notify(getNotification(mNotificationBuilder),
                            NOTIFICATION_ID);
        // mNotificationBuilder is initialized already
    }
    

    Also, note that bigContentView and RemoteViews are not completely redrawn. If some of the elements of bigContentView has visibility set to GONE, and if you want to show it the next time, you have to explicitly set visibility to VISIBLE.

    0 讨论(0)
  • 2020-11-29 23:51

    WARNING!

    The only correct way to update notification is to recreate RemoteViews before each NotificationManager#notify. Why? There's a memory leak leading to TransactionTooLargeException, as it has been reported in those questions:

    • TransactionTooLargeException on Samsung S7
    • TransactionTooLargeException while notify notification
    • Android notification manager TransactionTooLargeException
    • and in my case

    Each call on RemoteViews such as setViewVisibility(...) and so on adds corresponding action to queue of actions are to be applied. After notify the remote view is inflated and actions are actually applied. But the queue is not cleared!

    Take a look at screenshot taken during debugging this case.

    There I'm updating audio player notification with data coming from ViewModel. Application is stopped on line #81 and you can see the RemoteViews instance that has array of actions with size 51! But I only switched audio track twice and pressed pause! Of course I had to observe application crash with TransactionTooLargeException after a while.

    Shallow research confirmed there's no public API to directly or indirectly clear actions queue, so the only way to update notification view is to hold its state separately and recreate RemoteViews instance passed to Notification.Builder, anyway this doesn't overload UI thread a lot

    0 讨论(0)
  • 2020-11-29 23:52

    Here's a detail example for you to update the notification using RemoteViews:

    private static final int NOTIF_ID = 1234;
    private NotificationCompat.Builder mBuilder;
    private NotificationManager mNotificationManager;
    private RemoteViews mRemoteViews;
    private Notification mNotification;
    ...
    
    // call this method to setup notification for the first time
    private void setUpNotification(){
    
        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    
        // we need to build a basic notification first, then update it
        Intent intentNotif = new Intent(this, MainActivity.class);
        intentNotif.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent pendIntent = PendingIntent.getActivity(this, 0, intentNotif, PendingIntent.FLAG_UPDATE_CURRENT);
    
        // notification's layout
        mRemoteViews = new RemoteViews(getPackageName(), R.layout.custom_notification_small);
        // notification's icon
        mRemoteViews.setImageViewResource(R.id.notif_icon, R.drawable.ic_launcher);
        // notification's title
        mRemoteViews.setTextViewText(R.id.notif_title, getResources().getString(R.string.app_name));
        // notification's content
        mRemoteViews.setTextViewText(R.id.notif_content, getResources().getString(R.string.content_text));
    
        mBuilder = new NotificationCompat.Builder(this);
    
        CharSequence ticker = getResources().getString(R.string.ticker_text);
        int apiVersion = Build.VERSION.SDK_INT;
    
        if (apiVersion < VERSION_CODES.HONEYCOMB) {
            mNotification = new Notification(R.drawable.ic_launcher, ticker, System.currentTimeMillis());
            mNotification.contentView = mRemoteViews;
            mNotification.contentIntent = pendIntent;
    
            mNotification.flags |= Notification.FLAG_NO_CLEAR; //Do not clear the notification
            mNotification.defaults |= Notification.DEFAULT_LIGHTS;
    
            // starting service with notification in foreground mode
            startForeground(NOTIF_ID, mNotification);
    
        }else if (apiVersion >= VERSION_CODES.HONEYCOMB) {
            mBuilder.setSmallIcon(R.drawable.ic_launcher)
                    .setAutoCancel(false)
                    .setOngoing(true)
                    .setContentIntent(pendIntent)
                    .setContent(mRemoteViews)
                    .setTicker(ticker);
    
            // starting service with notification in foreground mode
            startForeground(NOTIF_ID, mBuilder.build());
        }
    }
    
    // use this method to update the Notification's UI
    private void updateNotification(){
    
        int api = Build.VERSION.SDK_INT;
        // update the icon
        mRemoteViews.setImageViewResource(R.id.notif_icon, R.drawable.icon_off2);
        // update the title
        mRemoteViews.setTextViewText(R.id.notif_title, getResources().getString(R.string.new_title));
        // update the content
        mRemoteViews.setTextViewText(R.id.notif_content, getResources().getString(R.string.new_content_text));
    
        // update the notification
        if (api < VERSION_CODES.HONEYCOMB) {
            mNotificationManager.notify(NOTIF_ID, mNotification);
        }else if (api >= VERSION_CODES.HONEYCOMB) {
            mNotificationManager.notify(NOTIF_ID, mBuilder.build());
        }
    }
    

    Layout for the Notification, i.e. res/layout/custom_notification_small.xml:

    <!-- We have to set the height to 64dp, this is the rule of the small notification -->
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:orientation="horizontal"
        android:id="@+id/notif_small"
        android:background="@drawable/notification_background">
    
        <ImageView
            android:id="@+id/notif_icon"
            android:contentDescription="@string/notif_small_desc"
            android:layout_width="47dp"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_alignParentLeft="true"
            android:src="@drawable/ic_launcher"
            android:layout_marginLeft="7dp"
            android:layout_marginRight="9dp"/>
    
        <TextView
            android:id="@+id/notif_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/notif_icon"
            android:singleLine="true"
            android:paddingTop="8dp"
            android:textSize="17sp"
            android:textStyle="bold"
            android:textColor="#000000"
            android:text="@string/app_name"/>
    
        <TextView
            android:id="@+id/notif_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/notif_icon"
            android:paddingBottom="9dp"
            android:layout_alignParentBottom="true"
            android:singleLine="true"
            android:textSize="13sp"
            android:textColor="#575757"
            android:text="Content" />
    </RelativeLayout>
    

    Hope this example helps you a lot!

    NOTE: You can't update the custom NotificationCompat on pre-Honeycomb, so I added an alternative way to update it on pre-Honeycomb, i.e. checking the API level first and use the deprecated Notification instead.

    0 讨论(0)
  • 2020-11-29 23:53

    Don't store Notification object, but the Notification.Builder object. Produce new notification every time before pushing it to

    NotificationManager.notify( id, notification );
    
    0 讨论(0)
提交回复
热议问题