현치비

안드로이드 아키텍쳐 컴포넌트(Android Architecture Components) - 3부 본문

Android/Android Library

안드로이드 아키텍쳐 컴포넌트(Android Architecture Components) - 3부

현CHIBI 2017. 8. 4. 16:40

오늘은 LiveData를 알아보자.


LiveData?


데이터를 가지고 있고, 데이터의 변경을 감지할 수 있는 클래스


끝이다.


필자는 Api 응답이나, 프래그먼트 간의 데이터 교환, 데이터 통일성 유지를 위해 많이 사용한다.


예를 들어, 카페 앱을 만든다고 생각해보자.


카페 게시글 List 화면(A) 프래그먼트, 카페 게시글 상세정보화면(B) 프레그먼트


두 프래그먼트 화면에서 각 카페 글에 대한 좋아요 갯수 데이터를 가지고 있다고 가정하자.


A -> B 프레그먼트로 전환될 때 우리는 newInstance(int likeCount) 이런식으로 데이터를 넘긴다.


B 프레그먼트에서 데이터를 변경하고, likeCount++;


B 프레그먼트에서 BackKey를 눌러 A로 다시 돌아오는 경우에 A에게 변경된 데이터를 다시 넘기고 싶다면?


getActivity().findFragmentByTag(AFragment.TAG).setLikeCount(likeCount); < 이런식으로 해야한다..


아니면 매번 프래그먼트 진입 시에 API request를 쏴서 최신 데이터를 받아와도 된다. (사용자 데이터 낭비우려..).


LiveData는 이러한 귀찮은 문제들을 손쉽게 해결해준다.


A와 B에서 같은 LiveData 객체를 observe하고 있다면,


LiveData에 setValue가 일어날 때, observe가 알아서 불리게 된다. (물론, 프래그먼트의 상태가 active 상태일때만!!)


내용은 복잡하지만, 배워두면 코드가 정말 간결해지고 멋진 앱을 만들 수 있으니


꼭 사용해보길 바란다.


사용법을 알아보자.


public class LocationLiveData extends LiveData<Location> {
   
private LocationManager locationManager;

   
private SimpleLocationListener listener = new SimpleLocationListener() {
       
@Override
       
public void onLocationChanged(Location location) {
            setValue
(location);
       
}
   
};

   
public LocationLiveData(Context context) {
        locationManager
= (LocationManager) context.getSystemService(
               
Context.LOCATION_SERVICE);
   
}

   
@Override
   
protected void onActive() {
        locationManager
.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
   
}

   
@Override
   
protected void onInactive() {
        locationManager
.removeUpdates(listener);
   
}
}


LiveData<T>를 상속받은 클래스를 만들어서 구현해주면 된다.


onActive는 이 LocationLiveData를 observe하는 클래스가 0개에서 1개 이상이 될 때 불리고,


onInactive는 이 LocationLiveData를 observe하는 클래스가 1개 이상에서 0개가 될 때 불린다.


setValue는 이 LiveData에 값을 넣어주는 메서드인데, 


이 observe하는 클래스들에게 값을 전부 전달해준다.


public class MainActivity extends LifeCycleActivity {


    @Override

    public void onCreate(Bundle saveInstanceState) {

MyViewModel myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);

myViewModel.locationLiveData.observe(location -> updateLocation(location));

    };

   
private updateLocation(Location location) {
        if (location == null) {

return;

}

//someting
   
}
}


위에 MyViewModel에서는 locationLiveData를 가지고 있고, observe를 이용하면 변경되는 location을 계속해서 받아볼 수 있다.


Activity나 Fragment에서 데이터를 얻기 위해 네트워크 통신이나 로컬 DB를 접근하는 로직을 더이상 사용하지 않아도 된다.


단지 필요한 데이터를 observe하고 있다가 데이터가 오면 view에 update해주면 된다.


일반적으로 ViewModel에서 네트워크나 DB에서 데이터를 얻어와서 


Field(전역변수) LiveData에 setValue를 해주는 방식을 많이 사용한다.


심화학습(유의할 점)


1. liveData.observe(location -> updateLocation(location)); 이런 식으로 코드를 짜면 새로운 Observe<T>가 계속 들어갈 수 있다.

-> 가로 모드로 전환할 경우 onCreate에서 저 코드가 있다면, Observe가 새로운 인스턴스로 계속 생성되어 계속 들어가게 된다.

-> LiveData가 변경되면 observe가 여러 번 불리게 되는 이상한 현상이 발생된다.


해결책 : 필드로 Observer를 생성하여 넣으면 같은 Observer 객체로 인식하여 중복으로 들어가지 않게 된다.


public class MainActivity extends LifeCycleActivity {
   
private Observer<Location> locationObserver = location -> updateLocation(location);

    @Override

    public void onCreate(Bundle saveInstanceState) {

MyViewModel myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);

myViewModel.locationLiveData.observe(locationObserver);

    };

   
private updateLocation(Location location) {
        if (location == null) {

return;

}

//someting
   
}
}



2. liveData.observe는 가로<->세로 모드 전환시 불리게 된다.

-> liveData.observe로 데이터를 받아온 상태에서 가로모드로 전환하면 새로운 Activity가 생성되면서 observe 데이터를 받게된다.

-> 당연한 이야기라고 들릴지도 모르겠지만, 이것을 고려해서 코딩하는 것을 추천한다.


ViewModel과 LiveData를 사용해보자


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 LifecycleFragment {
   
public void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);
       
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model
.getSelected().observe(this, { item ->
           
// update UI
       
});
   
}
}

MasterFragment에서 setOnclickListener()를 통해 model.select(item)를 호출하고 있다.


DetailFragment에서는 getSelected().observe()를 통해 item 데이터를 전달받을 수 있다.


예시로,


추천 버튼을 누르면 추천 갯수가 올라가고 추천 갯수를 보여주는 Fragment들이 많이 있다고 한다면,


각각의 Fragment들은 추천 갯수를 observe하고 값이 변경될 때마다 view를 업데이트해주면 된다.


LiveData를 통해 데이터의 변경을 언제든지 손쉽게 파악할 수 있다는 점이 큰 장점이다.


매번 필요한 곳에서 Data가 변경되었는지 체크를 해서 Data를 가져오는 코드가 줄어들게 된다.

Comments