【问题标题】:Android repository pattern: how pass extra data to Fragment/ActivityAndroid 存储库模式:如何将额外数据传递给 Fragment/Activity
【发布时间】:2018-11-01 08:19:54
【问题描述】:

我阅读了本指南:https://developer.android.com/jetpack/docs/guide 并在这种情况下尝试使用存储库模式:

应用程序通过 Retrofit lib 向服务器 /get/user 发出 GET 请求,服务器的响应可能是这样的(状态为 200):

 {
    "user": {"name" : "Jack", "id": "99"},
    "status": true
 }

或像这样(状态为 200):

{ "status": false, "message": "Some error here"}

或者,例如 500 错误。

我的UserFragment 应该显示对话框,取决于服务器响应:如果一切正常 - ok 消息,如果状态 false - 来自 api 响应的错误消息,如果 500 - 其他错误消息。

我的 POJO 模型如下所示:

public class User {
  private String id;
  private String name;
  // getters and setters omitted
}


public class ApiResponse {
  private User user;
  private Boolean status;
  private String message;
  // getters and setters omitted
}

我应该如何在存储库模式中处理它?

  1. 我的存储库对象是否应该将用户返回给 ViewModel?如果是 - 我的 ViewModel 如何知道 api 响应的状态和消息字段?
  2. 或者我的存储库对象应该直接将 ApiResponse 返回给 ViewModel,ViewModel 从中获取状态、消息和用户并将它们传递给 Fragment?但是如果我想将用户缓存到数据库怎么办?我们需要将所有 ApiResponse 存储到数据库还是只需要存储用户?
  3. 还是别的什么...?

我更喜欢 1,因为它对我来说更清楚,但我的问题 - 如何处理从服务器返回并需要在 Fragment 中显示数据的状态和消息字段。

【问题讨论】:

    标签: android retrofit repository-pattern android-jetpack


    【解决方案1】:

    这是我的做法:

    存储库:

    prival final MutableLiveData<User> userData = new MutableLiveData<>();
    
    public void getUser(){
    //post user to userData when you got response from the server
    }
    public LiveData<User> getUserData(){ return userData; }
    

    视图模型:

    public LiveData<User> user = Repository.getInstance().getUserData();
    

    在这种情况下,您的 viewModel 不会每次都创建 liveData,它会从 Repository 获取 liveData。此外,您将在您的存储库中加载数据,因此您不必经常触发调用。

    如果您需要了解每个呼叫状态,请使用您的 NetworkState 枚举在 liveData 和响应 obj livedata 中创建类似 DataSource 持有者对象

    这是我的 RemoteDataSource:

    public class RemoteDataSource<T> {
        private final MutableLiveData<NetworkState> networkState = new MutableLiveData<>();
        private final MutableLiveData<T> data = new MutableLiveData<>();
        private final Action action;
        private String errorMessage;
    
        public RemoteDataSource(Action action) {
            networkState.postValue(NetworkState.Default);
            this.action = action;
        }
    
        public MutableLiveData<NetworkState> getNetworkState() {
            return networkState;
        }
    
        public void setIsLoading() {
            networkState.postValue(NetworkState.Loading);
        }
    
        public void setDefault() {
            networkState.postValue(NetworkState.Default);
        }
    
        public void setIsLoaded(T data) {
            networkState.postValue(NetworkState.Loaded);
            this.data.postValue(data);
        }
    
        public void setFailed(@NonNull String errorMessage) {
            this.errorMessage = errorMessage;
            networkState.postValue(NetworkState.Failed);
        }
    
        public String getErrorMessage() {
            return errorMessage;
        }
    
        public MutableLiveData<T> getData() {
            return data;
        }
    
        public void executeLoad() {
            if (action != null) {
                try {
                    action.run();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      我使用存储库模式只保留简单的操作来检索所需的信息,没有什么复杂的:

      https://github.com/ApploverSoftware/android-mvvm-architecture/blob/master/app/src/main/java/pl/applover/architecture/mvvm/data/example/repositories/ExampleCitiesRepository.kt

      /**
       * Repository that exposes DataSources and Observables for loading data from local/network sources
       * Repository also exposes Subjects that inform about state of the calls
       */
      class ExampleCitiesRepository @Inject constructor(private val apiCities: ExampleCitiesApiEndpointsInterface,
                                                        private val daoCities: ExampleCityDao) {
      
          fun citiesDataSourceFactory(compositeDisposable: CompositeDisposable) = CitiesDataSourceFactory(apiCities, compositeDisposable)
      
          fun citiesFromNetwork() =
                  apiCities.getCitiesList().mapResponseList(mapper = { ExampleCityModel(it) })
      
          fun pagedCitiesFromDatabase() = daoCities.citiesPagedById().map { ExampleCityModel(it) }
      
          fun citiesFromDatabase() = daoCities.citiesById().map { it.map { ExampleCityModel(it) } }!!
      
          fun saveAllCitiesToDatabase(cities: Collection<ExampleCityModel>) = Single.fromCallable { daoCities.insertOrReplaceAll(cities.map { ExampleCityDbModel(it) }) }!!
      
          fun deleteAllCitiesFromDatabase() = Single.fromCallable { daoCities.deleteAll() }!!
      
      }
      

      此外,我将“后端模型”映射到应用模型(我不喜欢创建一个模型,因为后端可以更改它们的模型,然后它迫使我们也改变我们的模型)。就是这样,我可以获得我想要处理的模型,可以使用 Rx 的 dispose 结束调用或使用它获取错误代码或消息。

      ViewModel 可以很好地处理这个问题,因为我们可以在不再需要 viewModel 时获得回调,然后我们可以取消网络调用。如果应用程序没有打开,我们可以继续进行网络调用,因为实时数据可以安全地将数据从 ViewModel 及时传递到 View

      【讨论】:

        【解决方案3】:

        这取决于您计划在Fragment 上显示什么信息。

        如果网络出现问题,您会向用户显示错误消息吗?或者您是否会很高兴只显示“未找到用户”消息。

        您计划向用户展示的数据应进入ViewModel

        如果您打算直接显示来自 Api 的错误消息,请将其传递给 ViewModel

        如果您打算只显示用户,请将其传递给ViewModel。在这种情况下,错误信息只能泛化。

        【讨论】:

        • 如果网络出现问题,您会向用户显示错误消息吗?或者您是否会很高兴只显示“未找到用户”消息。 如果服务器返回“状态”:false,我应该显示“消息”字段的值,该服务器返回。如果服务器返回 500 错误,我应该显示“网络错误”消息。
        • 然后你需要将ApiResponse 传递给你的ViewModel。
        • 不过,另一种选择是您有第二个 LiveData&lt;String&gt; 包含错误消息。不过对于这种情况,您应该找到LiveData 的“一次性”实现。
        【解决方案4】:

        使用改造,
        您可以在回调类中获得响应,
        你必须创建一个新的类来扩展它,

        class SampleCallback(private var mContext: Context) : Callback<SampleResponse> {
            override fun onResponse(call: Call<SampleResponse>, response: Response<SampleResponse>) {
                when (response.code()) {
                    200-> //Update your View with mContext
                }
            }
        }
        

        不要忘记覆盖 onFailure。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-02-13
          • 2013-01-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多