【发布时间】:2020-10-11 07:28:41
【问题描述】:
据我所知,ViewModel 应该与 UI/View 隔离,并且只包含观察来自服务器或数据库的数据的逻辑
在我的应用程序中,我使用了 REST API "retrofit" 和博客 API,我尝试将当前代码迁移/升级到 MVVM,但有一些问题,让我们去代码
BloggerAPI 类
public class BloggerAPI {
private static final String BASE_URL =
"https://www.googleapis.com/blogger/v3/blogs/4294497614198718393/posts/";
private static final String KEY = "the Key";
private PostInterFace postInterFace;
private static BloggerAPI INSTANCE;
public BloggerAPI() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
postInterFace = retrofit.create(PostInterFace.class);
}
public static String getBaseUrl() {
return BASE_URL;
}
public static String getKEY() {
return KEY;
}
public static BloggerAPI getINSTANCE() {
if(INSTANCE == null){
INSTANCE = new BloggerAPI();
}
return INSTANCE;
}
public interface PostInterFace {
@GET
Call<PostList> getPostList(@Url String url);
}
public Call<PostList>getPosts(String url){
return postInterFace.getPostList(url);
}
}
我在 Mainctivity 中使用的这个 getData 方法来检索博客文章
public void getData() {
if (getItemsByLabelCalled) return;
progressBar.setVisibility(View.VISIBLE);
String url = BloggerAPI.getBaseUrl() + "?key=" + BloggerAPI.getKEY();
if (token != "") {
url = url + "&pageToken=" + token;
}
if (token == null) {
return;
}
final Call<PostList> postList = BloggerAPI.getINSTANCE().getPosts(url);
postList.enqueue(new Callback<PostList>() {
@Override
public void onResponse(@NonNull Call<PostList> call, @NonNull Response<PostList> response) {
if (response.isSuccessful()) {
progressBar.setVisibility(View.GONE);
PostList list = response.body();
Log.d(TAG, "onResponse: " + response.body());
if (list != null) {
token = list.getNextPageToken();
items.addAll(list.getItems());
adapter.notifyDataSetChanged();
for (int i = 0; i < items.size(); i++) {
items.get(i).setReDefinedID(i);
}
if (sqLiteItemsDBHelper == null || sqLiteItemsDBHelper.getAllItems().isEmpty()) {
SaveInDatabase task = new SaveInDatabase();
Item[] listArr = items.toArray(new Item[0]);
task.execute(listArr);
}
}
} else {
progressBar.setVisibility(View.GONE);
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.VISIBLE);
int sc = response.code();
switch (sc) {
case 400:
Log.e("Error 400", "Bad Request");
break;
case 404:
Log.e("Error 404", "Not Found");
break;
default:
Log.e("Error", "Generic Error");
}
}
}
@Override
public void onFailure(@NonNull Call<PostList> call, @NonNull Throwable t) {
Toast.makeText(MainActivity.this, "getData error occured", Toast.LENGTH_LONG).show();
Log.e(TAG, "onFailure: " + t.toString());
Log.e(TAG, "onFailure: " + t.getCause());
progressBar.setVisibility(View.GONE);
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.VISIBLE);
}
});
}
我创建了 PostsViewModel 来尝试实际思考如何迁移当前代码以使用 MVVM
public class PostsViewModel extends ViewModel {
public MutableLiveData<PostList> postListMutableLiveData = new MutableLiveData<>();
public void getData() {
String token = "";
// if (getItemsByLabelCalled) return;
// progressBar.setVisibility(View.VISIBLE);
String url = BloggerAPI.getBaseUrl() + "?key=" + BloggerAPI.getKEY();
if (token != "") {
url = url + "&pageToken=" + token;
}
if (token == null) {
return;
}
BloggerAPI.getINSTANCE().getPosts(url).enqueue(new Callback<PostList>() {
@Override
public void onResponse(Call<PostList> call, Response<PostList> response) {
postListMutableLiveData.setValue(response.body());
}
@Override
public void onFailure(Call<PostList> call, Throwable t) {
}
});
}
}
因此在 MainActivity 中使用
postsViewModel = ViewModelProviders.of(this).get(PostsViewModel.class);
postsViewModel.postListMutableLiveData.observe(this, postList -> {
items.addAll(postList.getItems());
adapter.notifyDataSetChanged();
});
现在使用这种方式的MVVM“ViewModel”有两个问题
- 首先在 MainActivity 中的当前 getData 方法中,它包含一些只能在 View 层工作的组件,如项目列表,recyclerView 需要设置
View.GONE以防响应不成功, progressBar,emptyView TextView,需要通知列表是否有变化的适配器,最后我需要上下文来使用创建 Toast 消息。
为了解决这个问题,我想将 UI 组件和其他东西添加到 ViewModel 类中并创建一个这样的构造函数
public class PostsViewModel extends ViewModel {
Context context;
List<Item> itemList;
PostAdapter postAdapter;
ProgressBar progressBar;
TextView textView;
public PostsViewModel(Context context, List<Item> itemList, PostAdapter postAdapter, ProgressBar progressBar, TextView textView) {
this.context = context;
this.itemList = itemList;
this.postAdapter = postAdapter;
this.progressBar = progressBar;
this.textView = textView;
}
但这在逻辑上与 MVVM 架构不符,并且肯定会导致内存泄漏,我将无法以这样的常规方式创建 ViewModel 的实例
postsViewModel = ViewModelProviders.of(this).get(PostsViewModel.class);
postsViewModel.postListMutableLiveData.observe(this, postList -> {
items.addAll(postList.getItems());
adapter.notifyDataSetChanged();
});
而且必须这样使用
postsViewModel = new PostsViewModel(this,items,adapter,progressBar,emptyView);
所以第一个问题是如何将这些 UI 组件与 ViewModel 绑定?
- 第二个在当前getata我使用了SaveInDatabase类使用AsyncTask的方式来保存SQLite数据库中的所有项目第二个问题是如何移动此类以使用 ViewModel?但它也需要在 View 层工作以避免泄漏
SaveInDatabase 类
static class SaveInDatabase extends AsyncTask<Item, Void, Void> {
@Override
protected Void doInBackground(Item... items) {
List<Item> itemsList = Arrays.asList(items);
// runtimeExceptionDaoItems.create(itemsList);
for (int i = 0 ; i< itemsList.size();i++) {
sqLiteItemsDBHelper.addItem(itemsList.get(i));
Log.e(TAG, "Size :" + sqLiteItemsDBHelper.getAllItems().size());
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
}
【问题讨论】:
-
这会有帮助吗? medium.com/@sreeharikv112/…
标签: java android mvvm android-asynctask retrofit