База данных комнат возвращает 1 дополнительный объект - PullRequest
1 голос
/ 12 октября 2019

У меня есть класс комнатной БД, который создает 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 -

enter image description here

Livingata выясняет, что было 1Пользователь в списке добавляет в качестве карты, а затем БД создает новые объекты, и liveata вызывает метод, добавляя еще 3 пользователей.

Почему это происходит ?? Я бы с удовольствием поцеловал чью-то ногу, если бы он решил эту проблему, не шутка

edit -

вот моя 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;
    }
  }


}

edit -

здесьмой дао -

@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();




}

Ответы [ 2 ]

1 голос
/ 12 октября 2019

Вставить всех пользователей в 1 транзакцию. 2 подхода: 1. Создать функцию в дао, которая получает список пользователей. 2. Создайте транзакцию в roomDB (как Google. Очень просто)

Я предпочитаю первую

1 голос
/ 12 октября 2019

попытайтесь понять, что я сделал комментарии

это необработанный код, чтобы дать вам представление

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
  • и при наблюдении за живыми данными пользователей я использую цикл, и в этом цикле я проверяю, добавлен ли этот пользователь уже

Вы его получили?

...