
[volca] RemoteViewsを使ったNotificationでTransactionTooLargeExceptionクラッシュする
こんにちは。
最近アウトプットができていなかったのですが、個人アプリ開発でのできごとをnoteに書いていくことにしようと思い書いていきます。
よろしくお願いします。
volca、はAndroid端末の音量を通知やクイック設定タイルから確認/操作できるアプリです。
経緯
以前より、レビューやメールで「通知が突然消えてしまう」というご意見をいただいていました。
通知を使った音量の操作はForegroundServiceを使って動かしており、長時間実行していて停止候補になっているのかなと思っていました。
(START_STICKY指定しているので再作成される見込みですが)
しかし、Crashlyticsを見てみると、NotificationManager.notify実行時にTransactionTooLargeExceptionでクラッシュしているとのログが。
Caused by android.os.TransactionTooLargeException
data parcel size 655660 bytes
android.os.BinderProxy.transactNative (BinderProxy.java)
android.app.NotificationManager.notify (NotificationManager.java:353)
com.wisteriastone.volca.domain.notification.VolumeNotificationService.onUpdate (VolumeNotificationService.java:28)
また、Stack Overflowの回答にあった、同じ事象に対する避けるべき実装を自アプリでやってしまっていることに気づき、改修を始めます。
避けるべき実装
RemoteViewsを使い回す

通知はカスタムのRemoteViewsを使い、音量の確認/操作ができるようになっています。
端末の音量変化を反映させるため、RemoteViews内でアプリケーションスコープでシングルトンになっているRepositoryの値を監視し、通知のUIに反映させていました。
改修前の実装では、RemoteViewsは一度作ったものを使いまわすようにしており、通知上のボタン操作があった場合は同じRemoteViewsを使ってNotificationwを作成し、NotificationManager#notifyを行っていました。
問題点
RemoteViews.mActionが増え続ける。
RemoteViewsのUI更新時にsetTextViewTextなどのset〜メソッドを呼ぶと、内部でaddActionが呼ばれ、mActionにActionが追加されます。
public void setCharSequence(@IdRes int viewId, String methodName, CharSequence value) {
addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.CHAR_SEQUENCE,
value));
}
同じRemoteViewsを使いまわしRemoteViewsのUI更新を続けているとmActionが増え続け、やがてTransactionTooLargeExceptionでクラッシュしてしまう、ことになっていました。
改修方法
RemoteViewsの使い回しを止めます。
Serviceでボタン操作のIntentアクションを受けて通知を更新していますが、その度にRemoteViewsを作り直し、NotificationManager#notifyするよう改修しました。
端末の音量変化を反映させるため、RemoteViews内でアプリケーションスコープでシングルトンになっているRepositoryの値を監視し、通知のUIに反映させていました。
こちらの方法で端末の音量変化の反映ができなくなったため、カスタムRemoteViewsを作る側で表示すべき状態を取得し、RemoteViewsに渡す方法に変更しました。
まとめ
RemoteViewsを使ったNotificationを使うときには、RemoteViewsを使いまわさず、UI更新のたびに作り直すようにする
UI変更時にRemoteViewsの内部でmActionが増やされ、TransactionTooLargeExceptionを引き起こすため