タブレット用プログラムの書き止め

android OS & iPadOS の記録。

【kotlin】 非同期処理(RecyclerViewにサムネイル設定が大量の場合)

2024-04-25 15:41:21 | Android studio 日記

RecyclerView.Adapterに画像のフルパスリストを渡し、サムネイル画像を生成する。
その画像を設定すると処理時間が長すぎてサムネイル画像のスクロールがスムーズに動かない。

画像作成と設定、更新を非同期処理で行う。
システムが管理しているサムネイル画像を利用する選択もあるが自前で用意する事にする。

サンプルなどから作成したコードは、随時開始してしまいCPUの能力を削りすぎる。
また、メモリもスレッド数分消費してしまう。
画像数が少なければ問題ないが状況により書き換え更新のストップやユーザーアクションに反応しなくなる。
非同期処理の意味がなくなるのでコルーチンにシングルスレッド処理を設定する。

コンパクトで処理が速いのですが古いデバイスでは低能力、低メモリ量なので大量処理には向かないため。


アダプター修正内容。

class MyFragment : Fragment() {
    /* 省略 * /

    private val parentJob = Job()

    inner class MyAdapter() : RecyclerView.Adapter<MyAdapter.ViewHolder>() {
        /* 省略 * /

        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            holder.imageView.setImageBitmap(loadImage(position))
            /* 省略 * /
        }

        private fun loadImage(position: Int): Bitmap? {
            val imagePath: String = pathList.get(position)
            /* 省略 * /

            if (!cf.exists()) { /* キャッシュファイルが無い時 * /

                CoroutineScope(
                    newSingleThreadExecutor().asCoroutineDispatcher()
                ).launch( parentJob ) {
                    asyncTask( imagePath, _recyclerView!!, position)
                }

                return BitmapFactory.decodeResource(
                    requireContext().getResources(),
                    R.drawable.img_file
                )
            }
            return BitmapFactory.decodeFile(cf.absolutePath) /* キャッシュファイルがあれば、Bitmapを返す * /
        }
        /* 省略 * /
    }

 

    suspend fun asyncTask(path:String, rv: RecyclerView, vID: Int) { 

        /* 省略 * /
        
        val ret = MyMakeThumbnail().toThumbnailFile(souFile, cacheFile, _saveSize)

        when ( ret ) {
            0 -> if (vID < _rv.adapter!!.itemCount) {
                _rv.post(Runnable {
                    _rv.adapter!!.notifyItemChanged(vID)
                })
            }

             /* 省略 * /
        }
    }
}


非同期を開始する場所に書く。

CoroutineScope(  newSingleThreadExecutor().asCoroutineDispatcher() ).launch(parentJob) {
    asyncTask(path, _recyclerView!!, position)
}

シングルスレッド処理を開始させる。
前の処理が続いている場合は待機され順番に1つずつ処理が行われる。


コルーチンがよく分からず取り合えず動く内容という形でした。

とりわけ、runBlockingが未理解で付けたら動く。くらいに使ってました(笑)
恐ろしい。


下記サイトを参考に何となくコルーチン組みました。

【Kotlin/JVM】CoroutineDispatcher を作る
       https#//qiita.com/sdkei/items/a056a1275b05b11ddcd4


CoroutinesのlaunchとasyncとChannelとFlowの使い分け
       https#//qiita.com/naoi/items/9892db4cec2e9c0f6114