如何使用 ViewModel 和 LiveData 进行改造 API 调用
Posted
技术标签:
【中文标题】如何使用 ViewModel 和 LiveData 进行改造 API 调用【英文标题】:How to make retrofit API call using ViewModel and LiveData 【发布时间】:2019-01-17 17:25:57 【问题描述】:这是我第一次尝试实现 MVVM 架构,我对进行 API 调用的正确方法有点困惑。
目前,我只是尝试从 IGDB API 进行简单查询,并输出日志中第一项的名称。
我的活动设置如下:
public class PopularGamesActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_popular_games);
PopularGamesViewModel popViewModel = ViewModelProviders.of(this).get(PopularGamesViewModel.class);
popViewModel.getGameList().observe(this, new Observer<List<Game>>()
@Override
public void onChanged(@Nullable List<Game> gameList)
String firstName = gameList.get(0).getName();
Timber.d(firstName);
);
我的视图模型设置如下:
public class PopularGamesViewModel extends androidViewModel
private static final String igdbBaseUrl = "https://api-endpoint.igdb.com/";
private static final String FIELDS = "id,name,genres,cover,popularity";
private static final String ORDER = "popularity:desc";
private static final int LIMIT = 30;
private LiveData<List<Game>> mGameList;
public PopularGamesViewModel(@NonNull Application application)
super(application);
// Create the retrofit builder
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(igdbBaseUrl)
.addConverterFactory(GsonConverterFactory.create());
// Build retrofit
Retrofit retrofit = builder.build();
// Create the retrofit client
RetrofitClient client = retrofit.create(RetrofitClient.class);
Call<LiveData<List<Game>>> call = client.getGame(FIELDS,
ORDER,
LIMIT);
call.enqueue(new Callback<LiveData<List<Game>>>()
@Override
public void onResponse(Call<LiveData<List<Game>>> call, Response<LiveData<List<Game>>> response)
if (response.body() != null)
Timber.d("Call response body not null");
mGameList = response.body();
else
Timber.d("Call response body is null");
@Override
public void onFailure(Call<LiveData<List<Game>>> call, Throwable t)
Timber.d("Retrofit call failed");
);
public LiveData<List<Game>> getGameList()
return mGameList;
现在问题是因为这是一个 API 调用,mGameList
的初始值将是 null,直到 call.enqueue
返回一个值。这将导致带有
popViewModel.getGameList().observe(this, new Observer<List<Game>>()
-
那么处理 LiveData 观察的正确方法是什么,
在进行 API 调用时?
我是否执行了 Retrofit API 调用
在正确的地方?
【问题讨论】:
【参考方案1】:您的代码中有 3 个问题。
-
您必须创建一个
MutableLiveData
对象,因为在 API 调用之前您有一个空响应,然后您的 LiveData
对象将通过 IGDB 响应以某种方式填充。
private MutableLiveData<List<Game>> mGameList = new MutableLiveData();
//...
public LiveData<List<Game>> getGameList()
return mGameList;
-
另一个错误是更改
mGameList
的引用而不是设置它的值,所以尝试更改:
Timber.d("Call response body not null");
mGameList = response.body();
到
mGameList.setValue(response.body());
-
在
ViewModel
类中调用改造可以避免关注点分离。最好创建一个存储库模块并通过界面获取您的响应。阅读此article 了解详情。
存储库模块负责处理数据操作。他们 为应用程序的其余部分提供一个干净的 API。他们知道从哪里获得 数据来自以及更新数据时调用的 API。你可以 将它们视为不同数据源之间的中介(持久 模型、Web 服务、缓存等)。
【讨论】:
感谢您的回复。在意识到您的回复之前,我实际上已经注意到了我的错误 1 和 2,所以我解决了这个问题。谢谢指出我的错误3,我去加个仓库。 我想很多人都有同样的问题,你救了我的命,谢谢。 我如何从View
知道存储库数据请求何时完成。【参考方案2】:
我刚刚受到 Google Demo 的启发,创建了一个库,可以为 Retrofit 添加 LiveData 支持。用法很简单:
Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(LiveDataCallAdapterFactory())
.build()
.create(GithubService::class.java)
.getUser("shawnlinboy").observe(this,
Observer response ->
when (response)
is ApiSuccessResponse ->
//success response
else ->
//failed response
)
图书馆网站: https://github.com/shawnlinboy/retrofit-livedata-adapter
【讨论】:
【参考方案3】:// 改造示例界面
public interface GitHubService
@GET("users/user/repos")
Call<List<String>> listRepos(@Path("user") String user);
// ViewModel 类
public static class MainActivityViewModel extends AndroidViewModel
final Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
final GitHubService gitHubService = retrofit.create(GitHubService.class);
// binding to EditText in DataBinding layout
public final MutableLiveData<String> userName = new MutableLiveData<>();
// binding to ListView in DataBinding layout
public final LiveData<List<String>> listLiveData = Transformations.switchMap(userName, (s) -> new MutableLiveData<List<String>>()
if (!TextUtils.isEmpty(s))
gitHubService.listRepos(s).enqueue(new Callback<List<String>>()
@Override
public void onResponse(Call<List<String>> call, Response<List<String>> response)
setValue(response.body());
@Override
public void onFailure(Call<List<String>> call, Throwable t)
);
);
public MainActivityViewModel(@NonNull Application application, SavedStateHandle savedStateHandle)
super(application);
【讨论】:
以上是关于如何使用 ViewModel 和 LiveData 进行改造 API 调用的主要内容,如果未能解决你的问题,请参考以下文章
如何使用ViewModel和LiveData进行改进API调用
使用 livedata 和 ViewModel 根据现有数据查找数据
JetpackLiveData 架构组件 ( LiveData 简介 | LiveData 使用方法 | ViewModel + LiveData 示例 )
JetpackLiveData 架构组件 ( LiveData 简介 | LiveData 使用方法 | ViewModel + LiveData 示例 )