- Android Studio 4.0.1
- Kotlin 1.4.0
- Minimum SDK: API 23
AndroidアプリでSDカード上にファイルを書き込み、またそれを読みだす手順をまとめます。サンドボックスの中に作成する場合はもう少し簡単です。共有フォルダなどに保存して、パソコンなどからもアクセスできるようにすることが目的の方法です。
準備
Manifestへ権限追加
※権限の追加は不要なようです。
※権限の確認は実行時に行われます。
<!-- 必要ない -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
DocumentFileクラス取り込み
Intent.ACTION_OPEN_DOCUMENT_TREEでのアクセスで使用します。
Intent.ACTION_CREATE_DOCUMENTとIntent.ACTION_OPEN_DOCUMENTで一つづつアクセスする場合はいりいません。
dependencies {
implementation "androidx.documentfile:documentfile:1.0.1"
}
権限の許可を取得
ユーザーに権限の確認を求めます。
※この処理は、必要ないそうです。が、備忘のため、いちおう残しておきます。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
ActivityCompat.requestPermissions(this, arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE
), 1)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == 1) {
var allowed = true
for (result in grantResults) {
if (result == PackageManager.PERMISSION_DENIED) {
allowed = false
}
}
if (!allowed) {
finish()
}
}
}
}
ファイルを書き込む、読み込む
テキストファイルを出力
出力するフォルダ、ファイル名をユーザーに選択させます。
アクセス権限がないフォルダを選択した場合などは、失敗します。
fun openWriteFile() {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.setType("text/plain")
intent.putExtra(Intent.EXTRA_TITLE, "sample1.txt")
startActivityForResult(intent, 101);
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == 101 && resultCode == RESULT_OK) {
if (data?.getData() != null) {
val uri: Uri = data.getData() as Uri
val out = contentResolver.openOutputStream(uri)
val writer = OutputStreamWriter(out)
writer.write("サンプルテキスト\n2行目")
writer.close()
}
}
super.onActivityResult(requestCode, resultCode, data)
}
読み込むファイルをユーザーに選択させます。
fun openReadFile() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.setType("text/plain")
startActivityForResult(intent, 102);
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == 102 && resultCode == RESULT_OK) {
if (data?.getData() != null) {
val uri: Uri = data.getData() as Uri
val input = contentResolver.openInputStream(uri)
val reader = InputStreamReader(input)
Log.d("nozaki", reader.readText())
}
}
super.onActivityResult(requestCode, resultCode, data)
}
フォルダ単位でアクセスする
基準となるフォルダをユーザーに選択させます。
そこにサブフォルダを作り、サブフォルダにファイルを作成します。
fun openFolder() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(intent, 103);
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == 103 && resultCode == RESULT_OK) {
if (data?.getData() != null) {
// ファイルアクセスのためのオブジェクトを取得する
val treeUri: Uri = data.getData() as Uri
val document = DocumentFile.fromTreeUri(this, treeUri)
// 作成するフォルダが既にあれば、そのオブジェクトを取得、なければ作成
var dir = document?.findFile("SampleFolder")
if (dir == null) {
dir = document?.createDirectory("SampleFolder")
}
if (dir == null || !dir.isDirectory()) {
return
}
// フォルダにファイルを作成する
val file = dir.createFile("text/plain", "sample2.txt")
if (file != null) {
val out = contentResolver.openOutputStream(file.uri)
val writer = OutputStreamWriter(out)
writer.write("サブフォルダへのサンプルテキスト\n2行目")
writer.close()
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
ユーザーが選択したフォルダのファイル一覧を取得します。
fun openFolder() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(intent, 104);
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == 104 && resultCode == RESULT_OK) {
if (data?.getData() != null) {
// ファイルアクセスのためのオブジェクトを取得する
val treeUri: Uri = data.getData() as Uri
val document = DocumentFile.fromTreeUri(this, treeUri)
if (document == null) {
return
}
// フォルダのファイル一覧をログに出力する
for (file in document.listFiles()) {
Log.d("nozaki", file.name)
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
アクセス権限の保存
一度ユーザーにフォルダを選択させ、その権限を保存しておくと、次回からはユーザーの確認を待つことなくそのフォルダにアクセスできるようになります。
権限と対象フォルダのUriを保存
getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
val pref = getSharedPreferences("app", Context.MODE_PRIVATE)
pref.edit().putString("treeUri", treeUri.toString()).commit()
保存したフォルダからファイル一覧を取得
ユーザーへの確認は行われません
fun readFolder() {
val pref = getSharedPreferences("app", Context.MODE_PRIVATE)
val treeUri = Uri.parse(pref.getString("treeUri", null))
val document = DocumentFile.fromTreeUri(this, treeUri)
if (document == null) {
return
}
for (file in document.listFiles()) {
Log.d("nozaki", file.name)
}
}