【Android】外部から取得した画像をデコードする時に気をつけること


APIなどを叩いて外部から取得した画像データを利用する場合、エラー等のデコードできない可能性や大きすぎる画像である可能性などを考慮する必要がある。
かなり大きな画像をデコードしてしまうと、Out of Memoryでクラッシュする可能性もあるため、直接ピクセルデータをデコードするのではなく、最初にサイズ情報のみをデコードし、必要なサンプリング数を設定してからデコードすることが必要になる。
こうしたことを考慮すると、以下のようなメソッドをutilメソッドとして定義しておき、使い回すのが好ましい。

    fun decodeBitmapFromByteArray(
        data: ByteArray,
        @Dimension requestWidth: Int,
        @Dimension requestHeight: Int
    ): Bitmap {
        val options = BitmapFactory.Options()

        options.inJustDecodeBounds = true
        BitmapFactory.decodeByteArray(data, 0, data.size, options)

        options.inSampleSize = sampleSize(
            options.outWidth, options.outHeight, requestWidth, requestHeight
        )
        options.inJustDecodeBounds = false
        return BitmapFactory.decodeByteArray(data, 0, data.size, options)
    }

    private fun sampleSize(
        sourceWidth: Int, sourceHeight: Int,
        requestWidth: Int, requestHeight: Int
    ): Int {
        if (requestWidth <= 0 || requestHeight <= 0) {
            return 1
        }
        if (sourceWidth * requestHeight < requestWidth * sourceHeight) {
            return if (sourceWidth > requestWidth) sourceWidth / requestWidth else 1
        }
        return if (sourceHeight > requestHeight) sourceHeight / requestHeight else 1
    }

inJustDecodeBoundsをtrueにしてdecodeByteArrayを呼び出すと、画像をメモリに展開せずに画像サイズを取得することができる。これを利用して画像サイズからサンプリングのサイズを決定し、optionsのinSampleSizeに設定した後に、inJustDecodeBoundsをfalseにして実際にdecodeを行う。