【Android】メモリリークが頻発して苦戦しながらも解決した話

先日Google Playに趣味で作ったアプリを初リリースしたのですが、

そのアプリ開発中にメモリリークが頻発していることが分かり、メモリリークってどう直せばいいんだ?と色々調べたり試したりして解決したことを残しておこうと思います。

なぜメモリリークしていると気づいたか

アプリ開発もいよいよ終盤をむかえる頃、ふと「そういえば会社で作っているアプリにはLeakCanaryってライブラリを入れていたなあ」と思い入れてみたのでした。

LeakCanaryはメモリリークを検出してくれるライブラリですが、これを入れたところすぐにメモリリークが発生しました・・・/(^o^)\

何が原因だったか

私の場合、2つありました。

①DataBindingを開放していなかった

②変数がViewを参照している場合に開放していなかった

それぞれ詳しく書いていきます。

①DataBindingを開放していなかった

[修正前]

private lateinit var binding: FragmentEditBinding

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    super.onCreateView(inflater, container, savedInstanceState)
    binding = FragmentEditBinding.inflate(inflater, container, false)
    binding.lifecycleOwner = this
    return binding.root
}

[修正後]

private var _binding: FragmentEditBinding? = null
private val binding get() = _binding!!

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    super.onCreateView(inflater, container, savedInstanceState)
    _binding = FragmentEditBinding.inflate(inflater, container, false)
    binding.lifecycleOwner = this
    return binding.root
}

override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

もともとDataBindingを非オプショナル型でlateinitで保持していたのですが、

オプショナル型の_bindingを定義し、bindingでは_bindingを取得するようにしました。

また、onDestroyView()で解放するようにしたことで、LeakCanaryで検知されなくなりました。

これはViewBindingの公式サイトに載っていた方法です。

https://developer.android.com/topic/libraries/view-binding?hl=ja#fragments

以下のサイトでも紹介されていました。

https://proandroiddev.com/avoiding-memory-leaks-when-using-data-binding-and-view-binding-3b91d571c150

②変数がViewを参照している場合に開放していなかった

以下の12-13行目を追加しました。

private var itemTouchHelper : ItemTouchHelper? = null

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    itemTouchHelper?.attachToRecyclerView(binding.recyclerView)
}

override fun onDestroyView() {
    super.onDestroyView()
    binding.recyclerView.adapter = null
    recyclerViewAdapter = null
    itemTouchHelper?.attachToRecyclerView(null)
    itemTouchHelper = null
    _binding = null
}

ItemTouchHelperは、attachToRecyclerView()でRecyclerViewの参照を持ちます。

これを変数で持っていると、RecyclerViewの参照が残り続けてしまいます。

ItemTouchHelper以外にも、変数がViewを参照している場合は解放してあげる必要がありました。


投稿日

カテゴリー:

投稿者:

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA