RemoteViews の内部構造
AppWidget や Notification のカスタム View に用いられる RemoteViews は、実際のところ View そのものは内包しておらず、View に関する情報(レイアウトのリソース ID やホストアプリケーションの情報など)と、View の操作を説明する情報を保持しています。
たとえば、RemoteViews を使って TextView に文字列をセットする場合、内部では、「setText
を指定された引数で呼び出す」という情報が RemoteViews の中に記録されます。この操作に関する情報はリスト形式ですべて RemoteViews の中に記録されており、RemoteViews#apply()
を呼び出すことでそれらが実行され、View が展開されるという仕組みになっています。
RemoteViews でのイベントハンドリングではPendingIntent
を使います。View
のクリックに反応してPendingIntent
に指定したIntent
が発行されるようになっています。
RemoteViews を活用する
これらを用いることで、他のアプリが今何をしているのか、ヒントを掴むことができるようになります。
ではどのようにそれらの情報を読み取るのかというと、公開された API は存在しないので、リフレクションで読むことになります。
RemoteViews
のmActions
には、操作の記録が残ります。リストに含められる情報はすべてParcelable
で、RemoteViews.Action
クラスを継承しています。操作の種類によって実際の具象クラスは異なりますが、概ねどのクラスであっても、どの View に対する操作かを明示するためのリソース ID が入ります。その他、リフレクションでメソッドを呼び出すのに必要な情報を持つクラスがあったり、Drawable のリソース ID を保持するクラスがあったりします。中には、RemoteViews をネストするものがあり、そこからさらに RemoteViews に対するアクションが記録されている、というようなものもあります。
このあたりはRemoteViewsを眺めているとひと通り把握できます。
ここまでの情報で何を読み取るかは、したいこととアプリケーションの動きに依存します。ぜひ色々ログに吐き出したり、デバッガをアタッチして中を覗いてみたりすると良いでしょう。
余談:PendingIntent も活用してしまう
PendingIntent もリフレクションを使うことでより多くの情報を得ることが出来ます。@hide
な API で、PendingIntent から発行される実際の Intent を取り出すメソッドが定義されているので、リフレクションでそのメソッドを呼び出し Intent が得て、いろいろ読み取ることができますね。
他のアプリの情報を読み取る、という点では、AccessibilityService が良く用いられますが、AppWidget や Notification に関して言えば、RemoteViews から読み取るというのも一つの手段です。
という訳で、こういう闇のコードを書く人も居るので、PendingIntent だからといって Intent 情報が読み取れないとたかをくくるのは危険です。気を付けましょう。
さいごに
情報を読み出すコードが、リフレクションや Parcel から read するのばかりで、可読性とメンテナンス性が第三象限の方まで振り切っているので、まあなんかそのあたりを手伝ってくれる良い子のライブラリを書きました。