티스토리 뷰
서론
일기 앱을 만들면서 Room DB에 저장된 일기 데이터를 백업하고 복구할 수 있는 기능을 구현해야 했다. 대부분의 일기 앱들은 텍스트 파일로 일기 데이터를 백업한다. 하지만 나는 글과 함께 일기에 업로드된 사진까지 백업하고 싶었기 때문에 Room DB 파일 자체를 로컬에 백업하는 방식으로 백업 기능을 구현해보고자 한다. 참고로 사용한 언어는 Java이다. SQLite 백업 & 복원 기능은 이 블로그를 참고해서 만들었다.
본론
Room DB나 SQLite로 데이터를 관리하고 있다면 우리가 백업해야 할 DB 파일은 다음의 위치에 보관되어 있다.
/data/com.example.패키지명/databases/DB이름_db
직접 파일 위치를 확인하고 싶다면 Android Studio 상단 메뉴에서 View > Tool Windows > Device File Explorer 를 열어 확인하면 된다.
백업 기능 구현하기
private static final String URL_APP_DB = "/data/com.example.main_패키지명/databases/DB파일_db";
private static final String URL_APP_SHM = "/data/com.example.main_패키지명/databases/DB파일_db-shm";
private static final String URL_APP_WAL = "/data/com.example.main_패키지명/databases/DB파일_db-wal";
private static final String URL_LOCAL_DB = "/Download/DB파일_db";
private static final String URL_LOCAL_SHM = "/Download/DB파일_db-shm";
private static final String URL_LOCAL_WAL = "/Download/DB파일_db-wal";
// 백업 파일 생성
private void createBackupFile() {
try {
backupEachFile(URL_APP_SHM, URL_LOCAL_SHM);
backupEachFile(URL_APP_WAL, URL_LOCAL_WAL);
backupEachFile(URL_APP_DB, URL_LOCAL_DB);
} catch (Exception e) {
Toast.makeText(getContext(), getContext().getString(R.string.backup_failed), Toast.LENGTH_LONG);
Log.e(TAG, "backup failed");
e.printStackTrace();
}
}
private void backupEachFile(String from, String to) throws IOException {
File sd = Environment.getExternalStorageDirectory();
File data = Environment.getDataDirectory();
if (sd.canWrite()) {
File currentDB = new File(data, from);
File backupDB = new File(sd, to);
FileChannel src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(backupDB).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
Toast.makeText(getContext(), getContext().getString(R.string.backup_success), Toast.LENGTH_LONG).show();
Log.i(TAG, from + "backup success");
cancel();
}
else {
Toast.makeText(getContext(), getContext().getString(R.string.backup_authorization_failed), Toast.LENGTH_LONG).show();
}
}
복구 기능 구현하기
private static final String URL_APP_DB = "/data/com.example.main_패키지명/databases/DB파일_db";
private static final String URL_APP_SHM = "/data/com.example.main_패키지명/databases/DB파일_db-shm";
private static final String URL_APP_WAL = "/data/com.example.main_패키지명/databases/DB파일_db-wal";
private static final String URL_LOCAL_DB = "/Download/DB파일_db";
private static final String URL_LOCAL_SHM = "/Download/DB파일_db-shm";
private static final String URL_LOCAL_WAL = "/Download/DB파일_db-wal";
// 백업 파일 불러오기
private void readBackupFile() {
try {
restoreEachFile(URL_LOCAL_SHM, URL_APP_SHM);
restoreEachFile(URL_LOCAL_WAL, URL_APP_WAL);
restoreEachFile(URL_LOCAL_DB, URL_APP_DB);
} catch (Exception e) {
Toast.makeText(getContext(), getContext().getString(R.string.restore_failed), Toast.LENGTH_LONG);
Log.e(TAG, "restore failed");
e.printStackTrace();
}
}
private void restoreEachFile(String from, String to) throws IOException {
File sd = Environment.getExternalStorageDirectory();
File data = Environment.getDataDirectory();
if (sd.canWrite()) {
File currentDB = new File(sd, from);
File restoreDB = new File(data, to);
FileChannel src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(restoreDB).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
Toast.makeText(getContext(), getContext().getString(R.string.restore_success), Toast.LENGTH_LONG).show();
Log.i(TAG, "restore success");
cancel();
}
else {
Toast.makeText(getContext(), getContext().getString(R.string.restore_authorization_failed), Toast.LENGTH_LONG).show();
}
}
로컬 백업 기능을 구현하기 위해서는 AndroidManifest.xml에 MANAGE_EXTERNAL_STORAGE 권한을 추가해야 한다. (Android 11 기준)
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
왜 3개의 DB 파일을 백업하는가?
갑작스러운 DB 데이터 손상, 유실을 방지하기 위해 .db 파일 뿐만 아니라 임시 파일 -shm과 -wal이 생성된다. SQLite DB에 update, delete와 같은 쓰기 요청이 들어오면 SQLite는 wal 파일에 일단 커밋들을 기록해두고, 나중에 한번에 DB 파일에 커밋한다. 그래서 만약 .db 파일만 백업한다면 wal 파일에 남은 커밋 기록들이 db 파일에 업데이트되지 않은 채 예전 db 파일을 백업하게 된다.
열심히 stack overflow를 뒤져봤지만 찾은 해결법은 -shm과 -wal 파일까지 백업하는 방법 밖에 없어서 필자는 3개의 db 파일 모두 로컬에 백업하는 방식으로 백업 기능을 구현했다. 혹시 더 쉬운 방법이 있다면 댓글로 알려주시면 감사함,, 😔
'ANDROID' 카테고리의 다른 글
[ANDROID] RecyclerView로 커스텀 캘린더뷰 제작하기 (Custom CalendarView) (0) | 2022.04.12 |
---|---|
Android View Binding(안드로이드 뷰바인딩) (0) | 2021.12.04 |
StartAcitivyForResult / OnActivityForResult deprecated -> registerForActivityResult (0) | 2021.12.04 |
- Total
- Today
- Yesterday
- 사용자ID
- cat
- baekjoon
- linuxawk
- 백준
- Baekjoon27211
- OnActivityForResult
- 리눅스cron
- Linux
- linuxgedit
- atq
- SELECT #SELECTFROM #WHERE #ORDERBY #GROUPBY #HAVING #EXISTS #NOTEXISTS #UNION #MINUS #INTERSECTION #SQL #SQLPLUS
- Baekjoon27219
- 백준27219
- 버추억박스오류
- virtualbox
- E_FAIL
- cron시스템
- linuxtouch
- 코테
- 백준27211
- linux파일
- 버추억박스에러
- api문서
- GitHubAPIforJava
- GithubAPI
- 쇼미더코드
- awk프로그램
- whatis
- 리눅스
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |