我正在使用内存数据库,并执行一些简单的任务,例如写一些电影,然后读出并显示它们。我正在使用RoomPersistance,并且已经设置了一些存储库。我的问题:
在这里,我从响应中获取电影,并通过insertMovie方法将它们插入数据库中。
for (OMDBItem movie : response.body().getItems())
{
omdbItemRepository.insertMovie(movie);
}
此方法如下:
public void insertMovie(OMDBItem movie){
AsyncTask<Void,Void,Void> atask = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
movieDataBase.movieDao().insert(movie);
return null;
}
}.execute();
}
然后我有这段代码:
for (OMDBItem movie : response.body().getItems())
{
omdbItemRepository.insertMovie(movie);
}
lista_filmovi=omdbItemRepository.getAllMovies();
和getAllMovies()是类似的方法,如下所示:
public List<OMDBItem> getAllMovies(){
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... voids) {
lista_filmovi=movieDataBase.movieDao().getAllMovies();
return null;
}
}.execute();
return lista_filmovi;
}
问题在于,有时,此方法getAllMovies返回我想要的电影,但有时仅返回null。而且,当我放置一些断点并在调试器中运行它时,它仅返回电影。我的问题是,通过在调试器中运行它并单击方法,我给了insertMovie(movie)AsyncTasks更多的时间来完成其工作,并且在调用getAllMovies()时,它给了我很好的结果。因此,基本上的问题是,有没有办法让insertMovie()AsyncTasks完成后才启动getAllMovies()AsyncTask。我知道我可以将onPostExecute放在insertMovie()中,但是我想将这些方法分开(我不想在insertMovie()之后每次都调用getAllMovies())。有解决办法吗?
答案 0 :(得分:1)
您的代码中有一些问题。
首先,您需要等待所有电影都写入数据库才能开始回读。然后,在读取中您不能仅返回lista_filmovi
的值,因为读取将是异步的,因此当您尝试读取它时返回的值将不存在。
编写电影的异步任务示例可以是:
public static class InsertMovie extends AsyncTask<OMDBItem,Void,Void> {
private final Database movieDataBase;
public InsertMovie(Database movieDataBase) {
this.movieDataBase = movieDataBase;
}
@Override
protected Void doInBackground(OMDBItem... movies) {
for (OMDBItem movie : movies)
movieDataBase.movieDao().insert(movie);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
// data is stored
}
}
要编写电影,请使用以下语句:
new InsertMovie(movieDataBase).execute(movies);
在调用OnPostExecute
之前,您不得尝试读取数据。有多种方法可以做到这一点,但更简单的方法就是从那里开始阅读。
然后再读一遍:
public static class GetAllMovies extends AsyncTask<Void,Void,List<OMDBItem>> {
private final Database movieDataBase;
public GetAllMovies(Database movieDataBase) {
this.movieDataBase = movieDataBase;
}
@Override
protected List<OMDBItem> doInBackground(Void... voids) {
return movieDataBase.movieDao().getAllMovies();
}
@Override
protected void onPostExecute(List<OMDBItem> allMovies) {
// post the result to your activity
}
}
同样,结果将在OnPostExecute
中可用,并且在调用该方法之前您无法访问它。
因此,将其适应您的活动的最佳方法有所不同。我建议使用AndroidViewModel
并以LiveData
对象的通知形式得到结果。在这种情况下,您甚至不需要使用AsyncTask,因为您只需将结果发布到LiveData
中即可。
从AndroidViewModel
开始,如下所示:
/** ViewModel providing additional features to ease Room DB access */
public class RoomViewModel extends AndroidViewModel {
/** Thread executing Room operations */
private static class RoomThread extends Thread {
/** Queue of tasks pending execution */
private BlockingQueue<Runnable> tasks = new LinkedBlockingQueue<>();
/** Set to false to stop */
private boolean running = true;
/** Send this to stop the execution */
private Runnable STOP = new Runnable() {
@Override
public void run() {
running = false;
}
};
@Override
public void run()
{
while (running) {
try {
// execute next in line, when available
tasks.take().run();
} catch (InterruptedException e) {
// this should not happen
return;
}
}
}
}
/** Executes backround Room requests in order */
private RoomThread roomThread = new RoomThread();
public RoomViewModel(@NonNull Application application) {
super(application);
// start the background execution thread
roomThread.start();
}
/**
* Queues the specified Runnable for execution
* @param runnable The Runnable to be executed in background
*/
protected void execute(Runnable runnable)
{
roomThread.tasks.offer(runnable);
}
@Override
protected void onCleared() {
// queue the stop request
execute(roomThread.STOP);
super.onCleared();
}
}
这将为您提供帮助,因为您只有一个后台线程可以访问数据库,因此可以对操作进行排序。
在扩展MovieViewModel
的{{1}}中,您可以使用:
RoomViewModel
和
// write the movies
execute(new Runnable() {
@Override
public void run() {
for (OMDBItem movie : movies) movieDataBase.movieDao().insert(movie);
}
});
在“活动”中,您可以将// read the movies
execute(new Runnable() {
@Override
public void run() {
allMovies.postValue(movieDataBase.movieDao().getAllMovies());
}
});
视作allMovies
,并在有新数据可显示时得到通知。