同步AsyncTasks

时间:2018-12-14 19:56:50

标签: android multithreading

我正在使用内存数据库,并执行一些简单的任务,例如写一些电影,然后读出并显示它们。我正在使用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())。有解决办法吗?

1 个答案:

答案 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,并在有新数据可显示时得到通知。