Android | MVP in action with my app

By Moka


내용이 조금 어려울수 있습니다. 왜냐하면 여러 MVP 예제 프로젝트를 통해 저만의 방식대로 만들었기 때문입니다.

앞서서 기본적으로 MVP 의 구조와 간단하게 구현하는 방법들을 살펴 보았습니다. 이번에는 제가 만든 샘플 프로젝트를 통해서 Base 클래스 들을 어떻게 만들어 활용하고있는지 살펴볼 예정입니다.

샘플 프로젝트는 “MVP 로 작성된 Sample Project - GitHub” 이며, 같은 구조를 적용하여 만든 어플은 “모닝카페-기상알람,가계부,일기” 입니다.


BaseMvpView

interface BaseMvpView {

    fun getActivity(): Activity

    fun showLoadingDialog()

    fun dismissLoadingDialog()

    fun getCompositeSubscription(): CompositeSubscription // rxJava 의 subscribe 때문

}


BaseMvpFragment

abstract class BaseMvpFragment : BaseFragment(), BaseMvpView {

    private var presenter: BasePresenter<BaseMvpView>? = null

    @Suppress("UNCHECKED_CAST")
    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        presenter = getPresenter() as BasePresenter<BaseMvpView>
        if (null != presenter)
            presenter!!.attachView(this)
        onViewCreated_afterAttachViewToPresenter()
    }

    protected abstract fun onViewCreated_afterAttachViewToPresenter()

    override fun onDestroyView() {
        super.onDestroyView()
        if (null != presenter)
            presenter!!.detachView()
    }

    protected abstract fun getPresenter(): BasePresenter<*>

}

BaseMvpFragment 는 추상 클래스로 만들어져 getPresenter() 를 구현하도록 해놓고, onViewCreated 에서 호출하여 presenter 의 인스턴스를 멤버변수로 가지고 있도록 해놓았습니다. 이렇게 한 이유는 presenter 를 DI 라이브러리를 통해 인젝션 받는데 구현하는 Fragment 에서 인젝션 후에 attachView() 하는 작업을 BaseMvpFragment 에 넣기 위함입니다. ( 제대로 이해가 됬을지 모르겠습니다 )

DI 라이브러리로 Dagger2 를 사용하였고, 추후에 관련 포스팅을 하도록 하겠습니다.

그리고 onDestroyView() 에서 detachView() 를 통해 presenter 에서 view 를 제거 합니다. 이는 조금있다 Presenter 에서 볼수있는데, presenter 에서 view 의 유무에 따라 작업을 중단해야할 일이 생길수 있기 떄문입니다.


BasePresenter

abstract class BasePresenter<VIEW : BaseMvpView> {

    var view: VIEW? = null
        private set

    fun attachView(view: VIEW) {
        this.view = view
    }

    open fun detachView() {
        view = null
    }

    val isAttached: Boolean
        get() = null != view

}

BasePresenter 는 Generic 을 이용하여 View interface 를 받을수 있도록 했습니다.


Fragment 에서

class TaskListFragment : BaseMvpFragment(), TaskListView {

    // ...
    /**
     * Injection value & needed mvp
     */

    @Inject
    lateinit var presenter: TaskListPresenter

    override fun getPresenter(): BasePresenter<*> = presenter

    /**
     * LifeCycle function
     */

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        super.onCreateView(inflater, container, savedInstanceState)
        val rootView = inflater!!.inflate(R.layout.fragment_task_list, container, false)

        DaggerFragmentComponent.builder()
                .applicationComponent((activity.application as MokaToyApplication).applicationComponent)
                .fragmentModule(FragmentModule())
                .build().inject(this)

        return rootView
    }
    ...
}

Fragment 에서 Presenter 를 Injection(주입) 하였고, BaseMvpFragment 를 상속 받았기 때문에 onCreateView() 에서 presenter 에 attachView 가 될것입니다.


Adapter

SeongUg Steve Jung 님의 Medium 포스팅 ‘Adapter, 누구냐 넌?’ 을 참고하여 구현하였습니다.

위의 포스팅처럼 adapter 를 interface 를 이용하여 View 가 할일, Model 이 할일로 구분하여 presenter 에서는 adapter 의 interface 를 통해서 adapter 를 컨트롤 하고있습니다.


다른 MVP 포스팅 보기 및 샘플 프로젝트