Насколько я знаю, ViewModel должен быть изолирован от UI / View и содержит только logi c, который наблюдает за данными, поступающими с сервера или базы данных
В моем приложении я использовал REST API "retrofit" и API blogger, и я попытался перенести / обновить текущий код до MVVM, но есть несколько проблем, давайте go к коду
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 метод, который я использовал в Mainctivity для получения сообщений в блоге
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"
- сначала в текущем методе getData в MainActivity, он содержит некоторые компоненты, которые должны работать только на уровне просмотра, например, список элементов, recyclerView должен установить
View.GONE
в случае неудачного ответа sful, progressBar, emptyView TextView, адаптер, который должен уведомлять, есть ли изменения в списке, и, наконец, мне нужен контекст для создания сообщений Toast.
Думаю, что для решения этой проблемы чтобы добавить компоненты пользовательского интерфейса и другие вещи в класс 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);
, поэтому первый вопрос: как связать эти компоненты пользовательского интерфейса с ViewModel?
секунда в текущем
getata Я использовал класс SaveInDatabase, использую
AsyncTask способ сохранить все элементы в базе данных
SQLite , второй вопрос: как переместить этот класс для работы с ViewModel? но он также должен работать на уровне просмотра, чтобы избежать утечки
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);
}
}