[英]How can I use MVVM with the UI components of the App/activity and AsyncTask
As I know that the ViewModel should be secluded from the UI/View and contains only the logic that observes the data that's coming from the server or database据我所知,ViewModel 应该与 UI/View 隔离开来,并且只包含观察来自服务器或数据库的数据的逻辑
In my App, I used REST API "retrofit" and blogger API and I tried to migrate/upgrade the current code to MVVM but there are a few problems, let's go to the code In my App, I used REST API "retrofit" and blogger API and I tried to migrate/upgrade the current code to MVVM but there are a few problems, let's go to the code
BloggerAPI Class BloggerAPI Class
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);
}
}
this getData method I used in the Mainctivity to retrieve blog posts我在 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);
}
});
}
I created the PostsViewModel to trying to think practically how to migrate the current code to use MVVM我创建了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) {
}
});
}
}
and it's used thus in MainActivity因此在 MainActivity 中使用它
postsViewModel = ViewModelProviders.of(this).get(PostsViewModel.class);
postsViewModel.postListMutableLiveData.observe(this, postList -> {
items.addAll(postList.getItems());
adapter.notifyDataSetChanged();
});
now there are two problems using this way of MVVM "ViewModel"现在使用这种方式的MVVM“ViewModel”有两个问题
View.GONE
in case of response unsuccessful, progressBar, emptyView TextView, the adapter that needs to notify if there are changes in the list, and finally I need the context to used the create the Toast messages.View.GONE
以防响应不成功,progressBar,emptyView TextView,需要的适配器通知列表中是否有更改,最后我需要上下文来使用创建 Toast 消息。 To solve this issue I think to add the UI components and other things into the ViewModel Class and create a constructor like this为了解决这个问题,我认为将 UI 组件和其他内容添加到 ViewModel Class 并创建一个像这样的构造函数
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;
}
but this is not logically with MVVM arch and for sure cause memory leaking also I will not be able to create the instance of ViewModel with regular way like this但这在逻辑上与 MVVM 架构不符,并且肯定会导致 memory 泄漏,我也将无法以这样的常规方式创建 ViewModel 的实例
postsViewModel = ViewModelProviders.of(this).get(PostsViewModel.class);
postsViewModel.postListMutableLiveData.observe(this, postList -> {
items.addAll(postList.getItems());
adapter.notifyDataSetChanged();
});
and must be used like this并且必须像这样使用
postsViewModel = new PostsViewModel(this,items,adapter,progressBar,emptyView);
so the first question is How to bind these UI components with the ViewModel?所以第一个问题是如何将这些 UI 组件与 ViewModel 绑定?
the SaveInDatabase Class SaveInDatabase Class
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);
}
}
Actually the question is too broad to answer because there are many ways to implement for this case.实际上这个问题太宽泛而无法回答,因为这种情况有很多方法可以实现。 First of all, never pass view objects to viewModel.
首先,永远不要将视图对象传递给 viewModel。 ViewModel is used to notify changes to ui layer with LiveData or rxJava without retaining the view instance.
ViewModel 用于通过 LiveData 或 rxJava 通知 ui 层的更改,而不保留视图实例。 You may try this way.
你可以试试这个方法。
class PostViewModel extends ViewModel {
private final MutableLiveData<PostList> postListLiveData = new MutableLiveData<PostList>();
private final MutableLiveData<Boolean> loadingStateLiveData = new MutableLiveData<Boolean>();
private String token = "";
public void getData() {
loadingStateLiveData.postValue(true);
// 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) {
loadingStateLiveData.postValue(false);
postListLiveData.setValue(response.body());
token = response.body().getNextPageToken(); //===> the token
}
@Override
public void onFailure(Call<PostList> call, Throwable t) {
loadingStateLiveData.postValue(false);
}
});
}
public LiveData<PostList> getPostListLiveData(){
return postListLiveData;
}
public LiveData<Boolean> getLoadingStateLiveData(){
return loadingStateLiveData;
}
}
and you may observe the changes from your activity like this.你可以像这样观察你的活动的变化。
postsViewModel = ViewModelProviders.of(this).get(PostsViewModel.class);
postsViewModel.getPostListLiveData().observe(this,postList->{
if(isYourPostListEmpty(postlist)) {
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.VISIBLE);
items.addAll(postList.getItems());
adapter.notifyDataSetChanged();
}else {
recyclerView.setVisibility(View.VISIBLE);
emptyView.setVisibility(View.GONE);
}
});
postsViewModel.getLoadingStateLiveData().observe(this,isLoading->{
if(isLoading) {
progressBar.setVisibility(View.VISIBLE);
}else {
progressBar.setVisibility(View.GONE);
}
});
For my personal prefer, I like using Enum for error handling, but I can't post here as it will make the answer very long.对于我个人的偏好,我喜欢使用 Enum 进行错误处理,但我不能在这里发布,因为它会使答案变得很长。 For your second question, use Room from google.
对于第二个问题,请使用 Google 的Room 。 It will make you life a lot easier.
它会让你的生活轻松很多。 It work very well with mvvm and it natively support liveData.
它与 mvvm 一起工作得很好,并且它原生支持 liveData。 You can try CodeLab from google to practise using room.
你可以试试谷歌的CodeLab来练习使用房间。
Bonus : You don't need to edit the url like this:奖励:您不需要像这样编辑 url :
String url = BloggerAPI.getBaseUrl() + "?key=" + BloggerAPI.getKEY();
if (token != "") {
url = url + "&pageToken=" + token;
}
You can use @Path or @query based on your requirements.您可以根据需要使用@Path或@query 。
As your question is bit broad, I am not giving any source code for the same, Rather mentioning samples which clearly resolves issues mentioned with MVVM .由于您的问题有点宽泛,我没有提供任何相同的源代码,而是提到了可以清楚地解决MVVM提到的问题的示例。
Clean Code Architecture can be followed which will clearly separate the responsibilities of each layer.可以遵循干净的代码架构,这将清楚地分离每一层的职责。
First of all application architecture needs to be restructured so that each layer has designated role in MVVM.首先需要重构应用程序架构,以便每一层在 MVVM 中都有指定的角色。 You can follow the following pattern for the same.
您可以按照以下模式进行相同的操作。
All these points (except Database part) are covered over Medium Article , were each step is covered with actual API's.所有这些点(除了数据库部分)都包含在Medium Article中,每个步骤都包含实际的 API。 Along with that unit test is also covered.
与该单元测试一起,也包括在内。
Libraries used are in this project are在这个项目中使用的库是
Full Source code can be found over Github完整的源代码可以在Github上找到
Kotlin is the official supported language for Android Development now. Kotlin 是 Android 开发的官方支持语言。 I suggest you should lean and migrate your java android projects to Kotlin.
我建议您应该精简并将您的 java android 项目迁移到 Kotlin。
Still for converting Kotlin to Java, Go to Menu
> Tools
> Kotlin
> Decompile Kotlin to Java
Option Still for converting Kotlin to Java, Go to
Menu
> Tools
> Kotlin
> Decompile Kotlin to Java
Option
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.