今作っているツイッタークライアントが、N(Android 7.0)になった辺りからContext
をリークさせるようになってしまったのでいろいろと調査した。
コードーディング会にもお邪魔して集中して取り組んだ。主催者の@operandoOSさん、会場を提供してくださったメルカリさん、ありがとうございました。
背景
このあたり
UdonRoad/MediaViewActivity.java at master · akihito104/UdonRoad · GitHub
UdonRoad/VideoMediaFragment.java at master · akihito104/UdonRoad · GitHub
MediaViewActivity
はViewPager
を持っており、FragmentPagerAdapter
を通して動画を再生するためのViewMediaFragement
をViewPager
にセットする*1。ViewMediaFragment
ではlayout resourceからViewGroup
をinflateしているのだが、ここでinflateしたVideoView
からContext
が漏れている様子。leakcanaryのスタックトレースは次の通り。
* GC ROOT android.media.PlayerBase$1.this$0 (anonymous subclass of com.android.internal.app.IAppOpsCallback$Stub) * references android.media.MediaPlayer.mSubtitleController * references android.media.SubtitleController.mAnchor * references android.widget.VideoView.mContext * leaks com.freshdigitable.udonroad.MediaViewActivity instance * Retaining: 59KB.
コードを読んで調べたこと
次の順で追いかけると、スタックトレースの3段目にあるSubtitleController.mAnchor
はVideoView
自身であるということがわかる。
Cross Reference: /frameworks/base/core/java/android/widget/VideoView.java
=> Cross Reference: /frameworks/base/media/java/android/media/MediaPlayer.java
=> Cross Reference: /frameworks/base/media/java/android/media/SubtitleController.java
スタックトレースの一番上のやつはPlayerBase
のコンストラクタの中でAppOpsService
にセットされているのだが、
Cross Reference: /frameworks/base/media/java/android/media/PlayerBase.java
リソースをリリースする時にこれがなぜか解放されないためにリークしているのではないか、と理解した。
Cross Reference: /frameworks/base/media/java/android/media/PlayerBase.java
PlayerBase
はNで追加されたクラスであるようす。
ぐぐったこと
同じようなリークの現象で困っている人がいたのだが、ButterKnifeのUnbinder.unbind()
をしていないからだと指摘されており、これを適用していい感じになったよありがとう!というやりとりで閉じられている。
ここにきて、僕が気づいていないだけで、僕も同じように参照をうまく解放できていないんじゃないかと思い始めてしまいふりだしへ。
その他のVideoView情報
M(Android 6.0)より前のVideoView
はAudioManager
がContext
を掴んで離さない(強参照している)ためにリークする。応急処置のワークアラウンドとしてつぎのようなコードが紹介されている。
Mの間だけは平穏だった様子。
ちなみに7.0以前のコードにもお茶目があったようで、これは7.1で修正された*2。issue trackerをみればわかることだが気が向いた人は探してみてほしい。
まとめ
SurfaceView + ExoPlayerで君だけのVideoViewを作ろう。根本的によくないコードを書いているんじゃないかという気もしていて、単に取り替えただけでは勉強にならないのでもう少し取り組むことにする。何か分かったら続きを書くかもしれない。