Поскольку вы используете MVVM впервые, мы можем постараться сделать все просто.
[ Просмотр Компонент C] ---- ( наблюдает ) [ ViewModel Компонент B] ---- [ Репозиторий]
В соответствии с правилом разделения проблем ViewModel должен предоставлять LiveData.LiveData использует Observers для наблюдения за изменениями данных.Целью ViewModel является отделение слоя данных от пользовательского интерфейса.ViewModel не должен знать о классах платформы Android.
В архитектуре MVVM роль ViewModel заключается в получении данных из репозитория.Вы можете либо сохранить свой json-файл в качестве локального источника данных, используя Room, либо сохранить Json API в качестве удаленного источника данных.В любом случае, общая реализация выглядит следующим образом:
Компонент A - объект (реализует ваши методы получения и установки)
Метод 1: Использование комнаты
@Entity(tableName = "file")
public class FileEntry{
@PrimaryKey(autoGenerate = true)
private int id;
private String content; // member variables
public FileEntry(String content){ // constructor
this.id = id;
this.content = content;
}
public int getId(){ // getter methods
return id;
}
public void setId(int id){ // setter methods
this.id = id;
}
public String getContent(){
return content;
}
public void setContent(String content){
this.content = content;
}
}
Метод 2: Использование удаленного источника данных
public class FileEntry implements Serializable{
public String getContent(){
return content;
}
private String content;
}
Компонент B - ViewModel (Уровень представления)
Метод 1: Использование комнаты
Когда вы спросили о том, как может передаваться контекст Android, вы можете сделатьпоэтому, расширив AndroidViewModel, как показано ниже, включить ссылку на приложение.Это если вашей базе данных требуется контекст приложения, но общее правило состоит в том, что Activity & Fragments не должны храниться во ViewModel.
Предположим, у вас есть "файлы" в качестве переменной-члена, определенной для вашего списка объектов, скажем, в этом случае, объекты "FileEntry":
public class FileViewModel extends AndroidViewModel{
// Wrap your list of FileEntry objects in LiveData to observe data changes
private LiveData<List<FileEntry>> files;
public FileViewModel(Application application){
super(application);
FilesDatabase db = FilesDatabase.getInstance(this.getApplication());
Метод 2: Использование удаленного источника данных
public class FileViewModel extends ViewModel{
public FileViewModel(){}
public LiveData<List<FileEntry>> getFileEntries(String content){
Repository repository = new Repository();
return repository.getFileEntries(content);
}
}
В этом случае метод getFileEntries содержит MutableLiveData:
final MutableLiveData<List<FileEntry>> mutableLiveData = new MutableLiveData<>();
Если вы реализуете клиент Retrofit, вы можете сделать что-нибудьаналогично приведенному ниже коду с использованием асинхронных обратных вызовов.Код был взят из Руководство по модернизации 2 в Future Studio с некоторыми изменениями для этого примера обсуждения.
// asynchronous
call.enqueue(new Callback<ApiData>() {
@Override
public void onResponse(Call<ApiData> call, Response<ApiData> response) {
if (response.isSuccessful()) {
mutableLiveData.setValue(response.body().getContent());
} else {
int statusCode = response.code();
// handle request errors yourself
ResponseBody errorBody = response.errorBody();
}
}
@Override
public void onFailure(Call<ApiData> call, Throwable t) {
// handle execution failures like no internet connectivity
}
return mutableLiveData;
Компонент C - представление (UI Controller)
Используете ли вы метод 1 или 2, вы можете сделать:
FileViewModel fileViewModel = ViewModelProviders.of(this).get(FileViewModel.class);
fileViewModel.getFileEntries(content).observe(this, fileObserver);
Надеюсь, что это полезно.
Влияние на производительность
По моему мнению, решение о том, какой метод использовать, может зависеть от того, сколько вызовов данных вы реализуете.Если несколько, Retrofit может быть лучшей идеей для упрощения вызовов API.Если вы реализуете его с помощью клиента Retrofit, у вас может быть что-то похожее на приведенный ниже код, взятый из этой справочной статьи в Руководстве по Android для архитектуры приложений :
public LiveData<User> getUser(int userId) {
LiveData<User> cached = userCache.get(userId);
if (cached != null) {
return cached;
}
final MutableLiveData<User> data = new MutableLiveData<>();
userCache.put(userId, data);
webservice.getUser(userId).enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
data.setValue(response.body());
}
});
return data;
}
Возможно, приведенная выше реализацияПреимущества производительности многопоточности, поскольку Retrofit позволяет выполнять асинхронные сетевые вызовы с использованием enqueue
и возвращать метод onResponse
в фоновом потоке.Используя метод 2, вы можете использовать шаблон обратного вызова Retrofit для сетевых вызовов в параллельных фоновых потоках, не вмешиваясь в основной поток пользовательского интерфейса.
Другое преимущество вышеописанной реализации состоит в том, что если вы делаете несколько вызовов API-данных, вы можете получить чистый ответ через интерфейс webservice
для вашей LiveData.Это позволяет нам опосредовать ответы между различными источниками данных.Затем вызов data.setValue
устанавливает значение MutableLiveData, а затем отправляет его активным наблюдателям в основном потоке в соответствии с документацией Android.
Если вы уже знакомы с SQL и реализуете только 1 базу данных, выбор библиотеки постоянных номеров может быть хорошим вариантом.Он также использует ViewModel, что дает преимущества в производительности, поскольку вероятность утечек памяти снижается, поскольку ViewModel поддерживает меньше надежных ссылок между вашим пользовательским интерфейсом и классами данных.
Одной из проблем может быть ваш репозиторий БД (например, FilesDatabase
, реализованный как одиночный, для обеспечения единой глобальной точки доступа, с использованием открытого статического метода для создания экземпляра класса, так что только 1 тот же экземплярбазы данных открыты в любое время? Если да, синглтон может быть ограничен областью приложения, и если пользователь все еще запускает приложение, ViewModel может быть утечка. Таким образом, убедитесь, что ваша ViewModel использует LiveData для ссылки наПредставления. Также может быть полезно использовать отложенную инициализацию, чтобы новый экземпляр класса FilesDatabase
singleton создавался с использованием метода getInstance
, если еще не создано ни одного предыдущего экземпляра:
private static FilesDatabase dbInstance;
// Synchronized may be an expensive operation but ensures only 1 thread runs at a time
public static synchronized FilesDatabase getInstance(Context context) {
if (dbInstance == null) {
// Creates the Room persistent database
dbInstance = Room.databaseBuilder(context.getApplicationContext(), FilesDatabase.class, FilesDatabase.DATABASE_NAME)
ДругойДело в том, что независимо от того, выбрали ли вы действие или фрагмент для своего пользовательского интерфейса, вы будете использовать ViewModelProviders.of
для сохранения вашей ViewModel, когда область действия действия или фрагмента активна. Если вы реализуете различные действия / фрагменты, у вас будут разныеэкземпляры ViewModel в вашем приложении.
Еслинапример, вы реализуете свою базу данных с помощью Room и хотите, чтобы ваш пользователь мог обновлять вашу базу данных во время использования вашего приложения, теперь вашему приложению может потребоваться один и тот же экземпляр ViewModel для всей вашей основной деятельности и операции обновления.Хотя это анти-шаблон, ViewModel предоставляет простую фабрику с пустым конструктором.Вы можете реализовать его в Room, используя public class UpdateFileViewModelFactory extends ViewModelProvider.NewInstanceFactory{
:
@Override
public <T extends ViewModel> T create(@NotNull Class<T> modelClass) {
return (T) new UpdateFileViewModel(sDb, sFileId);
Выше T является параметром типа create.В приведенном выше фабричном методе класс T расширяет ViewModel.Переменная-член sDb предназначена для FilesDatabase, а sFileId - для идентификатора int, который представляет каждый FileEntry.
Эта статья в разделе «Постоянные данные» от Android может быть более полезной, чем мои комментарии, если вы хотите узнать больше о затратах на производительность.