본문

180402(월) - Android Architecture Components (ViewModel)

Android Architecture Components 


ViewModel

- lifecycle을 고려하여 UI-related data를 store and manage하도록 디자인

- ViewModel class를 사용하면 configuration changes에도 data가 survive


- Android Framework는 activity and fragment 같은 UI controllers의 lifecycle을 관리

- framework는 completely out of your control된 user action and device events에 대한 response로 UI controller를 destroy or re-recreate 결정가능

- 위와같은 현상이 발생되면 UI관련된 store data를 lost

- activity가 recreate되면 new activity는 users list를 re-fetch 해야 한다.

- 단순한 data는 onSaveInstanceState()를 사용해서 onCreate()의 bundle data로 restore하면 되지만, 어디까지나 serialized and deserialized 가능한 small amount에만 적합하고 users or bitmaps 같은 potentially large amount data는 아니다.


- another problem은 UI controller가 asynchronous call을 자주 call해서 return 하는데 시간이 걸릴 수 있다는 것

- UI controller는 이러한 call을 manage하고 potential memory leaks를 피하기 위해 system을 destroyed하고 나서 system clean 한다.

- 이런 management에는 많은 maintenance가 필요하고 object가 re-create 되면 object가 이미 만든 call을 reissue해야 하므로 resource 낭비


- UI controller는 주로 display UI data, react to user actions, or handle operating system communication (ex permission request)

- UI controller가 loading data from database or network까지 하게되면 class가 너무 bloat 된다.

- UI controller가 assigning excessive responsibility이면 모든 일을 handling하려는 single class가 된다.

- 이렇게 single class가 되면 testing하기가 매우 harder해진다.

- UI controller logic에서 view data를 분리하는게 훨씬 효율적이다.


Implement a ViewModel

- Architecture components는 UI controller를 위한 UI관련 data preparing을 담당하는 ViewModel helper class를 제공한다.

- ViewModel class는 configuration change동안에도 automatically retained 이므로 next activity or fragment instance에서도 즉시 사용가능하다.

- 다음과 같이 activity or fragment대신에 ViewModel이 data를 획득하고 유지하는 역할을 담당해야 한다.

public class MyViewModel extends ViewModel {
   
private MutableLiveData<List<User>> users;
   
public LiveData<List<User>> getUsers() {
       
if (users == null) {
            users
= new MutableLiveData<List<Users>>();
            loadUsers
();
       
}
       
return users;
   
}

   
private void loadUsers() {
       
// Do an asynchronous operation to fetch users.
   
}
}
public class MyActivity extends AppCompatActivity {
   
public void onCreate(Bundle savedInstanceState) {
       
// Create a ViewModel the first time the system calls an activity's onCreate() method.
       
// Re-created activities receive the same MyViewModel instance created by the first activity.

       
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model
.getUsers().observe(this, users -> {
           
// update UI
       
});
   
}
}

- activity가 re-create되면 first activity에 의해서 생겨난 instance와 같은 MyViewModel을 receive.

- owner activity가 finished되면 framework는 resource clean up을 위해 ViewModel의 onCleared() method를 call

※ ViewModel은 activity context reference를 hold하고 있는 arch.LifeCycle or any class를 reference하지 말 것!

- ViewModel object는 view or LifecycleOwner의 특정 instantiations보다 outlive되도록 designed되었다.

- 이 design은 view and Lifecycle object를 알지 못하므로 ViewModel을 좀 더 쉽게 cover하는 test를 작성 가능하다.

- ViewModel object에는 LiveData같은 LifecycleObservers가 포함될 수 있으나, observe해서는 안된다.


The lifecycle of a ViewModel

- ViewModel object는 getting ViewModel할 때, ViewModelProvider에 LifeCycle scoped가 전달된다.

- ViewModel 은 lifecycle scope가 permanently goes away 될때까지 memory에 남아있다. (activity finishes, fragment detached)


- 다음 그림은 activity가 rotation -> finished로 되어가는 various lifecycle states 이다

- 연관된 activity lifecycle에 대한 ViewModel의 lifetime도 보여주고 있다.

- fragment에도 basic states는 동일하게 적용

Illustrates the lifecycle of a ViewModel as an activity changes state.


- usually ViewModel은 onCreate()에서 first time system call

- system may call onCreate() several times, But ViewModel은 first request ~ activity finish and destroyed 까지 exists


Share data between fragments

- activity는 아무것도 필요하지 않고, communication에 대해서 알지 않아도 됌

- fragment는 SharedViewModel and 서로에 관해 알 필요가 없음

- 각각의 fragment는 own lifecycle이 있고 다른 lifecycle에 영향받지 않음.

public class SharedViewModel extends ViewModel {
   
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

   
public void select(Item item) {
        selected
.setValue(item);
   
}

   
public LiveData<Item> getSelected() {
       
return selected;
   
}
}

public class MasterFragment extends Fragment {
   
private SharedViewModel model;
   
public void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);
        model
= ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector
.setOnClickListener(item -> {
            model
.select(item);
       
});
   
}
}

public class DetailFragment extends Fragment {
   
public void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);
       
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model
.getSelected().observe(this, { item ->
           
// Update the UI.
       
});
   
}
}


Replacing Loaders with ViewModel

CursorLoader 와 같은 loader class는 database와 sync된 app's UI data를 유지하는데 자주 쓰인다.

- 이를 ViewModel과 몇가지 다른 class로 대체 가능

- ViewModel을 사용하면 data-loading operation이 분리되어 class사이의 strong references가 적어진다.


common loaders

- observe database


ViewModel works

- Room and LiveData로 대체

This blog post

공유

댓글