首页 > 解决方案 > Login MVP with retrofit

问题描述

Hi I'm trying to create a simple app where use the retrofit library to make a call to the api to make the login and I'd like to use the MVP pattern, could anyone of you guide to how to do it, since I've searched a lot and I found lot different solutions, so now i'd like to see yours what I thought is :

Network -retrofit instance Model -User pojo Presenter - Calls to make from activity View - Methods like showProgressBar(), hideProgressBar()

but I'm not sure if it's a good approach... I'd like to check when fails also, and have all well organised.

标签: javaandroidmvpandroid-mvp

解决方案


For android architecture, currently googles recommended MVVM pattern over MVP with it's android architecture components (AAC). There are many reasons that mvvm is preferred over MVP. Here are some cons for MVP patterns.

Callback hell

  • MVP patterns separates business logic and view modification into Presenter and View, and it requires an interface for the view to interact with the presenter when business action is performed (eg.requesting data/uploading data). The view need to implement this interface, and the presenter typically call methods according to this interface to provide data to the view. In other words, MVP use callback for the data channel.

    For each action of these interactions, one method is added to the interface. It quickly becomes overwhelming when the interaction because there will be two many method to maintain. One change of the method in the interface also leads to multiple changes in both presenter and view due to the tightly coupled nature of this MVP.

Configuration changes

  • MVP does not solve this problem when configuration changes occurs (eg. like display metrics changes, screen rotation, etc). View state will not be persisted until you explicitly handle it.

Tight couple of view and presenter

  • As mentioned, in MVP, view and presenter is tightly coupled, maintenance will be pain in the ass when it comes to adding method, changing method signatures.

MVVM patterns generally is more suited and a better design pattern in a way that it is more maintainable. I prefer MVVM over MVP for the below reasons,

Reactive

  • MVVM is short term for Model View ViewModel, where view model is a similar layer of abstraction over presenter which provide data to the view. View observes the viewModel when viewModel notifies the view. It ensures the view always get the latest data. And it avoid callbacks hells by having only one place that the view can get the data, and the view always depends on it.

    MVVM works with reactive pattern very well. For examples, like you said, retrofit typically is used with Observable pattern (like RxJava ) in MVVM. Client of retrofit lives in viewModel and retrieve data from an Observable object that can be observe by the view. Same can be done for data persistence and data from system services as well.

Survival on configuration changes

  • ViewModel survives configuration changes by having view model exceeds the lifespan of the view. In the implementation for viewModel from AAC, it does exactly that. When configuration is changed, view can always retrieve state from the viewModel, which provides a much better UX.

Code Example

In retrofit, we need to grab an instance of the retrofit client. You should retrieve an instance either by singleton / dependency injection. Here is an example from a production project. Below examples will be in kotlin code cause it is fun.

To create an instance, (note that a JSON converter factory MOSHI is used)

        Retrofit.Builder()
                .baseUrl("http://google.com")
                .addConverterFactory(MoshiConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(get("MockHttpClient"))
                .build().create<ApiService>(ApiService::class.java)

Then, an interface needs to be defined for the api you use, note that it return a Flowable which is a Rxjava object which is similar to Observable but with backpressure handling. For now just know that it is an observable and will emit data.

interface ApiService {

    @FormUrlEncoded
    @POST(ACCESS_TOKEN_PATH)
    fun getAccessToken(
            @Field("client_id") client_id: String,
            @Field("client_secret") client_secret: String,
            @Field("grant_type") grant_type: String
    ): Flowable<GetAccessToken>
}

Then in your viewModel, for simplicity we can inject this instance directly / or retrieve it from the singleton.(A repository layer can also be added for data retrieval from the source) Note that here, live data is used as the connection between viewmodel and view, which is an observable that will dispose itself when the lifecycle of the view ends.

We subscribe to the observable return from the api and get the data from it.

class TimeSettingViewModel(val context: Application, val retrofit: Retrofit) : AndroidViewModel(context) {

    private val compositeDisposable = CompositeDisposable()
    val accessTokenLiveData: MutableLiveData<AccessToken> = MutableLiveData()

    override fun onCleared() {
        compositeDisposable.clear()
        super.onCleared()
    }

    fun getAccessToken(){
        retrofit.getAccessToken("some","thing","here")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    accessTokenLiveData.value = it
                },{
                    it.printStackTrace()
                })

    }


}

Later on in your view (Activity/ Fragment / other view-based view-controller), you can inject your viewModel here and observe the data from it. And based on that data, you can update your view.

private fun accessTokenLiveData() {
                timeSettingViewModel.accessTokenLiveData.observe(this, android.arch.lifecycle.Observer {
                    if (it != null) {
                        updateLoginView(it)
                    }
                })
                timeSettingViewModel.getAccessToken()
    }

推荐阅读