Glide を Coil に置き換える

# 概要
画像読み込みライブラリーの Glide を Coil に置き換える際のポイントについて書きます。

## Coil
Kotlin で実装された画像読み込みライブラリーです。[今月1.0がリリース](https://github.com/coil-kt/coil/releases/tag/1.0.0)されました。
軽量で十分なパフォーマンスを出すことができ、簡単に使える点が特徴だと紹介されています。

| 公式サイト | https://coil-kt.github.io/coil/
|:---|:---
| GitHub | https://github.com/coil-kt/coil

また、GIF画像についてはオプションです。[coil-gif](https://github.com/coil-kt/coil/tree/master/coil-gif) を別途導入する必要があります。

### コードの書きやすさ
後発ライブラリーだけあってコードの書きやすさは上回っています。Kotlin の拡張関数を用いて、簡潔に Kotlin らしいコードを書ける実装になっています。

私が14ファイルで Glide を使っているプロジェクトで置き換えを実施したところ、すべての書き換えが30分程度で終わりました。

### パフォーマンス
登場したばかりの頃は Glide に比べてパフォーマンス面で水をあけられていたようですが、最近(2020年10月)の記事だと大差ないほどに改善されているようです。

- [Benchmarking Image Loading Libraries on Android](https://colinwhite.medium.com/benchmarking-image-loading-libraries-on-android-3ddf365a1927)

### ライセンス
[Apache License 2.0](https://github.com/coil-kt/coil/blob/1.0.0/LICENSE.txt) でライセンスされています。

# 導入
依存を1行追加するだけで良いです。

```groovy:app/build.gradle
implementation "io.coil-kt:coil:1.0.0"
```

ProGuard の設定は Coil 自体は不要で、Coroutines や Okio や OkHttp の設定が追加されていない時は入れる必要があります。
https://github.com/coil-kt/coil#r8--proguard

# 置き換え
Kotlin の拡張関数で ImageView にロード用の関数が追加されます。

## 単純な読み込み

### Glide

```kotlin
Glide.with(imageView).load(url).into(imageView)
```

### Coil
Coil の場合は into しなくてよいです。

```kotlin
imageView.load(url)
```

## 読み込み時のパラメーター指定
読み込みサイズの変更等をする場合、Coil では Closure で指定します。

### Glide

```kotlin
Glide.with(imageView)
    .load(url)
    .placeholder(placeholder)
    .override(400)
    .into(imageView)
```

### Coil

```kotlin
imageView.load(url) { 
    placeholder(placeholder)
    size(400)
}
```

## ファイルの読み込み
Glide でファイルのパスを文字列で指定していたところは、Coil だと直接 File オブジェクトを渡せば読み込んでくれるので、そう修正します。
逆にパスだと読み込めません。String がファイルのパスではなく画像のURLとして扱われるためです。

### Glide

```kotlin
Glide.with(binding.icon)
        .load(path)
        .into(binding.icon)
```

### Coil
ファイルパスの場合は File オブジェクトにしてから渡す必要があります。

```kotlin
binding.icon.load(File(path))
```

## Drawable や Bitmap の読み込み
ImageView に直接読み込ませる以外にも、画像をダウンロードして Bitmap や Drawable として扱いたい場合、
Glide ではこのように書けました。

```kotlin
val image = Glide.with(context)
    .asBitmap()
    .load(uri)
    .submit()
    .get()
```

Coil では ImageLoader を使います。Context にインスタンスを取得するための拡張関数が追加されています。
Drawable の読み込みのみ対応していて、 Bitmap へは変換する関数を呼び出します。

```kotlin
val image = context.imageLoader
        .execute(ImageRequest.Builder(context).data(uri).build())
        .drawable
        ?.toBitmap()
```

## GIF 画像の読み込み
Coil では GIF 画像の読み込みはオプションです。必要な場合は [coil-gif](https://github.com/coil-kt/coil/tree/master/coil-gif) を導入しましょう。

```groovy:app/build.gradle
implementation "io.coil-kt:coil-gif:1.0.0"
```

load 関数の第2引数に、GIF画像読み込みに対応した ImageLoader を渡します。

```kotlin
fun setImage(path: String) {
    view.load(File(path), imageLoader)
}

private val imageLoader = ImageLoader.Builder(view.context)
        .componentRegistry {
            add(if (Build.VERSION.SDK_INT >= 28) ImageDecoderDecoder() else GifDecoder())
        }
        .build()
```

## （必要があれば）Okio の2系に変更
Coil では [Okio の2系](https://github.com/coil-kt/coil/blob/85557bb307927ed8f542db63fbe9c3216c6339eb/buildSrc/src/main/kotlin/Library.kt#L36)なので、 を利用しており、導入すると Okio が2系に更新されます。
Okio を直接使ってファイルの入出力操作を実装している場合、書き換えの対応をする必要があります。
Okio の2系は Kotlin の拡張関数を使うスタイルです。

[Recipes](https://github.com/square/okio#recipes) を参考に書き換えました。

### Source(読み込み)

```kotlin:1.x
Okio.buffer(Okio.source(file)).use { it.readByteArray() } 
```

```kotlin:2.x
file.source().use { source -> source.buffer().use { it.readByteArray() } }
```

### Sink(書き込み)

```kotlin:1.x
Okio.buffer(Okio.sink(file)).use { it.write(parcel.marshall()) }
```

```kotlin:2.x
file.sink().use { sink ->
    sink.buffer().use {
        it.write(parcel.marshall())
    }
}
```


## 測定
置き換えが完了したところで、APK の中身を確認してみました。

| | Classes | Methods | References methods
|:---|---:|---:|---:|---:
| Glide 版 | 6561 | 37985 | 43910
| Coil 版  | 6359 | 37058 | 42890

参照メソッド数が1,000近く減っているのはすごいですね。

# おわりに
Glide を使っているコードを Coil に置き換える際のポイントについて紹介しました。
単なる画像読み込み程度の軽い使い方であれば、置き換えるのにそう苦労はしないでしょう。
ただし、Okio を直接使っている場合は2系への書き換えが必要です。

使ってみた感じですと、以下のプロジェクトなら置き換えを進めてもよいかもしれません。

1. 完全新規のプロジェクト
2. Kotlin への移行が完全に終わっているプロジェクト
3. あまり Glide を使い込んでいない

また、クラス数やメソッド数を少しでも削減したいプロジェクトは導入を検討してみてもよいでしょう。