如何从LiveData中获取值?

12

我第一次使用Room。我正在研究LiveData的概念。我知道我们可以将记录从数据库中获取到LiveData,并附加haveObservers。

@Query("SELECT * FROM users")

<LiveData<List<TCUser>> getAll();

但我在后台执行同步,需要从服务器获取数据并与名为“users”的RoomDatabase表中的数据进行比较,然后从用户表中插入、更新或删除数据。在采取任何操作之前,如何遍历LiveData列表?如果将其放入for循环中,会出现错误。

或者,我在这种情况下不应该使用LiveData吗?

我想我需要调用

<LiveData<List<TCUser>> getAll().getValue()

但这样做是否正确呢?这里有更多的代码,以便让您了解我试图做什么:

List<User>serverUsers: Is the data received from a response from an API

private void updateUsers(List<User> serverUsers) {
    List<UserWithShifts> users = appDatabase.userDao().getAllUsers();
    HashMap<String, User> ids = new HashMap();
    HashMap<String, User> newIds = new HashMap();

    if (users != null) {
        for (UserWithShifts localUser : users) {
            ids.put(localUser.user.getId(), localUser.user);
        }
    }

    for (User serverUser : serverUsers) {
        newIds.put(serverUser.getId(), serverUser);

        if (!ids.containsKey(serverUser.getId())) {
            saveShiftForUser(serverUser);
        } else {
            User existingUser = ids.get(serverUser.getId());
            //If server data is newer than local
            if (DateTimeUtils.isLaterThan(serverUser.getUpdatedAt(), existingUser.getUpdatedAt())) {
                deleteEventsAndShifts(serverUser.getId());
                saveShiftForUser(serverUser);
            }
        }
    }

其中:

@Query("SELECT * FROM users")
List<UserWithShifts> getAllUsers();

updateUsers()函数中第一行提取数据的方式是否正确,应该使用哪种方法?

<LiveData<List<User>> getAll().getValue()
谢谢。

你能否稍微梳理一下你的问题,更清晰地说明你要将LiveData引入哪个架构中? - KG6ZVP
或者只需创建一个查询,检查用户是否存在,而不是返回整个用户行? - Enzokie
@Sneha:请标记答案为已接受或澄清问题。 - KG6ZVP
See edited answer - KG6ZVP
1个回答

6

如果我正确理解你的架构,updateUsers是在AsyncTask或类似的任务中。

这是我提出的方法,它涉及调整Dao以实现最大效益。你编写了很多代码来做出决策,而你可以让数据库来做。

这也不是紧凑或高效的代码,但我希望它能展示这些库更有效的使用方式。

后台线程(IntentService、AsyncTask等):

/*
 * assuming this method is executing on a background thread
 */
private void updateUsers(/* from API call */List<User> serverUsers) {
    for(User serverUser : serverUsers){
        switch(appDatabase.userDao().userExistsSynchronous(serverUser.getId())){
            case 0: //doesn't exist
                saveShiftForUser(serverUser);
            case 1: //does exist
                UserWithShifts localUser = appDatabase.userDao().getOldUserSynchronous(serverUser.getId(), serverUser.getUpdatedAt());
                if(localUser != null){ //there is a record that's too old
                    deleteEventsAndShifts(serverUser.getId());
                    saveShiftForUser(serverUser);
                }
            default: //something happened, log an error
        }
    }
}

如果在UI线程(Activity、Fragment、Service)上运行:
/*
 * If you receive the IllegalStateException, try this code
 *
 * NOTE: This code is not well architected. I would recommend refactoring if you need to do this to make things more elegant.
 *
 * Also, RxJava is better suited to this use case than LiveData, but this may be easier for you to get started with
 */
private void updateUsers(/* from API call */List<User> serverUsers) {
    for(User serverUser : serverUsers){
        final LiveData<Integer> userExistsLiveData = appDatabase.userDao().userExists(serverUser.getId());
        userExistsLiveData.observe(/*activity or fragment*/ context, exists -> {
            userExistsLiveData.removeObservers(context); //call this so that this same code block isn't executed again. Remember, observers are fired when the result of the query changes.
            switch(exists){
                case 0: //doesn't exist
                    saveShiftForUser(serverUser);
                case 1: //does exist
                    final LiveData<UserWithShifts> localUserLiveData = appDatabase.userDao().getOldUser(serverUser.getId(), serverUser.getUpdatedAt());
                    localUserLiveData.observe(/*activity or fragment*/ context, localUser -> { //this observer won't be called unless the local data is out of date
                        localUserLiveData.removeObservers(context); //call this so that this same code block isn't executed again. Remember, observers are fired when the result of the query changes.
                        deleteEventsAndShifts(serverUser.getId());
                        saveShiftForUser(serverUser);
                    });
                default: //something happened, log an error
            }
        });
    }
}

您需要根据自己决定使用的方法修改Dao

@Dao
public interface UserDao{
    /*
     * LiveData should be chosen for most use cases as running on the main thread will result in the error described on the other method
     */
    @Query("SELECT * FROM users")
    LiveData<List<UserWithShifts>> getAllUsers();

    /*
     * If you attempt to call this method on the main thread, you will receive the following error:
     *
     * Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long periods of time.
     *  at android.arch.persistence.room.RoomDatabase.assertNotMainThread(AppDatabase.java:XXX)
     *  at android.arch.persistence.room.RoomDatabase.query(AppDatabase.java:XXX)
     *
     */
    @Query("SELECT * FROM users")
    List<UserWithShifts> getAllUsersSynchronous();

    @Query("SELECT EXISTS (SELECT * FROM users WHERE id = :id)")
    LiveData<Integer> userExists(String id);

    @Query("SELECT EXISTS (SELECT * FROM users WHERE id = :id)")
    Integer userExistsSynchronous(String id);

    @Query("SELECT * FROM users WHERE id = :id AND updatedAt < :updatedAt LIMIT 1")
    LiveData<UserWithShifts> getOldUser(String id, Long updatedAt);

    @Query("SELECT * FROM users WHERE id = :id AND updatedAt < :updatedAt LIMIT 1")
    UserWithShifts getOldUserSynchronous(String id, Long updatedAt);
}

这能解决你的问题吗?

注意:我没有看到你的 saveShiftForUserdeleteEventsAndShifts 方法。插入、保存和更新都是由 Room 同步执行的。如果你在主线程上运行任一方法(我猜测这就是你的错误来自的地方),应该创建一个从 appDatabase 返回的 daoWrapper,如下所示:

public class UserDaoWrapper {
    private final UserDao userDao;

    public UserDaoWrapper(UserDao userDao) {
        this.userDao = userDao;
    }

    public LiveData<Long[]> insertAsync(UserWithShifts... users){
        final MutableLiveData<Long[]> keys = new MutableLiveData<>();
        HandlerThread ht = new HandlerThread("");
        ht.start();
        Handler h = new Handler(ht.getLooper());
        h.post(() -> keys.postValue(userDao.insert(users)));
        return keys;
    }

    public void updateAsync(UserWithShifts...users){
        HandlerThread ht = new HandlerThread("");
        ht.start();
        Handler h = new Handler(ht.getLooper());
        h.post(() -> {
            userDao.update(users);
        });
    }

    public void deleteAsync(User... users){
        HandlerThread ht = new HandlerThread("");
        ht.start();
        Handler h = new Handler(ht.getLooper());
        h.post(() -> {
            for(User e : users)
                userDao.delete(e.getId());
        });
    }
}

除非我处理数据,否则我不会将数据插入数据库,所以你提供的代码真的有用吗? - user2234
这非常有帮助,因为数据库查询是同步操作,并使用LiveData观察它们的输出可以避免很多样板代码。您在此提出的问题似乎与最初提出的问题不同。您是否可以编辑您的问题以包含有关数据获取和“处理”的架构的详细信息? - KG6ZVP
你的问题是如何从服务器中检索数据吗? - KG6ZVP
{btsdaf} - user2234
2
{btsdaf} - KG6ZVP
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接