평범한 개발자 행복한 가족, 패밀리그램

개발/안드로이드

Android MVVM 패턴을 위한 Architecture Components library - LiveData #2

패밀리그램 2017. 11. 20. 16:01

https://developer.android.com/topic/libraries/architecture/adding-components.html



아키텍처 콤포넌트 소개 영상


LiveData - LiveData는 옵저버 패턴의 Data holder 이다. Data가 변경될 때 Observer를 통해 감지 할 수 있으며, 이를 통해 UI를 업데이트 할 수 있다.

이전 포스팅인 Room Database에 LiveData를 사용 할 수 있다. 간단하게 LiveData를 살펴본 뒤 느낀 부분은 LiveData 는 Reactive + Object 라는 느낌이었다. Reactive X의 Subject와 같은 Observable 를 정의된 Object ( LiveData<T> ) 가 기본적으로 갖고 있으며 , LiveData의 API를 통해 Object의 값을 변경하고, 변경 되었을 때 LiveData에 등록된 Observer 를 통하여 값의 변경을 감지 할 수 있다. 


이전 포스팅의 RoomDatabase를 구현한 MemoDatabase를 보면 LiveData를 RxJava의 Completable 처럼 사용 한 것을 확인 할 수 있다. 


Android MVVM 패턴을 위한 Architecture Components library - ROOM #1


private final MutableLiveData<Boolean> mIsDatabaseCreated = new MutableLiveData<>();


위와 비슷한 예제가 영상에 다이어그램으로 표시 되는데 간단한 개념은 아래 와 같다. 



LiveData는 데이터가 변경되면 알려준다


기존 UI에는 Thursday 라는 요일이 표시되어 있고, Data에서는 Friday가 준비되어 있다.



Friday가 Mutable LiveData에 저장되면 UI에서 등록시킨 Observer가 데이터 변화를 감지하고 Notification을 받는다.



위 다이어그램 코드로 표현하면 아래와 같다


1
2
3
4
5
6
7
String format = "%s is a good day for a hike.";
message.setText(String.format(format"Thursday"));
MutableLiveData<String> dayOfWeek = new MutableLiveData<>();
dayOfWeek.observe(this, s -> {
    message.setText(String.format(format, s));
});
dayOfWeek.postValue("Friday");
cs



LiveData는 Lifecycle(생명주기) 를 알고있다.

RxJava의 기본 Component와 LiveData의 가장 큰 차이점은  생명주기이다.   LiveData와 Lifecycle을 연관지었을 때 LiveData가 Lifecycle을 이용하기 위해서는 Android Lifecycle을 알고 있는 특정 Components 와 함께 사용되어야 한다는 것을 추측 할 수 있다.  간단한 Lifecycle에 대한 명칭과 Object를 확인 해보자.


Lifecycle Owners - Activities, Fragments

LifecycleOwner의 Interface 코드는아래와 같다. 

1
2
3
4
5
6
7
8
9
public interface LifecycleOwner {
    /**
     * Returns the Lifecycle of the provider.
     *
     * @return The lifecycle of the provider.
     */
    @NonNull
    Lifecycle getLifecycle();
}
cs



그리고 LifecycleOwner가 V7 library에 구현되어있는 UI 리스트는 아래와 같다.


기본으로 상속 받는 V7 library의 AppCompatActivity 가 LifecycleOwner이다. 

꼭 Activity가 아니더라도 생명주기를 갖을 수 있는 특정 객체에 LifecycleOwner를 구현한다면, 구현된 객체 내에서도 LiveData를 사용 할 수 있지 않을까 생각해본다.



Lifecycle Observers - Interface LifecycleObserver ( LiveData ) 

LiveData<T> 는 Abstract class기 때문에 별도로 구현하지 않는 이상 MutableLiveData<T> 를 사용 할 것 같다. (Java util package의 List<T> 와 같다고 생각하면 될 것 같다 ) 
하지만 Java의 다양성을 사용하기 위해 parameter는 LiveData<T>를 사용하는 것이 바람직 할 것 이다. ( List<T> 처럼.. )

LiveData의 객체를 생성후 observe시 사용되는 Parameter는 아래와 같다.

1
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer);
cs


그렇다 필수 Parameter가 LifecycleOwner이다.  

꼭 LifecycleOwner를 넣어야 하는것은 아니다. observeForever()를 이용해 단일 Observer만 등록도 가능하다. 하지만 Forever이다.




Dao에서의 LiveData 사용 - 기존 List<T>를 LiveData<List<T>>로 변경

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Dao
public interface ProductDao{
 
    @Insert(onConflict = IGNORE)
    void insert(ProductEntity data);
 
    @Insert(onConflict = IGNORE)
    void insertAll(List<ProductEntity> data);
 
 
    @Query("SELECT * From ProductEntity")
    List<ProductEntity> findAll();
 
    @Update(onConflict = REPLACE)
    void update(ProductEntity data);
 
    @Query("DELETE FROM ProductEntity")
    void deleteAll();
}
cs


이전 포스팅에서 정의 하였던 ProductDao 이다. 위 List<ProductEntity>를 LiveData로 바꿔보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Dao
public interface ProductDao{
 
    @Insert(onConflict = IGNORE)
    void insert(ProductEntity data);
 
    @Insert(onConflict = IGNORE)
    void insertAll(List<ProductEntity> data);
 
 
    @Query("SELECT * From ProductEntity")
    LiveData<List<ProductEntity>> findAll();
 
    @Update(onConflict = REPLACE)
    void update(ProductEntity data);
 
    @Query("DELETE FROM ProductEntity")
    void deleteAll();
}
cs


교체 완료!


그럼 이전 포스팅에서 사용했던 테스트 코드를 간단하게 재활용하여 동작시켜 보자.


Android Test

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
@RunWith(AndroidJUnit4.class)
public class ProductDaoTest {
 
    private static final String TAG = "ProductDaoTest";
 
    Context context;
    ProductDao mProductDao;
 
 
    @Before
    public void createDb() {
        context = InstrumentationRegistry.getTargetContext();
        mProductDao = MemoDatabase.getInstance(context).productDao();
    }
 
    @Test
    public void insertAndRead() throws InterruptedException {
        final CountDownLatch latch = new CountDownLatch(1);
        mProductDao.deleteAll();
        mProductDao.insert(makeProduct("Apple""Apple.co.kr"));
        LiveData<List<ProductEntity>> liveProductList = mProductDao.findAll();
        liveProductList.observeForever(productEntities -> {
            Log.d(TAG, "IN Observe!!!! CHANGE PRODUCT" + productEntities);
        });
 
        Log.d(TAG, "ALL PRODUCT" + liveProductList);
        mProductDao.insert(makeProduct("Windows""Microsoft.co.kr"));
        latch.await(1, TimeUnit.SECONDS);
        mProductDao.insert(makeProduct("Mouse""Mickey"));
        latch.await(1, TimeUnit.SECONDS);
    }
 
 
 
    @After
    public void closeDb(){
        MemoDatabase.getInstance(context).close();
    }
 
    public ProductEntity makeProduct(String name, String store){
        Log.d(TAG, "makeProduct() called with: name = [" + name + "], store = [" + store + "]");
        ProductEntity product = new ProductEntity();
        product.setId(UUID.randomUUID().toString());
        product.setCheckInDate(0);
        product.setCheckOutDate(0);
        product.setName(name);
        product.setStoreName(store);
        return product;
    }
}
cs


LiveData에 observer를 등록 후 Product Data를 Insert 할 때 마다 등록된 Observer를 통해 변경된 전체 Product List를 전달 받는 것을 확인 할 수 있다.


정리 - 기존에 등록된 DB Update Observable 들을 LiveData로 변경 가능하다!

Realm의 RealmChangeListener를 통해 변경사항을 감지하여 Observable에게 전달해주던 것을 Room(SQLite)으로 Migration 후 LiveData로 대체 가능 할 것 이다.

Room과 LiveData를 공부하였으니, 다음에는 ViewModel에 대해 알아보도록 해야겠다.


반응형