티스토리 뷰

AAC (Android Architectire Components)

  • 안드로이드 아키텍처 구성요소는 테스트와 유지관리가 쉬운 앱을 디자인하도록 돕는 라이브러리의 모음이다
  • 앱의 수명 주기를 관리하여 구성 변경을 유지하고 메모리 누수를 방지하며 UI에 쉽게 데이터를 로드할 수 있게 해준다
  • 현재 AAC는 아래 5개 라이브러리를 제공한다
    1. LifeCycle
    2. LiveData
    3. ViewModel
    4. Room
    5. Paging
  • 아래의 그림은 아키텍처 컴포넌트들이 상호작용하는 순서를 나타낸 그림이다

ViewModel

  • MVVM 패턴에서 ViewModel은 Repository와 View를 연결해주는 다리 역할을 한다
  • ViewModel에서는 UI를 위한 데이터를 가지고 있으며, 화면 회전과 같이 구성을 변경할 때도 데이터를 유지할 수 있다
  • AAC의 ViewModel은 데이터를 생명주기와 분리하여 관리할 수 있도록 해준다 
  • AndroidViewModel을 상속받은 ViewModel의 객체를 ViewModelProvider로 만들어 사용하는데, 이 ViewModel의 범위는 ViewModelProvider의 파라미터로 전달되는 LifeCycleOwner에 의해 지정된다

  • 위의 사진을 보면 Activity가 rotation되어도 ViewModel은 사라지지않고 하나로 유지된다
  • 이렇게, ViewModel을 사용하면 Activity와 Fragment간의 커뮤니케이션이 필요할 때 하나의 ViewModel을 바라보고 같은 LiveData에 접근하여 데이터를 사용할 수 있다

✔️ AndroidViewModel 

  • application을 파라미터로 사용하는 ViewModel의 서브클래스이다.
  • AndroidViewModel은 activity나 fragment의 생명주기에 의존적인 ViewModel 클래스와 달리 어플리케이션의 Scope와 함께하는 ViewModel 객체를 생성한다
  • ViewModel 내에서 context가 필요하다면 AVM을 사용해야한다 (RoomDB의 객체를 생성할 때는 application이 필요한데, 이 때 activity의 context를 사용하면 activity가 destroy된 경우 메모리 누수가 발생할 수 있으므로 싱글톤 application을 사용한다)

✔️ ViewModelProvider

/**
* Creates {@code ViewModelProvider}. This will create {@code ViewModels}
* and retain them in a store of the given {@code ViewModelStoreOwner}.
* <p>
* This method will use the
* {@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory() default factory}
* if the owner implements {@link HasDefaultViewModelProviderFactory}. Otherwise, a
* {@link NewInstanceFactory} will be used.
*/
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
	this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
		? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
		: NewInstanceFactory.getInstance());
}
  • ViewModel 객체를 만들어주는 ViewModelProvider의 코드를 살펴보겠다!
  • ViewModelStoreOwner는 ViewModel을 관리하는 ViewModelStore 객체를 만들고 관리하는 인터페이스이다
    • (ViewModelStoreOwner 인터페이스  --관리/생성-->  ViewModelStore 객체  --관리/생성--> ViewModel 객체)
  • activity와 fragment에서 ViewModelStoreOwner를 구현하고 있기 때문에ViewModel 객체를 만들 때는 activity와 fragment가 필요하다 (우리가 파라미터의 어떤 owner를 전달하냐에 따라서 ViewModelScope가 정해지는 것이다)
  • 그렇다면 저기있는 Factory는 뭐지!??!
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
public interface Factory {
	/**
	* Creates a new instance of the given {@code Class}.
	* <p>
	*
	* @param modelClass a {@code Class} whose instance is requested
	* @param <T>        The type parameter for the ViewModel.
	* @return a newly created ViewModel
	*/
	@NonNull
	<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
  • Factory는 ViewModelProvider 안에 있는 인터페이스로 실질적인 ViewModel 인스턴스를 생성해준다
public static class NewInstanceFactory implements Factory {

        private static NewInstanceFactory sInstance;

        /**
         * Retrieve a singleton instance of NewInstanceFactory.
         *
         * @return A valid {@link NewInstanceFactory}
         */
        @NonNull
        static NewInstanceFactory getInstance() {
            if (sInstance == null) {
                sInstance = new NewInstanceFactory();
            }
            return sInstance;
        }

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
}
  • 바로 이렇게 Factory 인터페이스를 상속받는 NewInstanceFactory 클래스 안에 있는 create 메소드로 인스턴스를 만들어 반환해준다

✔️ ViewModel 객체 생성하기

- ViewModel 클래스 타입 객체 생성

val viewModel = ViewModelProvider(this).get(PlaceSearchViewModel::class.java)
  • ViewModel 클래스를 상속받고 파라미터가 없는 ViewModel 객체는 위와같이 ViewModelProvider를 사용해 생성하여 사용한다
  • 여기서  해당 객체가 생성되고 있는 activity를 owner로 보내준 것이다
viewModel = ViewModelProvider(requireActivity()).get(PlaceSearchViewModel::class.java)
  • fragment에서 viewModel 생성 시 fragment가 호출되는 activity를 owner로 넘기면 된다

- AndroidViewModel 클래스 타입 객체 생성

  • AVM 타입 객체를 생성을 위해서는 owner와 함께 factory 객체를 파라미터로 함께 넘겨줘야한다🌟
viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(PlaceSearchViewModel::class.java)
  • 특별히 Factory 인터페이스를 커스텀하지 않아도 되는 경우 위에서 살펴본 NewInstanceFactory 클래스로 인스턴스를 생성하여 넘겨줄 수 있다

- Factory를 커스텀하여 ViewModel 객체 생성

class placeSearchViewModel(val idx: Int) : ViewModel() {
   ...
}
  • 만약 위와 같이 viewModel에 파라미터로 값이 전달되는 경우에는 Factory를 커스텀해서 사용해야한다
class ViewModelFactory(val idx: Int) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return modelClass.getConstructor(Int::class.java).newInstance(idx)
    }
}
  • Factory 클래스를 상속받는 커스텀 클래스를 작성하여 파라미터를 받아 그 값을 이용해 ViewModel 인스턴스를 생성해 반환해주게 된다
val viewModel = ViewModelProvider(this, ViewModelFactory(1)).get(PlaceSearchViewModel::class.java)
  • ViweModel 객체 생성 시에는 커스텀한 Factory 클래스로 인스턴스를 만들어 파라미터로 넘겨주면 된다

✔️ ViewModel 을 직접 사용하기

StartPlaceSearchActivity와 PlaceListFragment에서 하나의 PlaceSearchViewModel 객체를 사용해 같은 LiveData를 사용하는 코드를 짜는 것이 나의 임무이다

open class BaseViewModel(application: EarlyBuddyApplication) : AndroidViewModel(application){

    private val compositeDisposable = CompositeDisposable()

    fun addDisposable(disposable: Disposable) {
        compositeDisposable.add(disposable)
    }

    override fun onCleared() {
        compositeDisposable.clear()
        super.onCleared()
    }
    
}
  • AndroidViewModel을 상속받는 BaseViewModel을 다음과 같이 만들어놓았다
  • parameter로 application을 전달해야하는 것이 ViewModel을 상속받을 때와는 다른 차이점이다
class PlaceSearchViewModel : BaseViewModel(EarlyBuddyApplication.getGlobalApplicationContext()) {
	
    private var _placeList = MutableLiveData<List<PlaceSearch>>()
    val placeList : LiveData<List<PlaceSearch>> get() = _placeList
	
    fun getPlaceSearchData(query: String){
    		...
    	}
}
  • BaseViewModel을 상속받는 PlaceSearchViewModel 클래스를 만들어주었다
  • 상속받는 클래스를 정의할 때 BaseViewModel의 파라미터로 application을 생성해 넘겨줘야한다
  • StartPlaceSearchActivity와 PlaceListFragment 둘 다 바로 여기있는 placeList라는 LiveData에 접근을 해야한다
class StartPlaceSearchActivity : BaseActivity<ActivityStartPlaceSearchBinding, PlaceSearchViewModel>() {

    override val layoutResID: Int
        get() = R.layout.activity_start_place_search
    override lateinit var viewModel: PlaceSearchViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(PlaceSearchViewModel::class.java)
	...
    }
    
    fun getPlaceData(){
        val query = act_start_place_search_et_search.text.toString()
        viewModel.getPlaceSearchData(query)
    }
}
  • activity에서 ViewModelProvider를 이용해 ViewModel 객체를 만들어줬다
  • owner로는 this로 해당 activity를 보내고, NewInstanceFactory클래스로 인스턴스를 생성해 파라미터로 함께 보낸다
  • getPlaceData에서는 viewModel 객체로 ViewModel 클래스 내부 통신 함수에 접근하여 LiveData를 업데이트 해준다
class PlaceListFragment : BaseFragment<FragmentPlaceListBinding, PlaceSearchViewModel>() {

    override val layoutResID: Int
        get() = R.layout.fragment_place_list
    override lateinit var viewModel: PlaceSearchViewModel

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel = ViewModelProvider(requireActivity(), ViewModelProvider.NewInstanceFactory()).get(PlaceSearchViewModel::class.java)
        viewModel.placeList.observe(viewLifecycleOwner, Observer {
            (viewDataBinding.fragPlaceListRv.adapter as BaseRecyclerViewAdapter<PlaceSearch, ItemPlaceListBinding>)
                .replaceAll(it)
            (viewDataBinding.fragPlaceListRv.adapter as BaseRecyclerViewAdapter<PlaceSearch, ItemPlaceListBinding>)
                .notifyDataSetChanged()
        })
    }
}
  • fragment에서 역시 activity에서와 같은 방식으로 ViewModel 객체를 생성해준다
  • owner로는 activity를 넘긴다
  • 생성한 ViewModel 객체를 이용해 ViewModel 클래스 내부의 LiveData를 observe하고 있다가 activity에서 이루어지는 통신을 통해 LiveData에 업데이트가 일어나면 RecyclerView의 내용을 바꿔준다!

포스팅을 끝내니까 4시간이나 지났다

그래도 이렇게 하나하나 찾아보며 공부를 하니 내 실수때매 발생한 에러에 화를 내던 내 자신을 반성하게 되었다 😥

또 정말 똑똑하고 멋지신 분들이 블로그에 이미 너무 잘 정리해놓으신 덕에 도움이 많이 되었다❗️

나도 언젠가 그런 사람이 되고 싶다는 생각을 하며 얼른 프로젝트에 Room을 적용해 다음주에는 Room에 대한 글을 포스팅해야겠다 🥊

 

참고자료 🕊

 

[Android] AAC ViewModel 을 생성하는 6가지 방법 - ViewModelProvider

이 글은 이전 포스팅([Android] 화면 회전해도 데이터 유지하기 - AAC ViewModel)에 이어지는 글입니다. ViewModel 클래스를 상속하여 정의한 클래스는 개발자가 직접 생성자를 통하여서 인스턴스를 생성�

readystory.tistory.com

 

Android Architecture Components ViewModel을 간단하게 초기화 하려면? |

I’m an Android Developer.

thdev.tech

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/12   »
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
글 보관함