- 아키텍처 구조
- 구글 코드랩 예제 :
https://codelabs.developers.google.com/codelabs/android-room-with-a-view/index.html?index=..%2F..index#0
1. 기본 구성
- dependencies 구성
def room_version = "2.1.0-alpha04"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
def lifecycle_version = "2.0.0"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
> 라이프사이클
https://developer.android.com/jetpack/androidx/releases/lifecycle?hl=ko#declaring_dependencies
> Room
https://developer.android.com/jetpack/androidx/releases/room?hl=ko
- Entity
@Entity(tableName = "user_table")
public class User {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
private int id;
@NonNull
@ColumnInfo(name = "userName")
private String userName;
@NonNull
@ColumnInfo(name = "age")
private int age;
}
> autoGenerate를 적용하면 insert 과정에서 id 값이 자동으로 증가한다.
- Dao
@Dao
public interface UserDao {
@Insert
long insert(User user);
@Update
int update(User user);
@Query("DELETE FROM user_table")
int deleteAll();
@Query("DELETE FROM user_table WHERE id = :id")
int deleteUser(int id);
@Query("SELECT * from user_table ORDER BY userName ASC")
LiveData<List<User>> getAllUsers();
}
> LiveData를 사용하지 않을 경우
@Query("SELECT * from user_table ORDER BY userName ASC")
List<User> getAllUsers();
- Database
@Database(entities = {User.class}, version = 1)
public abstract class UserRoomDatabase extends RoomDatabase {
public abstract UserDao userDao();
private static volatile UserRoomDatabase INSTANCE;
static UserRoomDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (UserRoomDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), UserRoomDatabase.class, "user_database").build();
}
}
}
return INSTANCE;
}
}
- 설명
androidx.room:room-compiler가 generatedJava 디렉토리의 소스코드를 자동 생성한다.
SQLiteOpenHelper를 사용했을 때 보다 더 생산성이 좋다.
링크 :
2. 안드로이드 앱 아키텍처 적용
Room 아키텍처는 안드로이드 앱 아키텍처의 DB 연동 구성요소이다.
- Repository
class UserRepository {
private static final String TAG = UserRepository.class.getSimpleName();
private final UserDao userDao;
private final LiveData<List<User>> allUsers;
UserRepository(Application application) {
UserRoomDatabase db = UserRoomDatabase.getDatabase(application);
userDao = db.userDao();
allUsers = userDao.getAllUsers();
}
public void insert(User user) {
new AsyncTask<User, Void, Long>() {
@Override
protected Long doInBackground(User... users) {
if (userDao == null)
return -1L;
return userDao.insert(users[0]);
}
@Override
protected void onPostExecute(Long aLong) {
super.onPostExecute(aLong);
Log.d(TAG, "insert : " + aLong);
}
}.execute(user);
}
public void update(User user) {
new AsyncTask<User, Void, Integer>() {
@Override
protected Integer doInBackground(User... users) {
if (userDao == null)
return -1;
return userDao.update(users[0]);
}
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
Log.d(TAG, "update : " + integer);
}
}.execute(user);
}
public void deleteAll() {
new AsyncTask<Void, Void, Integer>() {
@Override
protected Integer doInBackground(Void... voids) {
if (userDao == null)
return -1;
return userDao.deleteAll();
}
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
Log.d(TAG, "deleteAll : " + integer);
}
}.execute();
}
public void deleteUser(int id) {
new AsyncTask<Integer, Void, Integer>() {
@Override
protected Integer doInBackground(Integer... integers) {
if (userDao == null)
return -1;
return userDao.deleteUser(integers[0]);
}
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
Log.d(TAG, "deleteUser : " + integer);
}
}.execute(id);
}
public LiveData<List<User>> getAllUsers() {
return allUsers;
}
}
> 메인쓰레드에서 DB 연동을 사용하지 않아야 한다.
> LiveData를 사용하지 않는 곳은 AsyncTask를 적용한다.
- AndroidViewModel
public class UserViewModel extends AndroidViewModel {
private static final String TAG = UserViewModel.class.getSimpleName();
private final UserRepository repository;
private final LiveData<List<User>> allUsers;
public UserViewModel(Application application) {
super(application);
repository = new UserRepository(application);
allUsers = repository.getAllUsers();
}
public void insert(User user) {
repository.insert(user);
}
public void update(User user) {
repository.update(user);
}
public void deleteAll() {
repository.deleteAll();
}
public void deleteUser(int id) {
repository.deleteUser(id);
}
public LiveData<List<User>> getAllUsers() {
return allUsers;
}
}
> AndroidViewModel를 상속하고 대부분의 메소드들은 델리게이트로 구성한다.
- ViewModel 구현
userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
userViewModel.getAllUsers().observe(this, new Observer<List<User>>() {
@Override
public void onChanged(List<User> users) {
}
});
> 라이프사이클을 이용해서 ViewModel을 통해 DB 연동을 한다.
> 라이프 사이클 :
https://developer.android.com/jetpack/androidx/releases/lifecycle?hl=ko#declaring_dependencies
3. 샘플코드
소스코드
APK
- Room의 구성을 더 강력하게 만드는 구성은 Room + ViewModel + Paging 조합이다.
추가적으로 Data Binding을 함께 구성하기도 하지만 반드시 필요한 경우만 사용해하 한다.
- Room을 사용하는 주요 목적은 안드로이드 앱 아키텍처 구조 적용과 SQLiteOpenHelper의 불편함을 줄이기 위함이다.
- 안드로이드와 SQLite의 관계를 이해하고 Room 아키텍처를 사용하도록 하자.