안드로이드 앱에서 파일을 다룰 때는 대부분 java.io 패키지에서 제공하는 클래스를 이용한다.
// 파일 객체 생성 후 데이터 쓰기
val file = File(filesDir, "test.txt")
val writeStream: OutputStreamWriter = file.writer()
writeStream.write("hello world")
writeStream.flush()
// 파일의 데이터 읽기
val readStream: BufferedReader = file.reader().buffered()
readStream.forEachLine {
Log.d(TAG, "$it")
}
// Context 객체로 데이터를 쓰고 읽기
openFileOutput("test2.txt", Context.MODE_PRIVATE).use {
it.write("hello world!!".toByteArray())
}
openFileInput("test2.txt").bufferedReader().forEachLine {
Log.d(TAG, "$it")
}
저장 데이터는 Device Manager -> data -> data -> 패키지명 -> files 에서 확인 가능
내부 저장소의 파티션을 나누어 외장 메모리로 제공할 수 있다.
// 외장 메모리 사용 가능 여부 판단
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
Log.d(TAG, "MOUNTED")
} else {
Log.d(TAG, "UNMOUNTED")
}
안드로이드 버전과 API 호환성을 위해 매니페스트에 다음처럼 선언하는 것이 좋다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
...
android:requestLegacyExternalStorage="true">
...
</application>
</manifest>
외장 메모리 공간은 앱별 저장소와 공용 저장소로 구분된다.
앱별저장소는 개별 앱에 할당된 공간으로 해당 앱에서만 접근할 수 있다.
앱별 저장소의 파일을 외부 앱에서 접그낳게 하려면 파일 프로바이더로 공개해야 한다.
// 앱별 저장소에 파일 쓰고 읽기
val file: File = File(getExternalFilesDir(null), "test.txt")
val writeStream: OutputStreamWriter = file.writer()
writeStream.write("hi")
writeStream.flush()
val readStream: BufferedReader = file.reader().buffered()
readStream.forEachLine {
Log.d(TAG, "$it")
}
Device Manager -> storage -> emulated -> 0 -> Android -> data -> 패키지명 -> files 안에 있음
앱에서 만든 파일을 모든 앱이 이용할 수 있게 하고 싶을 때도 있다. 대표적인 사례가 카메라 앱이다.
카메라 앱은 파일을 앱별 저장소가 아닌 공용 저장소에 만든다.
공용 저장소는 모든 앱을 위한 공간이므로 파일을 만든 앱을 삭제해도 파일은 삭제되지 않는다.
공용 저장소는 파일 경로로 직접 접근하지 않고 시스템이 제공하는 API를 이용한다.
// 공용 저장소에 접근
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME
)
val cursor = contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
null
)
cursor?.let {
while (cursor.moveToNext()) {
Log.d(TAG, "_id : ${cursor.getLong(0)}, name : ${cursor.getString(1)}")
}
}
// 이미지 파일의 Uri값 가져오기
val contentUri: Uri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
cursor.getLong(0)
)
// 이미지 데이터 가져오기
val resolver = applicationContext.contentResolver
resolver.openInputStream(contentUri).use { stream ->
// stream 객체에서 작업 수행
val option = BitmapFactory.Options()
option.inSampleSize = 10
val bitmap = BitmapFactory.decodeStream(stream, null, option)
binding.resultImageView.setImageBitmap(bitmap)
}
contentResolver.query() 함수의 첫 번째 매개변수에 Uri값을 MediaStore.Images를 사용하였다.
이는 안드로이드 이미지 파일의 공용 저장소인 DCIM과 Pictures 디렉터리를 가리킨다.