8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Android】SDカードに保存する

Last updated at Posted at 2019-07-08

この記事は

AndroidでSDカードに何かを保存したい

使うものは

使わないものは

  • getExternalDirectoryとか

まずコード

SDカードへのアクセス権限をユーザに求める
    private fun requestSecondaryStorageAccess() {
        val sm = getSystemService(STORAGE_SERVICE) as StorageManager
        sm.storageVolumes.find { storageVolume -> storageVolume.isRemovable }.let {
            val intent = it.createAccessIntent(null)
            startActivityForResult(intent, REQUEST_SDCARD_ACCESS)
        }
    }
権限付与されたURIをごにょごにょする
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (requestCode == REQUEST_SDCARD_ACCESS && resultCode == RESULT_OK) {
            data?.data?.let {
                contentResolver.takePersistableUriPermission(
                    it,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                )

                val outUri = DocumentFile.fromTreeUri(this, it)?.createFile("text/plain", "sample")?.uri
                outUri?.let{
                    contentResolver.openOutputStream(it)?.use{
                        it.write("hoge".toByteArray())
                    }
                }
            }
        }

細かく

    val sm = getSystemService(STORAGE_SERVICE) as StorageManager
    sm.storageVolumes.find { storageVolume -> storageVolume.isRemovable }.let {
        val intent = it.createAccessIntent(null)
        startActivityForResult(intent, REQUEST_SDCARD_ACCESS)
    }
  • StorageManagerのgetStorageVolumes()で、利用可能な全てのStorageVolumeを取得し、isRemovable()で取り外し可能であることをチェックする(=SDカードである)
  • SDカードであるStorageVolumeに対してcreateAccessIntent(null)でディレクトリアクセス権を要求するIntentを生成し、発行する
    data?.data?.let {
        contentResolver.takePersistableUriPermission(
            it,
            Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
        )
  • ユーザがアクセス権限を与えたディレクトリのUriがdataに格納される
  • このUriはそのままだと揮発するが、ContentResolverのtakePersistableUriPermissionでシステムにUriを覚えさせておくことが可能
    • 端末を再起動するまで有効。一度覚えると再度ユーザに権限許可を求めずRESULT_OKが返される。
    val outUri = DocumentFile.fromTreeUri(this, it)?.createFile("text/plain", "sample")?.uri
    outUri?.let{
        contentResolver.openOutputStream(it)?.use{
            it.write("hoge".toByteArray())
        }
    }
  • DocumentFileのfromTreeUriで「アクセス許可されたUri」からDocumentFileオブジェクトを生成
    • この場合、SDカードのルートディレクトリを指す
  • DocumentFileのcreateFileでmimeTypeとfilenameを指定してDocumentFileを生成し、Uriを取得
  • 新ファイルのoutputStreamを生成し、hogeと書き込んだ

Android Q

createAccessIntentがDeprecatedとなった。余計なことしやがって!

ディレクトリピッカーを開いてユーザに権限を付けてもらう
    val intent = volume.createOpenDocumentTreeIntent()
    startActivityForResult(intent, REQUEST_SDCARD_ACCESS)
  • SDカードの権限を付けますよ~という方法が封じられ、ユーザがディレクトリを指定することが必要になった。
  • 因みにAndroidQでは見送られたが、来年(=2020?)にはSAFに完全移行しないとファイル周りは使えなくなるらしい。どこかに書いてあったけどソースは忘れました。ごめんなさい。
8
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?