web-dev-qa-db-ja.com

RemoteViewsで通知を更新する方法は?

通知をフォアグラウンドモードで実行しているカスタムRemoteViewsから Service で通知を作成しています(つまり、サービスは通知はユーザーに表示されます)。通知は[継続中]に設定されているため、ユーザーは通知をスワイプできません。

たとえば、リモートビューのレイアウトに含まれるImageViewに表示されるビットマップを変更したり、TextViewのテキスト値を変更したりします。リモートビューのレイアウトは、XMLレイアウトファイルで設定されます。

私の問題は、通知が作成されてユーザーに表示されると、 setImageViewResource() のようなRemoteViewsの関数を呼び出してBitmapImageViewの場合、setImageViewResource()を呼び出さない限り、変更は表示されません。後で呼び出す:

NotificationManager.notify( id, notification );

または

Service.startForeground(id,notification);

しかし、これは私には正しく聞こえません。作成済みの通知でRemoteViews UIを更新するには、通知を再初期化する必要があるとは信じられません。通知にButtonコントロールがある場合、タッチおよびリリース時にそれ自体が更新されます。したがって、これを適切に行う方法がありますが、その方法はわかりません。

Serviceインスタンス内に通知を作成するコードは次のとおりです。

this.notiRemoteViews = new MyRemoteViews(this,this.getApplicationContext().getPackageName(),R.layout.activity_noti1);

Notification.Builder notibuilder = new Notification.Builder(this.getApplicationContext());
notibuilder.setContentTitle("Test");
notibuilder.setContentText("test");
notibuilder.setSmallIcon(R.drawable.icon2);
notibuilder.setOngoing(true);

this.manager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
this.noti = notibuilder.build();
this.noti.contentView = this.notiRemoteViews;
this.noti.bigContentView = this.notiRemoteViews;
this.startForeground(NOTIFICATION_ID, this.noti);

UIの変更を通知に「強制」する機能:

public void updateNotiUI(){
    this.startForeground(NOTIFICATION_ID, this.noti);
}

MyRemoteViewsクラス内で、必要に応じて、UIを変更するためにこれを行います。

this.setImageViewResource(R.id.iconOFF, R.drawable.icon_off2);
this.ptMyService.updateNotiUI();

通知のRemoteViewsのUIコンポーネントを更新する正しい方法を教えてください。

30
Sinisa

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

通知のレイアウト、つまり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>

この例があなたのお役に立てば幸いです!

注:pre-HoneycombでカスタムNotificationCompatを更新することはできません。したがって、pre-Honeycombで更新する別の方法を追加しました。つまり、最初にAPIレベルをチェックし、代わりに非推奨のNotificationを使用します。

49
Anggrayudi H

警告!

通知を更新する唯一の正しい方法は、各NotificationManager#notifyの前にRemoteViewを再作成することです。どうして?これらの質問で報告されているように、TransactionTooLargeExceptionにつながるメモリリークがあります。

SetViewVisibility(...)などのRemoteViewの各呼び出しは、対応するアクションが適用されるアクションのキューに追加されます。通知後、リモートビューが膨張し、実際にアクションが適用されます。しかし、キューはクリアされません!

このケースのデバッグ中に撮影したスクリーンショットを見てください。

enter image here

そこで、ViewModelからのデータでオーディオプレーヤーの通知を更新しています。アプリケーションは#81行目で停止し、サイズ51のアクションの配列を持つRemoteViewsインスタンスを確認できます!しかし、オーディオトラックを2回だけ切り替えて、一時停止を押しました!もちろん、しばらくしてTransactionTooLargeExceptionでアプリケーションのクラッシュを観察する必要がありました。

浅い調査により、アクションキューを直接または間接的にクリアするパブリックAPIがないことが確認されたため、通知ビューを更新する唯一の方法は、その状態を個別に保持し、Notification.Builderに渡されるRemoteViewsインスタンスを再作成することです。

4
Max Elkin

通知ビューを更新することを通知システムに通知するには、NotificationManager.notify(id, notification)を呼び出す必要があります。こちらがドキュメントリンクです http://developer.Android.com/training/notify-user/managing.html

Notificationオブジェクトを返すメソッドがあります。

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
}

また、bigContentViewRemoteViewsは完全には再描画されないことに注意してください。 bigContentViewの一部の要素の可視性がGONEに設定されており、次回表示する場合は、VISIBLEに可視性を明示的に設定する必要があります。

2
Froyo

Notificationオブジェクトを保存せず、Notification.Builderオブジェクト。プッシュする前に毎回新しい通知を生成する

NotificationManager.notify( id, notification );
0
dykzei eleeot