在 ViewModel 中,我们如何 "assign" 从 Room 到 MutableLiveData 的 LiveData
How can we "assign" LiveData from Room to MutableLiveData, within ViewModel
最近,我被下面的代码卡住了。
public class NoteViewModel extends ViewModel {
private final MutableLiveData<List<Note>> notesLiveData = new MutableLiveData<>();
public NoteViewModel() {
LiveData<List<Note>> notesLiveDataFromRepository = NoteRepository.INSTANCE.getNotes();
// How can I "assign" LiveData from Room, to MutableLiveData?
}
}
我在想,我怎样才能"assign"LiveData
从Room
,变成MutableLiveData
?
使用 Transformation.map
和 Transformation.switchMap
是行不通的,因为两者都是 returns LiveData
,而不是 MutableLiveData
.
可能的解决方法
一种可能的解决方案是,而不是
@Dao
public abstract class NoteDao {
@Transaction
@Query("SELECT * FROM plain_note")
public abstract LiveData<List<Note>> getNotes();
我会用
@Dao
public abstract class NoteDao {
@Transaction
@Query("SELECT * FROM plain_note")
public abstract List<Note> getNotes();
然后,在我的ViewModel
里,我会写
public class NoteViewModel extends ViewModel {
private final MutableLiveData<List<Note>> notesLiveData = new MutableLiveData<>();
public NoteViewModel() {
new Thread(() -> {
List<Note> notesLiveDataFromRepository = NoteRepository.INSTANCE.getNotes();
notesLiveData.postValue(notesLiveDataFromRepository);
}).start();
}
}
我不太喜欢这种方法,因为我不得不显式地处理线程问题。
是否有更好的方法来避免显式处理线程?
诀窍是不在视图模型中进行任何实际提取。
无论是从网络还是数据库获取数据,都应该在存储库中完成。 ViewModel 在这方面应该是不可知的。
在 ViewModel 中,使用 LiveData class,而不是 MutableLiveData。除非你真的找到它的用例。
// In your constructor, no extra thread
notesLiveData = notesLiveDataFromRepository.getAllNotes();
然后在您的存储库中,您可以在 getAllNotes() 方法中使用逻辑来确定这些注释的来源。在存储库中,您有 MutableLiveData。然后,您可以从获取数据的线程向其发布值。不过,这不是房间所必需的,而是为您处理的。
因此,在您的存储库中,您将返回另一个由 DAO 方法直接支持的 LiveData。
在这种情况下,您需要坚持使用 public abstract LiveData<List<Note>> getNotes();
。
Activity
public class MyActivity extends AppCompatActivity {
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up your view model
viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
// Observe the view model
viewModel.getMyLiveData().observe(this, s -> {
// You work with the data provided through the view model here.
// You should only really be delivering UI updates at this point. Updating
// a RecyclerView for example.
Log.v("LIVEDATA", "The livedata changed: "+s);
});
// This will start the off-the-UI-thread work that we want to perform.
MyRepository.getInstance().doSomeStuff();
}
}
视图模型
public class MyViewModel extends AndroidViewModel {
@NonNull
private MyRepository repo = MyRepository.getInstance();
@NonNull
private LiveData<String> myLiveData;
public MyViewModel(@NonNull Application application) {
super(application);
// The local live data needs to reference the repository live data
myLiveData = repo.getMyLiveData();
}
@NonNull
public LiveData<String> getMyLiveData() {
return myLiveData;
}
}
存储库
public class MyRepository {
private static MyRepository instance;
// Note the use of MutableLiveData, this allows changes to be made
@NonNull
private MutableLiveData<String> myLiveData = new MutableLiveData<>();
public static MyRepository getInstance() {
if(instance == null) {
synchronized (MyRepository.class) {
if(instance == null) {
instance = new MyRepository();
}
}
}
return instance;
}
// The getter upcasts to LiveData, this ensures that only the repository can cause a change
@NonNull
public LiveData<String> getMyLiveData() {
return myLiveData;
}
// This method runs some work for 3 seconds. It then posts a status update to the live data.
// This would effectively be the "doInBackground" method from AsyncTask.
public void doSomeStuff() {
new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {
}
myLiveData.postValue("Updated time: "+System.currentTimeMillis());
}).start();
}
}
很简单。
class MainViewModel: ViewModel() {
@Inject lateinit var currencyRepository: CurrencyRepository
val notifyCurrencyList = MediatorLiveData<List<Currency?>>()
init {
CurrencyApplication.component.inject(this)
}
fun loadCurrencyList() {
notifyCurrencyList.addSource(currencyRepository.loadLatestRates()) {
notifyCurrencyList.value = it
}
}
}
最近,我被下面的代码卡住了。
public class NoteViewModel extends ViewModel {
private final MutableLiveData<List<Note>> notesLiveData = new MutableLiveData<>();
public NoteViewModel() {
LiveData<List<Note>> notesLiveDataFromRepository = NoteRepository.INSTANCE.getNotes();
// How can I "assign" LiveData from Room, to MutableLiveData?
}
}
我在想,我怎样才能"assign"LiveData
从Room
,变成MutableLiveData
?
使用 Transformation.map
和 Transformation.switchMap
是行不通的,因为两者都是 returns LiveData
,而不是 MutableLiveData
.
可能的解决方法
一种可能的解决方案是,而不是
@Dao
public abstract class NoteDao {
@Transaction
@Query("SELECT * FROM plain_note")
public abstract LiveData<List<Note>> getNotes();
我会用
@Dao
public abstract class NoteDao {
@Transaction
@Query("SELECT * FROM plain_note")
public abstract List<Note> getNotes();
然后,在我的ViewModel
里,我会写
public class NoteViewModel extends ViewModel {
private final MutableLiveData<List<Note>> notesLiveData = new MutableLiveData<>();
public NoteViewModel() {
new Thread(() -> {
List<Note> notesLiveDataFromRepository = NoteRepository.INSTANCE.getNotes();
notesLiveData.postValue(notesLiveDataFromRepository);
}).start();
}
}
我不太喜欢这种方法,因为我不得不显式地处理线程问题。
是否有更好的方法来避免显式处理线程?
诀窍是不在视图模型中进行任何实际提取。
无论是从网络还是数据库获取数据,都应该在存储库中完成。 ViewModel 在这方面应该是不可知的。
在 ViewModel 中,使用 LiveData class,而不是 MutableLiveData。除非你真的找到它的用例。
// In your constructor, no extra thread
notesLiveData = notesLiveDataFromRepository.getAllNotes();
然后在您的存储库中,您可以在 getAllNotes() 方法中使用逻辑来确定这些注释的来源。在存储库中,您有 MutableLiveData。然后,您可以从获取数据的线程向其发布值。不过,这不是房间所必需的,而是为您处理的。
因此,在您的存储库中,您将返回另一个由 DAO 方法直接支持的 LiveData。
在这种情况下,您需要坚持使用 public abstract LiveData<List<Note>> getNotes();
。
Activity
public class MyActivity extends AppCompatActivity {
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up your view model
viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
// Observe the view model
viewModel.getMyLiveData().observe(this, s -> {
// You work with the data provided through the view model here.
// You should only really be delivering UI updates at this point. Updating
// a RecyclerView for example.
Log.v("LIVEDATA", "The livedata changed: "+s);
});
// This will start the off-the-UI-thread work that we want to perform.
MyRepository.getInstance().doSomeStuff();
}
}
视图模型
public class MyViewModel extends AndroidViewModel {
@NonNull
private MyRepository repo = MyRepository.getInstance();
@NonNull
private LiveData<String> myLiveData;
public MyViewModel(@NonNull Application application) {
super(application);
// The local live data needs to reference the repository live data
myLiveData = repo.getMyLiveData();
}
@NonNull
public LiveData<String> getMyLiveData() {
return myLiveData;
}
}
存储库
public class MyRepository {
private static MyRepository instance;
// Note the use of MutableLiveData, this allows changes to be made
@NonNull
private MutableLiveData<String> myLiveData = new MutableLiveData<>();
public static MyRepository getInstance() {
if(instance == null) {
synchronized (MyRepository.class) {
if(instance == null) {
instance = new MyRepository();
}
}
}
return instance;
}
// The getter upcasts to LiveData, this ensures that only the repository can cause a change
@NonNull
public LiveData<String> getMyLiveData() {
return myLiveData;
}
// This method runs some work for 3 seconds. It then posts a status update to the live data.
// This would effectively be the "doInBackground" method from AsyncTask.
public void doSomeStuff() {
new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {
}
myLiveData.postValue("Updated time: "+System.currentTimeMillis());
}).start();
}
}
很简单。
class MainViewModel: ViewModel() {
@Inject lateinit var currencyRepository: CurrencyRepository
val notifyCurrencyList = MediatorLiveData<List<Currency?>>()
init {
CurrencyApplication.component.inject(this)
}
fun loadCurrencyList() {
notifyCurrencyList.addSource(currencyRepository.loadLatestRates()) {
notifyCurrencyList.value = it
}
}
}