【问题标题】:Room database is returning 1 extra object房间数据库返回 1 个额外的对象
【发布时间】:2019-10-12 15:52:29
【问题描述】:

我有一个创建 3 个用户对象的房间数据库类 -

@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class UserDatabase extends RoomDatabase {

  private static UserDatabase instance;

  public abstract UserDao userDao();

  public static synchronized UserDatabase getInstance(Context context) {

    Log.d("inside observe - ", "inside database");

    if (instance == null) {
      instance = Room.databaseBuilder(context.getApplicationContext(), UserDatabase.class, "user_database").fallbackToDestructiveMigration().addCallback(roomUserCallback).build();
    }
    return instance;
  }

  private static RoomDatabase.Callback roomUserCallback = new RoomDatabase.Callback() {

    @Override
    public void onCreate(@NonNull SupportSQLiteDatabase db) {
      super.onCreate(db);
      new PopulateDbAsyncTask(instance).execute();
    }
  };

  //TODO - delete this in the future. This is just for populating.
  private static class PopulateDbAsyncTask extends AsyncTask<Void, Void, Void> {

    static final String URL = "https://www.shortlist.com/media/images/2019/05/40-favourite-songs-of-famous-people-28-1556672663-9rFo-column-width-inline.jpg";
    static final String URL2 = "https://img-s-msn-com.akamaized.net/tenant/amp/entityid/BBR9VUw.img?h=416&amp;w=624&amp;m=6&amp;q=60&amp;u=t&amp;o=f&amp;l=f&amp;x=2232&amp;y=979";
    static final String URL3 = "https://dz9yg0snnohlc.cloudfront.net/new-what-famous-people-with-depression-have-said-about-the-condition-1.jpg";
    private UserDao userDao;

    private PopulateDbAsyncTask(UserDatabase db) {
      userDao = db.userDao();
    }

    @Override
    protected Void doInBackground(Void... voids) {
      userDao.insert(new User(URL, "Barak Obama1", "/@BarakObama1"));
      userDao.insert(new User(URL2, "Barak Obama2", "/@BarakObama2"));
      userDao.insert(new User(URL3, "Barak Obama3", "/@BarakObama3"));
      return null;
    }
  }
}

我正在使用 viewmodel 来获取用户作为 LiveData。 出于某种原因,在我第一次安装我的应用程序时,我创建了一个额外的“barak obama1”用户,并且在所有 3 个“普通”用户之后立即按顺序 - barak obama3、2 和 1。 这是我的 MainActivity -

private ArrayList<User> usersList;
@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    fetchUserList();
  }

  private void fetchUserList() {
    userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
    final Observer<List<User>> userObserver = users -> {
      Log.d("inside observe - ", "inside main activity, list size - " + users.size());
      usersList = (ArrayList) users;
      initViewsAndListeners();
      addCards();
    };
    userViewModel.getAllUsers().observe(this, userObserver);
  }

  private void addCards(){
    TinderCardView tinderCardView;
    for (int i = 0; i < usersList.size(); i++) {
      tinderCardView = new TinderCardView(this);
      tinderCardView.bind(usersList.get(i));
      Log.d("inside observe - ", "inside main activity, user value - " + usersList.get(i).getUsername());
      tinderStackLayout.addCard(tinderCardView);
      Log.d("addCardCalled - ", "\nindex value - " + i + "\n" +
          "userlist size - " + usersList.size());
    }
  }

  @Override
  public void onClick(View view) {
    int buttonTag = Integer.valueOf(String.valueOf(view.getTag()));
    TinderCardView topCardOnStack = tinderStackLayout.getTopCardOnStack();
    topCardOnStack.handleButtonPressed(buttonTag);
//    if (buttonTag == 1) { // TODO - move logic to card class
//       userViewModel.delete(usersList.get(0));
//       //fetchUserList();
//    }
  }

  private void initViewsAndListeners() {
    tinderStackLayout = findViewById(R.id.activity_main_tinder_stack_layout);
    mDeleteButton = findViewById(R.id.activity_main_delete_button);
    mPassButton = findViewById(R.id.activity_main_pass_button);
    mApproveButton = findViewById(R.id.activity_main_approve_button);
    mDeleteButton.setOnClickListener(this);
    mApproveButton.setOnClickListener(this);
    mPassButton.setOnClickListener(this);
  }

如您所见,我到处都有日志消息,因此您可以了解我现在要向您展示的内容。我得到了一个额外的用户,首先是“barak obama1”用户,然后是其他所有 3 -

livedata 发现列表中有 1 个用户,作为卡片添加,然后数据库创建新对象,livedata 调用该方法,增加了 3 个用户。

为什么会这样??如果他解决了这个问题,我很乐意亲吻某人的腿,不是开玩笑。

编辑 -

这是我的 ViewModel -

public class UserViewModel extends AndroidViewModel {

  private UserRepository repository;
  private LiveData<List<User>> allUsers;

  public UserViewModel(@NonNull Application application) {
    super(application);
    repository = new UserRepository(application);
    allUsers = repository.getAllUsers();

  }

  public void insert(User user) {
    repository.insert(user);
  }

  public void update(User user) {
    repository.update(user);
  }

  public void delete(User user) {
    repository.delete(user);
  }

  public void deleteAllUsers(){
    repository.deleteAllUsers();
  }

  public LiveData<List<User>> getAllUsers() {
    Log.d("inside observe - ", "inside viewmodel");
    return allUsers;
  }
}

和我的仓库 -

public class UserRepository {

  private UserDao userDao;
  private LiveData<List<User>> allUsers;

  public UserRepository(Application application) {
    UserDatabase database = UserDatabase.getInstance(application);
    userDao = database.userDao();
    allUsers = userDao.getAllUsers();
  }

  public void insert(User user) {
    new InsertUserAsyncTask(userDao).execute(user);
  }

  public void update(User user) {
    new UpdateUserAsyncTask(userDao).execute(user);
  }

  public void delete(User user) {
    new DeleteUserAsyncTask(userDao).execute(user);
  }

  public void deleteAllUsers() {
    new DeleteAllUsersAsyncTask();
  }

  public LiveData<List<User>> getAllUsers() {
    Log.d("inside observe - ", "inside repository");
    return allUsers;
  }

  //TODO - migrate all 4 async tasks into one.
  private static class InsertUserAsyncTask extends AsyncTask<User, Void, Void> {

    private UserDao userDao;

    private InsertUserAsyncTask(UserDao userDao) {
      this.userDao = userDao;
    }

    @Override
    protected Void doInBackground(User... users) {
      userDao.insert(users[0]);
      return null;
    }
  }

  private static class UpdateUserAsyncTask extends AsyncTask<User, Void, Void> {

    private UserDao userDao;

    private UpdateUserAsyncTask(UserDao userDao) {
      this.userDao = userDao;
    }

    @Override
    protected Void doInBackground(User... users) {
      userDao.update(users[0]);
      return null;
    }
  }

  private static class DeleteUserAsyncTask extends AsyncTask<User, Void, Void> {

    private UserDao userDao;

    private DeleteUserAsyncTask(UserDao userDao) {
      this.userDao = userDao;
    }

    @Override
    protected Void doInBackground(User... users) {
      userDao.delete(users[0]);
      return null;
    }
  }

  private static class DeleteAllUsersAsyncTask extends AsyncTask<Void, Void, Void> {

    private UserDao userDao;

    private DeleteAllUsersAsyncTask() {
      this.userDao = userDao;
    }

    @Override
    protected Void doInBackground(Void... voids) {
      userDao.deleteAllUsers();
      return null;
    }
  }


}

编辑 -

这是我的道 -

@Dao
public interface UserDao {

  @Insert
  void insert(User user);

  @Update
  void update(User user);

  @Delete
  void delete(User user);

  @Query("DELETE FROM user_table")
  void deleteAllUsers();

  @Query("SELECT * FROM user_table ORDER BY id DESC")
  LiveData<List<User>> getAllUsers();




}

【问题讨论】:

  • 实时数据就是这样工作的。它没有返回任何额外的东西。它正在返回您要求它返回的内容。因此,当您渲染数据时,请检查您是否已经渲染了数据,如果没有则渲染它。简单!!!
  • 你的意思是,在数据库类中我应该检查我是否已经上传了数据?如果是的话 - 你能告诉我根据我提供的数据库类正确的方法是什么吗?
  • 你的ViewModel是什么样的?
  • @Md.Asaduzzaman 编辑并添加了视图模型和存储库
  • 尝试在addCards()的第一行添加tinderStackLayout.removeAllViews()并检查。这不是 liveData 的问题。稍后我会解释,首先检查它是否工作。

标签: android android-room android-livedata


【解决方案1】:

在 1 个事务中插入所有用户。 2种方法: 1.在dao中创建一个接收用户列表的函数。 2.在roomDB中创建一个事务(google how。很简单)

我更喜欢第一个

【讨论】:

  • 您的解决方案似乎是正确的,但我在 google 上没有找到任何关于此的信息,而且我的自我实现无法正常工作。能否请您链接到我的谷歌搜索/编辑并发布代码示例?
  • ` abstract class UsersDao{ @Insert abstract fun insertUsers(users: List) : Long @Transaction open fun deleteAndInsertUsersAsTransaction(users: List) { deleteAllUsers() // db call 1 insertUsers(users) // 数据库调用 2 } }`
【解决方案2】:

试着去理解 我做了cmets

这是原始代码,仅供参考

private ArrayList<User> usersList;

@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    this.usersList = new ArrayList(); // initalise the array list here
    fetchUserList();
  }

  private void fetchUserList() {
    userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
    final Observer<List<User>> userObserver = users -> {
      Log.d("inside observe - ", "inside main activity, list size - " + users.size());
      usersList = (ArrayList) users; //dont do this ! instead follow the below instructions
      // to do
      for(User user : users){
        if(!usersList.contains(user)){
            usersList.add(user);
        }
      }
      // to do ends here
      initViewsAndListeners();
      addCards();
    };
    userViewModel.getAllUsers().observe(this, userObserver);
  }

}

看看我做了什么:

  • 初始化用户列表
  • 当观察用户实时数据时,我使用循环,在那个循环中我 检查是否已添加此用户

你明白了吗?

【讨论】:

  • 谢谢你,我会试试你的解决方案。我只是不明白为什么首先会有任何重复的信息!怎么可能,刚刚安装了应用。
  • 它是因为,当你这样做时,'usersList = (ArrayList) users;'在你的观察者中。观察者获取数据 2 次,而您的代码只获取数据并呈现它,因此它呈现多次数据。我做了什么我在 oncreate 中初始化了 userArrayList,在观察者中我检查数据是否已经被渲染?
  • 如果我理解正确,数据库开始创建 3 个对象,当完成第一个对象时,将向上发送一个 livedata,在 3 个对象中只给我一个对象(因为数据已更改),所以我一次得到一个对象,第二次得到所有 3 个对象。如果我说的是正确的,为什么我不能得到 1 个对象,然后是 2 个对象而不是 3 个对象?
  • 在第一条评论中添加了 dao
猜你喜欢
  • 1970-01-01
  • 2020-06-29
  • 1970-01-01
  • 1970-01-01
  • 2020-06-24
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
相关资源
最近更新 更多