【Android】異なるActivityにbitmap画像を渡す方法（FAILED BINDER TRANSACTION !!!  (parcel size = 00000000 )が起きたときの対処法）

# 概要

Androidで異なるActivityにbitmapを渡す際、bitmapのサイズが大きすぎてタイトルのようなエラーとなってしまったので、その原因と対処法を示す。

# 原因

Androidで画像（bitmap）をActivity間で受け渡しする際、
以下のように行う。


```kotlin:SubActivity.kt

// MainActivityを起動するための、intentを初期化する
val intent: Intent = Intent(context, MainActivity::class.java)

// intentにbitmapをセットする
intent.putExtra("bitmap", bitmap)

// MainActivityを起動する
startActivity(intent)
```

このとき、putExtraでセットするbitmapのサイズが大きすぎる場合、以下のような例外が発生する。

```bash:
E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!  (parcel size = 12583424)
```

`12583424` はこのときセットしたbitmapのサイズ

# 対処法

bitmapは容量が大きいので、異なるActivityに画像データを渡したい場合、
bitmapを直接渡すのではなく、Uriに変換して渡し、
受け取る側のActivityでは、必要に応じてUriをbitmapに変換して利用する。

以下に実装例を示す。

```kotlin:SubActivity.kt
/**
 * 渡す側のActivity
 */

// MainActivityを起動するための、intentを初期化する
val intent: Intent = Intent(context, MainActivity::class.java)

// bitmapをuriに変換する
val uri: Uri = bitmapToUri(bitmap)

// intentにuriをセットする
intent.putExtra("uri", uri)

// MainActivityを起動する
startActivity(intent)


/**
 * bitmapをuriに変換する
 * 
 * @param bitmap ビットマップ形式のデータ
 * @return bitmapから取得したcontent://スキーマ形式のuri
 * /
private fun bitmapToUri(bitmap: Bitmap): Uri {

    // 一時ファイル作成用のキャッシュディレクトリを定義する
    val cacheDir: File = this.cacheDir

    // 現在日時からファイル名を生成する
    val fileName: String = System.currentTimeMillis().toString() + ".jpg"

    // 空のファイルを生成する
    val file = File(cacheDir, fileName)

    // ファイルにバイトデータを書き込み開始する
    val fileOutputStream: FileOutputStream? = FileOutputStream(file)

    // ファイルにbitmapを書き込む
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream)

    // ファイルにバイトデータを書き込み終了する
    fileOutputStream?.close()

    // ファイルからcontent://スキーマ形式のuriを取得する
    val contentSchemaUri: Uri = FileProvider.getUriForFile(this@SubActivity, "com.hoge.fuga.fileprovider.fileprovider", file)
    return contentSchemaUri
}
```

bitmapをuriに変換するのは、上記のように結構めんどくさい。
一度fileにしてからbitmapを流し込んでUriにする必要がある。

Uriは content://スキーマ形式のuri と file://スキーマ形式のuri がある。それぞれ精製方法とできることが異なるので注意が必要。

また、 `FileProvider.getUriForFile()` は、 `AndroidManifest.xml` に `androidx.core.content.FileProvider"` を定義して、 `res/xml/provider_paths.xml` を作成する必要がある。※1

```kotlin:MainActivity.kt
/**
 * 受け取る側のActivity
 */

// intentからuriを取得する
val uri = intent.getExtras().get("uri") as Uri?

// uriをnullチェックする
uri?.let { nonNullUri: Uri ->

    // uriをbitmapに変換する
    val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, nonNullUri) as? Bitmap
}
```

※1

```xml:AndroidManifest.xml
    <application>

        // 省略

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.hoge.fuga.fileprovider"
            android:enabled="true"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>

    </application>
```


```xml:res/xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <cache-path name="cache" path="." />
</paths>
```

これで異なるActivityに画像を渡すことができる。