버전에 따라 다른 사진 저장 방식
문제 상황
생성된 조언을 갤러리에 저장하는 과정에서, 안드로이드 버전에 따라 다르게 구현해야 하는 문제에 직면했다.
에뮬레이터는 Q 버전
이 후를 사용했기 때문에 MediaStore
를 이용하여 개발을 하였다. 하지만, 테스트용 기기는 Q 버전
이전이라 실제로 저장이 안되는 문제가 발생하였다.
해결 방법
- *Q버전 이전과 이후를 분리한다.
- 실행되는 기기의 버전을 확인하는 방법은
Build.VERSION.SDK_INT
로 확인이 가능하다. - if 문을 통해 버전을 체크한다.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
- 실행되는 기기의 버전을 확인하는 방법은
- 파일 이름
- 타임 스탬프를 사용하여 중복을 방지한다.
- 파일 식별자에서는 이름이 곧 고유 식별자이므로, 이름이 동일하게 되면 덮어쓰여지게 된다.
${System.currentTimeMillis()}.jpg
- 타임 스탬프를 사용하여 중복을 방지한다.
- Q버전 이전 -
File 사용
- 갤러리 디랙토리 경로 알아내기
Environment.getExternalStoragePublicDirectory()
- 인자로
Environment.DIRECTORY_PICTURES
를 사용하여 사진을 저장하는 경로를 알아낸다.
- 인자로
- 파일 객체 생성
File(imagesDir, filename)
imagesDir
에filename
의File
객체를 생성한다.
fileOutputStream()
- 데이터 쓰기
- 미디어 스캐닝
- 저장된 이미지를 미디어 라이브러리에서 확인하기 위해 스캔하는 작업을 수행한다.
- 갤러리 디랙토리 경로 알아내기
- Q버전 이후 -
MediaStore 사용
contentsValue
생성- 이미지 이름
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
MIME
타입 설정put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
- 경로 설정
put(MediaStore.MediaColumns.PATH)
- 이미지 이름
- 이미지 저장
resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
- 이미지 저장의 결과로
Uri
를 반환한다.
- 이미지 저장의 결과로
fileOutputStream()
- 데이터 쓰기
- 미디어 스캐닝
전체 코드
class ImageSaveRepository @Inject constructor(private val context : Context ){
fun saveImageToStorage(bitmap: Bitmap) : Flow<Status<String>> = flow{
emit(Status.Loading())
try {
val filename = "${System.currentTimeMillis()}.jpg"
var fos: OutputStream? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
context.contentResolver?.also { resolver ->
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
}
val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
fos = imageUri?.let { resolver.openOutputStream(it) }
// 미디어 스캐닝 실행
MediaScannerConnection.scanFile(
context,
arrayOf(imageUri.toString()),
null
) { _, _ -> }
}
} else {
val imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
val image = File(imagesDir, filename)
fos = FileOutputStream(image)
// 미디어 스캐닝 실행
MediaScannerConnection.scanFile(
context,
arrayOf(image.absolutePath),
null
) { _, _ -> }
}
fos?.use {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
emit(Status.Success(null))
}
fos?.close()
}
catch (e: Exception) {
emit(Status.Error(message = e.localizedMessage ?: ""))
}
}
}
'안드로이드 > KOTLIN' 카테고리의 다른 글
[Kotlin/Android] SharedPreferences Delegate (1) | 2024.02.20 |
---|---|
[Fragment] Bundle에 객체를 전달하기 (0) | 2023.03.23 |
[Parcelize] intent에 Data class를 넣어서 전달하기 (0) | 2023.03.06 |
Retrofit @Get, @Query 인코딩 문제 해결방법 (0) | 2023.02.01 |
[Jitpack] 사용하기 (0) | 2023.01.17 |