Hun's Blog

안드로이드 Room Database + Live Data 본문

Android

안드로이드 Room Database + Live Data

jhk-im 2020. 3. 25. 15:02

참고

https://developer.android.com/training/data-storage/room?hl=ko

 

Room을 사용하여 로컬 데이터베이스에 데이터 저장  |  Android 개발자  |  Android Developers

Room 라이브러리를 사용하여 더 쉽게 데이터를 유지하는 방법 알아보기

developer.android.com

https://www.youtube.com/watch?v=jit8j4gblIk&list=PLxTmPHxRH3VXHOBnaGQcbSGslbAjr8obc&index=3

 

Room 예제 실행화면

 

Room DataBase

SQLite에 대한 추상화 레이어를 제공하여 원활한 데이터베이스 액세스를 지원하는 동시에 SQLite를 완벽히 활용한다. 

 

3가지 구성요소

 

1) 데이터베이스 

:데이터베이스 홀더를 포함하며 앱의 지속적인 관계형 데이터에 대한 기본 연결을 위한 액세스 포인트 역할을 한다. 

-> @Database로 주석처리 되어있다. 

-> RoomDatabase를 상속받은 추상 클래스이다. 

-> 인수가 0개인 추상 메서드를 포함하고 @Dao로 주석 처리된 클래스를 반환한다. 

 

2) 항목

:데이터베이스 내의 테이블을 나타낸다.

 

3) DAO

: 데이터베이스에 액세스하는데 사용되는 메서드가 포함되어있는 인터페이스이다. 

 

 

 

app - build.gradle

1
2
3
4
5
    //Room
    implementation 'androidx.room:room-runtime:2.2.5'
    annotationProcessor 'androidx.room:room-compiler:2.2.5'
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata:2.2.0"
 

 

 

activity_room.xml

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<?xml version="1.0" encoding="utf-8"?>
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".room.RoomActivity">
 
    <EditText
        android:id="@+id/add_edit"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:hint="타이틀 입력"
        app:layout_constraintEnd_toStartOf="@id/add_button"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
 
    <Button
        android:id="@+id/add_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="추가"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
 
 
    <EditText
        android:id="@+id/update_id_edit"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="아이디 입력"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/update_title_edit"
        app:layout_constraintTop_toBottomOf="@id/add_edit"/>
    <EditText
        android:id="@+id/update_title_edit"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="타이틀 입력"
        app:layout_constraintEnd_toStartOf="@id/update_button"
        app:layout_constraintStart_toEndOf="@id/update_id_edit"
        app:layout_constraintTop_toBottomOf="@id/add_button" />
    <Button
        android:id="@+id/update_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="변경"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/add_button" />
 
    <EditText
        android:id="@+id/delete_edit"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:hint="아이디 입력"
        app:layout_constraintEnd_toStartOf="@id/delete_button"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/update_button" />
    <Button
        android:id="@+id/delete_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="삭제"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/update_button" />
 
    <Button
        android:id="@+id/clear_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="전체삭제"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/delete_button" />
 
    <TextView
        android:id="@+id/result_text"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        android:text="TextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/clear_button" />
 
 
 
 

 

 

DataModel -> 항목

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
32
33
34
35
36
37
38
39
40
41
/**
 * Room 에서 사용할 수 있는 Entity 가 되기위해서
 * @Entity Annotation 추가
 * */
@Entity
public class DataModel {
    /**
     * id = primaryKEY
     * autoGenerate -> 자동으로 증가
     * */
    @PrimaryKey(autoGenerate = true)
    private int id;
    private String title;
 
    // 생성자
    public DataModel(String title){
        this.title = title;
    }
 
    public void setTitle(String title) { this.title = title; }
 
    public String getTitle() { return title; }
 
    public int getId() { return id; }
 
    public void setId(int id) { this.id = id; }
 
    /**
     * toString 재정의 -> 내용확인하기 위함
     * */
    @NonNull
    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("DataModel{");
        sb.append("id=").append(id);
        sb.append(", title=").append(title).append('\'');
        sb.append('}');
        return sb.toString();
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

 

 

LocalDataBase -> 데이터베이스 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * Database 객체 -> abstract class , RoomDatabase 상속
 * Database annotation -> Entity 클래스와 버전을 명시
 **/
@Database(entities = {DataModel.class}, version = 1, exportSchema = false)
public abstract class LocalDatabase extends RoomDatabase {
 
    /**
     * 이 객체가 제공하는 Data Access Object
     * AppDatabase 가 생성되고 TodoDAO 를 통해서 조작한다.
     **/
    public abstract DataModelDAO dataModelDAO();
 
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

 

 

 

DataModelDAO -> DAO

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
 * Data Access Object -> Todo Entity 에 접근하기 위한 인터페이스
 * @Dao annotation 추가
 * Todo 에서 어떤 동작을 할 것인지 정의한다.
 * */
@Dao
public interface DataModelDAO {
 
    /**
     * Todo 라는 테이블에 여러 내용이 있을 것이다.
     * Todo 의 모든 내용을 List 에 담는 getAll() 이라는 메소드가 필요하다.
     * Query annotation -> 실제 sql query 를 사용할 수 있음
     * LiveData<객체> -> 해당 객체는 관찰 가능한 객체가 된다.
     **/
    @Query("SELECT * FROM DataModel")
    LiveData<List<DataModel>> getAll();
 
    /**
     * id로 데이터 찾기
     * */
    @Query("SELECT * FROM DataModel WHERE id = :mId")
    DataModel getData(int mId);
 
    /**
     * Insert annotation -> 내용추가
     **/
    @Insert
    void insert(DataModel dataModel);
 
    /**
     * Delete annotation -> 내용 삭제
     **/
    @Delete
    void delete(DataModel dataModel);
 
    /**
     * id로 데이터를 찾고 입력받은 String 으로 타이틀을 변경
     **/
    @Query("UPDATE DataModel SET title =:mTitle  WHERE id =:mId ")
    void dataUpdate(int mId, String mTitle);
 
 
    /**
     *  Clear All -> 리스트 전체삭제
    **/
    @Query("DELETE FROM DataModel")
    void clearAll();
    
}
 
 

getAll 메소드에서 LiveData가 활용된다.

LiveData가 List<DataModel> 을 감싸고 있다. 

액티비티에서 그 활용도를 알아볼 수 있다. 

 

 

RoomActivity

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
public class RoomActivity extends AppCompatActivity {
 
    //UI View
    private EditText mAddEdit, mUpdateIdEdit, mUpdateTitleEdit, mDeleteEdit;
    private TextView mResultTextView;
    private Button mAddButton, mUpdateButton, mDeleteButton, mClearButton;
 
    // Room Database
    private LocalDatabase db;
 
    private final String INSERT = "INSERT";
    private final String UPDATE = "UPDATE";
    private final String DELETE = "DELETE";
    private final String CLEAR = "CLEAR";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_room);
 
        mAddEdit = (EditText) findViewById(R.id.add_edit);
        mUpdateIdEdit = (EditText) findViewById(R.id.update_id_edit);
        mUpdateTitleEdit = (EditText) findViewById(R.id.update_title_edit);
        mDeleteEdit = (EditText) findViewById(R.id.delete_edit);
 
        mResultTextView = (TextView) findViewById(R.id.result_text);
        mAddButton = (Button) findViewById(R.id.add_button);
        mUpdateButton = (Button) findViewById(R.id.update_button);
        mDeleteButton = (Button) findViewById(R.id.delete_button);
        mClearButton = (Button) findViewById(R.id.clear_button);
 
        /**
         * LocalDatabase 객체 생성 -> db
         * databaseBuilder(context, Database 객체, Database 명)
         * Database 명의 파일이 실제로 생성 된다.
         **/
        db = Room.databaseBuilder(this, LocalDatabase.class"test-db")
                .build();
 
        /**
         * Database 를 관찰하고 변경이 감지될 때 UI 갱신
         * 데이터베이스 db -> 데이터베이스 mDataModelDAO()
         * -> getAll() 가져오는 List<DataModel> 객체는 관찰 가능한 객체 이므로
         * -> observe 메소드로 관찰하고 변경이 되면 dataList 에 추가한다.
         * 변경된 내용이 담긴 dataList 를 출력한다.
         **/
        /*
        * 람다식 사용
        * file -> project structure -> modules -> source compatibility, target compatibility -> 1.8
        **/
        db.dataModelDAO().getAll().observe(this, dataList -> {
            mResultTextView.setText(dataList.toString());
        });
 
        /**
         * Insert
         * 데이터배이스 객체 . 데이터베이스 DAO . insert -> new DataModel (텍스트 추가)
         **/
        mAddButton.setOnClickListener(v -> {
            /**
             *  AsyncTask 생성자에 execute 로 DataModelDAO 객체를 던저준다.
             *  비동기 처리
             **/
            new DaoAsyncTask(db.dataModelDAO(),INSERT,0,"")
                    .execute(new DataModel(mAddEdit.getText().toString()));
        });
 
        /**
         * Update
         * 데이터베이스 -> getData(id) -> id 입력하여 DataModel 받아오기
         * -> update(DataModel) 해당 데이터 업데이트
         **/
        mUpdateButton.setOnClickListener(v ->
                        new DaoAsyncTask(
                                db.dataModelDAO(),
                                UPDATE,
                                Integer.parseInt(mUpdateIdEdit.getText().toString()),
                                mUpdateTitleEdit.getText().toString()
                        ).execute()
        );
 
        /**
         * Delete
         * 데이터베이스 -> getData(id) -> id 입력하여 DataModel 받아오기
         * -> delete(DataModel) 해당 데이터 삭제
         * */
        mDeleteButton.setOnClickListener(v ->
                new DaoAsyncTask(
                        db.dataModelDAO(),
                        DELETE,
                        Integer.parseInt(mDeleteEdit.getText().toString()),
                        ""
                ).execute()
        );
 
 
        /**
         * Clear
         * 데이터베이스 -> allClear -> 리스트 전부 지움
         * */
        mClearButton.setOnClickListener(v ->
                new DaoAsyncTask(db.dataModelDAO(),CLEAR,0,"").execute()
        );
 
 
    }
 
    /**
     * 비동기 처리 해주는 것은 dataModelDAO() 이다.
     * InsertAsyncTask 생성자를 만들어 dataModelDAO() 객체를 받는다.
     **/
    private static class DaoAsyncTask extends AsyncTask<DataModel,Void,Void>{
        
 
        private DataModelDAO mDataModelDAO;
        private String mType;
        private int mId;
        private String mTitle;
 
 
        private DaoAsyncTask (DataModelDAO dataModelDAO, String type, int id, String title) {
            this.mDataModelDAO = dataModelDAO;
            this.mType = type;
            this.mId = id;
            this.mTitle = title;
        }
 
        @Override
        protected Void doInBackground(DataModel... dataModels) {
 
            if(mType.equals("INSERT")){
                mDataModelDAO.insert(dataModels[0]);
            }
            else if(mType.equals("UPDATE")){
                if(mDataModelDAO.getData(mId) != null){
                    mDataModelDAO.dataUpdate(mId,mTitle);
                }
            }
            else if(mType.equals("DELETE")){
                if(mDataModelDAO.getData(mId) != null) {
                    mDataModelDAO.delete(mDataModelDAO.getData(mId));
                }
            }
            else if(mType.equals("CLEAR")){
                mDataModelDAO.clearAll();
            }
            return null;
        }
 
    }
 
}
 
 

51번 줄에서 DAO의 getAll() 메소드가 호출되었다.

getAll() 메소드에서 반환하는 DataModel 리스트는 LiveData 객체가 감싸고있다. 

즉, LiveData의 메소드를 활용할 수 있게 되는 것이고 observe 메소드로 DataModel의 변화를 감지할 수 있게 되었다.

*변화가 감지되면 데이터를 갱신한다* 를 구현할 수 있게 되는 것이다.