【自由研究】小さいViewに大きい画像を表示させようとするとメモリに負担がかかるのか

# 序章
[65% Smaller APKs and 70% Less Memory: How I Optimized My Android App -Part II (App Memory)](https://medium.com/@tarunanchala/65-smaller-apks-and-70-less-memory-how-i-optimized-my-android-app-part-ii-app-memory-d4e652cf2c49)という記事の中で、次のような記述がありました。

> We had a 64x64 px (Small) ImageView, but our API was providing a 512x512 px(large) image. Decoding such large image for a small placeholder is inefficient in terms of memory usage and app performance.

> 私たちは64x64px（小）のImageViewを持っていましたが、APIは512x512px（大）の画像を提供していました。小さなプレースホルダーのためにこのような大きな画像をデコードすることは、メモリ使用量とアプリのパフォーマンスの点で非効率的です。

![スクリーンショット 2024-02-01 23.27.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906559/a4af3abf-5062-7de0-5da0-d1c8383c55a7.png)

ここで言われているのは、画像サイズの大きなものを、小さなサイズのImage View内に表示させようとするのはメモリ使用量やアプリパフォーマンスの観点から良くないということです。

この部分について疑問に思ったので、実際にその通りなのか実験してみることにしました。


# 環境
- 検証端末：Pixel 7a 
- Android バージョン：14
- Android Studio：Android Studio Hedgehog | 2023.1.1
- Kotlinのバージョン：1.5.3
- composeBom：2024.01.00

# 準備

以下の画像の64px * 64pxのものと、512px * 512pxのものを用意する。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906559/77857989-9cb6-5808-241e-9abda9b413ba.png" width=300>

64px * 64pxの方を`sample_img_64`と命名し、512px * 512pxの方を`sample_img_512`と命名する。

用意した二つのサイズの画像をpng形式にコンバートし、Android Studioのdrawable配下にインポートする。

![スクリーンショット 2024-02-01 22.23.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906559/56ef1ef3-74dd-0068-76a5-f2bd242f72bc.png)

# 実験

## 1. Android Viewの場合

### 1-1 小さいImageViewに大きい画像を表示する

#### 実装

記述したコードは次のとおりです。
64dp * 64dpのImageViewを配置し、その中で512px * 512pxの画像を表示しています。

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:theme="@style/Base.Theme.MyApplication"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/image"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:src="@drawable/sample_img_512"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

```
#### 結果

使用したメモリは **115.4MB** でした。

![スクリーンショット 2024-02-01 22.40.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906559/a24966b0-3fe1-abd7-3c83-df0f74263ffc.png)


### 1-2 同じ大きさのImage Viewに画像を配置する

#### 実装

記述したコードは次のとおりです。
64dp * 64dpの`ImageView`を配置し、その中で64px * 64pxの画像を表示しています。

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:theme="@style/Base.Theme.MyApplication"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/image"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:src="@drawable/sample_img_64"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

```

#### 結果
使用したメモリは **110.8MB** でした。

![スクリーンショット 2024-02-01 22.41.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906559/1841d381-3f36-a54d-e095-81f4d1ab786c.png)


## 2. Jetpack Composeの場合

### 2-1 小さいImage()に大きい画像を表示する

#### 実装
記述したコードは次のとおりです。
512px * 512pxのサイズの画像を64dp * 64dpのImage composableに表示するコードを書きました。

```kotlin
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Sample()
        }
    }
}

@Composable
fun Sample() {
    Image(
        modifier = Modifier.size(64.dp),
        painter = painterResource(id = R.drawable.sample_img_512),
        contentDescription = null
    )
}
```

#### 結果

使用したメモリは **117.5MB** でした

![スクリーンショット 2024-02-01 22.59.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906559/72ad7e19-8cb7-1bce-4e08-34129c227745.png)


### 2-2 同じ大きさのImage Viewに画像を配置する

#### 実装

記述したコードは次のとおりです。
64px * 64pxのサイズの画像を64dp * 64dpのImage composableに表示するコードを書きました。

```kotlin
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Sample()
        }
    }
}

@Composable
fun Sample() {
    Image(
        modifier = Modifier.size(64.dp),
        painter = painterResource(id = R.drawable.sample_img_64),
        contentDescription = null
    )
}
```

#### 結果

使用したメモリは **115.4MB** でした。

![スクリーンショット 2024-02-01 23.00.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906559/63cf79b0-66bc-76fd-be22-ef19bdb86642.png)


# 結果・考察
実験結果を次の表にまとめました。

|  | 大きいサイズの画像を表示 | 同じサイズの画像を表示 |
|:-:|:-:|:-:|
| Android View | 115.4 | 110.8 |
| Jetpack Compose | 117.5 | 115.4 |

小さなViewに大きな画像を表示させようとするのは、同じサイズのViewに画像を表示するよりメモリを多く使います。

この結果はJetpack Composeでも同様でした。

しかし差分が一桁なので誤差の可能性もあります。

もう少し多くの画像を小さなViewへデコードしようとすると結果が顕著になるかもしれませんが、この記事ではこれ以上調査はしません。

[追記]
本記事を公開して次のようなFBを頂きました。

https://twitter.com/NabeCott/status/1753179544167821723

https://twitter.com/matsuda323/status/1755730152745791834

元記事でもCoilを使っていた通り、画像を直接表示させるのと画像を扱うライブラリを通して表示させるのとではまたパフォーマンスも違ってくるようです。

次回の自由研究では、以下の内容にdeep diveしてみます。

- Coil・Glideについて調査する
    - CoilやGlideのメモリキャッシュ、ディスクキャッシュについても調査
- 本記事と同様な実験を行う