Device에 App이 설치되면 App마다 사용할 수 있는 저장공간이 있습니다. 각 앱이 사용할 수 있는 directory마다 차이점이 있고, 자주 헷갈리는 부분이라서 한번 정리해보았습니다.
이 글은 안드로이드 Q가 공개되기 전에 쓰여졌습니다.
안드로이드 Q에서 외부 저장소의 새로운 정책인 Scoped Storage가 소개되었습니다. 그렇기 때문에 이 글의 내용은 안드로이드 P 이하의 디바이스에만 해당됩니다. Scoped storage에 대한 자세한 내용은 안드로이드 Q의 새로운 저장소 정책, Scoped Storage 알아보기를 참고해주세요.
Code(Apk) 위치
어떤 App이 설치되면 device 내부의 특정 dir에 apk, libs, oat 파일 등이 저장됩니다. Oreo부터 폴더명은 package의 이름에 random hashcode의 조합으로 생성됩니다.
- /data/app/[package name]-[hashcode]/ 으로 폴더 생성됨
$ /data/app/com.codechacha.storages-v0U9I2KC_tE0MS5CN8NoTw== # ls
base.apk oat lib
Internal storage (내부 저장소)
내부 저장소도 특정 dir에 file, cache 등의 공간이 생성됩니다. cache의 경우 Device 내부에 저장공간이 부족한 경우 동의를 구하지 않고 삭제할 수 있는 폴더입니다. 삭제되서는 안되는 파일이라면 cache 폴더에 저장하면 안됩니다.
어떤 App의 Internal storage는 그 앱 스스로만 사용이 가능하며 다른 앱은 접근할 수 없습니다. 또한, file을 write/read 하는데 어떤 Permission도 필요하지 않습니다.
- /data/user/[User number]/[package name]/ 으로 폴더 생성됨
adb shell로 확인해보면 여러 폴더들이 있습니다.
$ /data/user/0/com.codechacha.storages # ls
cache code_cache files
App이 이런 path들은 쉽게 알 수 있도록 Context 객체에서 여러 method를 제공해줍니다.
// Internal storage
Log.d(TAG, "Internal private data dir: "
+ getDataDir().getAbsolutePath());
Log.d(TAG, "Internal private file dir: "
+ getFilesDir().getAbsolutePath());
Log.d(TAG, "Internal private cache dir: "
+ getCacheDir().getAbsolutePath());
03-10 22:25:18.979 6931 6931 D MainActivity: Internal private data dir: /data/user/0/com.codechacha.storages
03-10 22:25:18.979 6931 6931 D MainActivity: Internal private file dir: /data/user/0/com.codechacha.storages/files
03-10 22:25:18.979 6931 6931 D MainActivity: Internal private cache dir: /data/user/0/com.codechacha.storages/cache
External storage (외부 저장소)
외부 저장소도 내부 저장소와 비슷합니다. 하지만 외부저장소는 write/read 시 permission이 필요합니다. 그리고 모든 앱에서 접근이 가능하기 때문에 private을 보장하지 않습니다. 또한, PC에서 이 Storage에 접근이 가능하기 때문에 일시적으로 write/read가 불가능할 수 있습니다.
App 전용 폴더
App 전용으로 생성되는 storage가 있습니다. External이기 때문에 다른 앱에서도 접근이 가능하지만, 중요한 것은 이 App이 삭제된다면 이 공간도 함께 삭제된다는 점입니다. 따라서, 다른 App에서 공유되길 원하는 data는 이 곳에 저장되어서는 안됩니다.
- /storage/emulated/0/Android/data/[package name]/ 으로 폴더 생성됨
Java로 path구하는 코드와 실행 Log입니다.
// External app's storage
Log.d(TAG, "External app's cache dir: "
+ getExternalCacheDir().getAbsolutePath());
Log.d(TAG, "External app's file dir: "
+ getExternalFilesDir(Environment.DIRECTORY_PICTURES));
03-10 22:52:49.350 7302 7302 D MainActivity: External app's cache dir: /storage/emulated/0/Android/data/com.codechacha.storages/cache
03-10 22:52:49.350 7302 7302 D MainActivity: External app's file dir: /storage/emulated/0/Android/data/com.codechacha.storages/files/Pictures
공용 폴더
공용 폴더는 앱들이 삭제된다고 해도 저장된 data가 삭제되지 않습니다.
- /storage/emulated/0/[Content 종류]
Java로 path구하는 코드와 실행 Log입니다. getExternalStoragePublicDirectory()
는 argument로 Environment.DIRECTORY_PICTURES
처럼 Content 종류를 넘겨줍니다.
// External public storage
Log.d(TAG, "External public root dir: "
+ Environment.getExternalStorageDirectory());
Log.d(TAG, "External public file dir: "
+ Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES));
03-10 22:52:49.351 7302 7302 D MainActivity: External public root dir: /storage/emulated/0
03-10 22:52:49.352 7302 7302 D MainActivity: External public file dir: /storage/emulated/0/Pictures
Permissions
read/write를 하려면 AndroidManifest.xml에 다음 권한을 선언해야 합니다. Runtime permission이기 때문에 사용자의 허락을 받아야 합니다.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
External storage 사용 가능 확인
PC에서 mount하는 경우 write/read가 안될 수 있기 때문에 사용하기 전에 사용가능한지 확인이 필요합니다.
/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
정리
지금까지 안드로이드의 내부/외부 저장소에 대해서 알아보았습니다.
이 글은 안드로이드 Q가 공개되기 전에 쓰여졌습니다. 안드로이드 Q에서 외부 저장소의 새로운 정책인 Scoped Storage가 소개되었습니다. 그렇기 때문에 이 글의 내용은 안드로이드 P 이하의 디바이스에만 해당됩니다.
Scoped storage에 대한 자세한 내용은 안드로이드 Q의 새로운 저장소 정책, Scoped Storage 알아보기를 참고해주세요.
참고
Related Posts
- Android 14 - 사진/동영상 파일, 일부 접근 권한 소개
- Android - adb push, pull로 파일 복사, 다운로드
- Android 14 - 암시적 인텐트 변경사항 및 문제 해결
- Jetpack Compose - Row와 Column
- Android 13, AOSP 오픈소스 다운로드 및 빌드
- Android 13 - 세분화된 미디어 파일 권한
- Android 13에서 Notification 권한 요청, 알림 띄우기
- Android 13에서 'Access blocked: ComponentInfo' 에러 해결
- 에러 해결: android gradle plugin requires java 11 to run. you are currently using java 1.8.
- 안드로이드 - 코루틴과 Retrofit으로 비동기 통신 예제
- 안드로이드 - 코루틴으로 URL 이미지 불러오기
- Android - 진동, Vibrator, VibrationEffect 예제
- Some problems were found with the configuration of task 에러 수정
- Query method parameters should either be a type that can be converted into a database column or a List
- 우분투에서 Android 12 오픈소스 다운로드 및 빌드
- Android - ViewModel을 생성하는 방법
- Android - Transformations.map(), switchMap() 차이점
- Android - Transformations.distinctUntilChanged() 소개
- Android - TabLayout 구현 방법 (+ ViewPager2)
- Android - 휴대폰 전화번호 가져오는 방법
- Android 12 - Splash Screens 알아보기
- Android 12 - Incremental Install (Play as you Download) 소개
- Android - adb 명령어로 bugreport 로그 파일 추출
- Android - adb 명령어로 App 데이터 삭제
- Android - adb 명령어로 앱 비활성화, 활성화
- Android - adb 명령어로 특정 패키지의 PID 찾기
- Android - adb 명령어로 퍼미션 Grant 또는 Revoke
- Android - adb 명령어로 apk 설치, 삭제
- Android - adb 명령어로 특정 패키지의 프로세스 종료
- Android - adb 명령어로 screen capture 저장
- Android - adb 명령어로 System 앱 삭제, 설치
- Android - adb 명령어로 settings value 확인, 변경
- Android 12 - IntentFilter의 exported 명시적 선언
- Android - adb 명령어로 공장초기화(Factory reset)
- Android - adb logcat 명령어로 로그 출력