简体   繁体   English

带有LiveData,存储库和ViewModel的Room Database如何一起工作?

[英]How does the Room Database with LiveData, repository, and viewmodel work together?

I am new to Android Studio development and most of it is straight-forward. 我是Android Studio开发的新手,大部分工作都很简单。 It seems these concepts are fairly new and Google's documentation for them are either poorly written or just confusing me greatly. 这些概念似乎还很新,而针对它们的Google文档编写得很差,或者使我非常困惑。 Even looking on here at others questions hasn't been sufficient to clear this up for me. 即使在这里看着别人的问题,也不足以为我解决这个问题。

What I have so far in my app is a user either registers or logs in where the character of the user gets saved into the database or is retrieved from database respectfully. 到目前为止,我的应用程序中有一个用户注册或登录,该用户的字符已保存到数据库中或已分别从数据库中检索到。 At the current state the login and registration work with the database inserting and retrieving their character update the UI accordingly. 在当前状态下,登录和注册以及数据库插入和检索其字符的工作将相应地更新UI。

I have fragments that alter the characters stats and the UI does not get updated after changes using an observer on the character using the ViewModel. 我有一些片段会更改字符统计信息,并且使用ViewModel在角色上使用观察者进行更改后,UI不会更新。 Also, the database is not updated with the changes either. 同样,数据库也不会使用更改进行更新。

There are probably simple things I am missing to get this to work. 要使它正常工作,我可能缺少一些简单的东西。 Please let me know of any alterations I should make to the current code and any advice moving forward. 请让我知道我应该对当前代码进行的任何更改以及任何建议。

The main objective I am trying to do is save the characters changes to the database and have the UI update with the new changes. 我试图做的主要目标是将字符更改保存到数据库中,并使用新更改来更新UI。

EDIT: I have removed the callback logic from the database, refactored some of my Dao queries and updated the repo and viewmodel accordingly, and added a findById query. 编辑:我已经从数据库中删除了回调逻辑,重构了一些我的Dao查询,并相应地更新了repo和viewmodel,并添加了findById查询。 I have added databinding to my home fragment's xml using my character class. 我已经使用我的角色类将数据绑定添加到了我的家庭片段的xml中。

My database: 我的数据库:

@Database(entities = {Character.class}, version = 1)
public abstract class MyDatabase extends RoomDatabase {

    public abstract UserDao userDao();

    private static MyDatabase INSTANCE;

    public static MyDatabase getDatabase(final Context context) {
        if (INSTANCE == null) {
            synchronized (MyDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                           MyDatabase.class, "character_database")
                           .build();
                }
           }
       }
       return INSTANCE;
    }
}

My DAO: 我的DAO:

@Dao        //Data Access Object
public interface UserDao {

    @Query("SELECT * FROM character_table")
    LiveData<List<Character>> getAllCharacters();

    @Query("SELECT * FROM character_table WHERE email LIKE :email LIMIT 1")
    LiveData<Character> findByEmail(String email);

    @Query("SELECT * FROM character_table WHERE name")
    LiveData<List<Character>> sortByName();

    @Query("SELECT * FROM character_table WHERE id LIKE :id LIMIT 1")
    LiveData<Character> findById(int id);

    @Query("SELECT * FROM character_table WHERE rank")
    LiveData<List<Character>> sortByRank();

    @Query("SELECT * FROM character_table WHERE total_exp")
    LiveData<List<Character>> sortByExp();

    @Query("SELECT * FROM character_table WHERE village")
    LiveData<List<Character>> sortByVillage();

    @Query("SELECT * FROM character_table WHERE email LIKE :email AND password LIKE :password")
    LiveData<Character> login(String email, String password);

    @Delete
    void deleteCharacter(Character player);

    @Query("DELETE FROM character_table")
    void deleteAll();

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void save(Character player);

    @Update
    void update(Character player);

My repository: EDIT: I added asynctasks to update and delete. 我的存储库:编辑:我添加了asynctasks以进行更新和删除。

public class CharacterRepository {

    private final UserDao userDao;
    private LiveData<List<Character>> allCharacters;
    private LiveData<List<Character>> sortByName;
    private LiveData<List<Character>> sortByExp;
    private LiveData<List<Character>> sortByRank;
    private LiveData<List<Character>> sortByVillage;

    CharacterRepository(Application application){
        MyDatabase db = MyDatabase.getDatabase(application);
        userDao = db.userDao();
        allCharacters = userDao.getAllCharacters();
        sortByName = userDao.sortByName();
        sortByExp = userDao.sortByExp();
        sortByRank = userDao.sortByRank();
        sortByVillage = userDao.sortByVillage();
    }

    public LiveData<Character> login(String email, String password){
        return userDao.login(email, password);
    }

    LiveData<List<Character>> sortByName(){
        return sortByName;
    }

    LiveData<Character> findByEmail(String email){
        return userDao.findByEmail(email);
    }

    LiveData<List<Character>> sortByName(){
        return sortByName;
    }

    LiveData<List<Character>> sortByExp(){
        return sortByExp;
    }

    LiveData<List<Character>> sortByRank(){
        return sortByRank;
    }

    LiveData<List<Character>> sortByVillage(){
        return sortByVillage;
    }

    LiveData<List<Character>> getAll(){
        return allCharacters;
    }

    public void insert(Character player){
        new insertAsyncTask(userDao).execute(player);
    }

    public void update(Character player){
        new updateAsyncTask(userDao).execute(player);
    }

    public void delete(Character player){
        new deleteAsyncTask(userDao).execute(player);
    }

    public LiveData<Character>  getPlayer(String id){
        return userDao.findByEmail(id);
    }

    private static class insertAsyncTask extends AsyncTask<Character, Void, Void> {

        private UserDao mAsyncTaskDao;

        insertAsyncTask(UserDao dao) {
            mAsyncTaskDao = dao;
        }

        @Override
        protected Void doInBackground(Character... characters) {
            mAsyncTaskDao.save(characters[0]);
            return null;
        }
    }

    private static class updateAsyncTask extends AsyncTask<Character, Void, Void> {

        private UserDao mAsyncTaskDao;

        updateAsyncTask(UserDao dao) {
            mAsyncTaskDao = dao;
        }

        @Override
        protected Void doInBackground(Character... characters) {
            mAsyncTaskDao.update(characters[0]);
            return null;
        }
    }

    private static class deleteAsyncTask extends AsyncTask<Character, Void, Void> {

        private UserDao mAsyncTaskDao;

        deleteAsyncTask(UserDao dao) {
            mAsyncTaskDao = dao;
        }

        @Override
        protected Void doInBackground(Character... characters) {
            mAsyncTaskDao.deleteCharacter(characters[0]);
            return null;
        }
    }
}

My ViewModel: EDIT: getPlayer is found by id instead of email. 我的ViewModel:编辑:通过id而不是电子邮件找到getPlayer。

public class MyViewModel extends AndroidViewModel {

    private CharacterRepository cRepository;
    private LiveData<List<Character>> allCharacters;

    public MyViewModel(Application application){
        super(application);
        cRepository = new CharacterRepository(application);
        allCharacters = cRepository.getAll();
    }

    LiveData<List<Character>> getAllCharacters() {return allCharacters;}

    public void insert(Character player){
        cRepository.insert(player);
    }

    public void deletePlayer(Character player){
        cRepository.delete(player);
    }

    public void updatePlayer(Character player){
        cRepository.update(player);
    }

    public LiveData<Character> getPlayer(int id){
        return cRepository.getPlayer(id);
    }

    public LiveData<Character> findByEmail(String email){
        return cRepository.findByEmail(email);
    }

    public LiveData<Character> login(String email, String password){
        return cRepository.login(email, password);
    }
}

This code is my home fragment with the databinding added: EDIT: Used getArguments to get the Id from my activity, called binding.setPlayer(player) inside onChanged() makes everything work properly updating the database and the UI. 这段代码是我的家庭片段,其中添加了数据绑定:编辑:使用getArguments从我的活​​动中获取ID,在onChanged()内称为binding.setPlayer(player)可使一切正常更新数据库和UI。 After I set something for the player I update the player 为播放器设置内容后,更新播放器

    private MyViewModel viewModel;
    private FragmentHomeBinding binding;
    private View rootView;
    private Character player;
    private int id;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, 
                             @Nullable ViewGroup container, 
                             @Nullable Bundle savedInstanceState) {
    binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false);
    rootView = binding.getRoot();
    viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
    id = getArguments().getInt("id"); 
    return rootView;
}

@Override
public void onStart() {
    super.onStart();

    // Home fragment buttons.
    final Button sleepButton = getView().findViewById(R.id.sleepButton);
    final Button bankButton  = getView().findViewById(R.id.bankButton);
    final Button infoButton  = getView().findViewById(R.id.infoButton);

    // The view that shows the players pool ratio.
    info = getView().findViewById(R.id.poolAmount);
    layout = getView().findViewById(R.id.poolInfo);

    // The players status bars.
    healthBar = getView().findViewById(R.id.healthBar);
    chakraBar = getView().findViewById(R.id.chakraBar);
    staminaBar = getView().findViewById(R.id.staminaBar);

    //Observe LiveData Character.
    viewModel.getPlayer(id).observe(this, new Observer<Character>() {
        @Override
        public void onChanged(@Nullable final Character character) {
            player = character;
            player.setRank(updateRank());
            binding.setPlayer(player);

            //Setting the progress and max for each user pool.
            healthBar.setProgress((int)player.getHealth());
            healthBar.setMax((int)player.getHealthMax());

            chakraBar.setProgress((int)player.getChakra());
            chakraBar.setMax((int)player.getChakraMax());

            staminaBar.setProgress((int)player.getStamina());
            staminaBar.setMax((int)player.getStaminaMax());

        }
    });

    sleepButton.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View v) {
            if (player.isAwake()) {
                player.setAwake(false);
                sleepButton.setText("Wake Up");
                Toast.makeText(getContext(), "You went to sleep...", Toast.LENGTH_SHORT).show();
            } else {
                player.setAwake(true);
                sleepButton.setText("Sleep");
                Toast.makeText(getContext(), "You woke up!", Toast.LENGTH_SHORT).show();
            }
        }
    });

    bankButton.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View v) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
            final View bankPrompt = getLayoutInflater().inflate(R.layout.bank_prompt, null);
            builder.setView(bankPrompt);

            final AlertDialog dialog = builder.create();
            dialog.show();

            TextView bankMoney = bankPrompt.findViewById(R.id.bankAmount);
            TextView pocketMoney = bankPrompt.findViewById(R.id.pocketAmount);
            Button depositButton = bankPrompt.findViewById(R.id.depositButton);
            Button withdrawButton = bankPrompt.findViewById(R.id.withdrawButton);
            ImageButton closeBankPrompt = bankPrompt.findViewById(R.id.closeBank);

            pocketMoney.setText(String.valueOf(player.getPocketMoney()));
            bankMoney.setText(String.valueOf(player.getBankMoney()));

            depositButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    final View transactionPrompt = getLayoutInflater()
                            .inflate(R.layout.bank_transaction, null);
                    builder.setView(transactionPrompt);
                    TextView transactionText = transactionPrompt.findViewById(R.id.bankTransactionText);
                    transactionText.setText(R.string.bank_deposit);

                    final AlertDialog dialog = builder.create();

                    dialog.show();
                    Button confirmDeposit = transactionPrompt.findViewById(R.id.confirmTransaction);
                    ImageButton closePrompt = transactionPrompt.findViewById(R.id.cancelTransaction);

                    confirmDeposit.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            bankTransaction(player,0, transactionPrompt, bankPrompt);
                            dialog.hide();
                        }
                    });

                    closePrompt.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            dialog.hide();
                        }
                    });
                }
            });

            withdrawButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    final View transactionPrompt = getLayoutInflater()
                            .inflate(R.layout.bank_transaction, null);
                    builder.setView(transactionPrompt);

                    TextView transactionText = transactionPrompt.findViewById(R.id.bankTransactionText);
                    transactionText.setText(R.string.bank_withdraw);

                    final AlertDialog dialog = builder.create();

                    dialog.show();
                    Button confirmWithdraw = transactionPrompt.findViewById(R.id.confirmTransaction);
                    ImageButton closePrompt = transactionPrompt.findViewById(R.id.cancelTransaction);

                    confirmWithdraw.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            bankTransaction(player,1, transactionPrompt, bankPrompt);
                            dialog.hide();
                        }
                    });

                    closePrompt.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            dialog.hide();
                            }
                    });
                }
            });

            closeBankPrompt.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    dialog.hide();
                }
            });
        }
    });

    infoButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ScrollView info = getView().findViewById(R.id.infoView);

            if (info.getVisibility() == View.VISIBLE)
                info.setVisibility(View.GONE);
            else
                info.setVisibility(View.VISIBLE);
        }
    });
}

I have the same observe line in my second fragment with similar actions as this one. 我在第二个片段中具有相同的观察线,并且与此动作类似。 They function properly the UI just isn't updated after the first time. 它们可以正常运行,只是第一次后不会更新UI。 I feel like I have the wrong approach about the observer and how its supposed to be called. 我觉得我对观察者及其应如何调用有错误的方法。 EDIT: I did the same in my second fragment now and it also works. 编辑:我现在在第二个片段中做了同样的事情,它也可以工作。

I figured out how it works now and will update the code accordingly. 我知道了它现在是如何工作的,并将相应地更新代码。 Maybe someone will find it useful. 也许有人会发现它有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 viewmodel、repository 和 livedata 访问 room db - How to access room db using viewmodel, repository, and livedata Android - ViewModel、LiveData、Room 和 Retrofit 以及协程放在 kotlin 中 - Android - ViewModel, LiveData, Room and Retrofit and coroutines put together in kotlin 如何在 Room 中将 SearchView 与 LiveData 和 ViewModel 一起使用 - How to use SearchView with LiveData and ViewModel in Room LiveData如何知道房间数据库中何时更改数据? - How does LiveData know when data is changed in Room Database? 如何使用存储库和ViewModel更新会议室数据库中的字段 - How do I update a field in a room database using a repository & viewmodel ViewModel 如何缓存 LiveData? - How does ViewModel cache LiveData? 我想使用 Room Database、LiveData 和 Viewmodel 实时更新我的适配器。 但不知道如何 - I want to update my adapter in realtime using Room Database , LiveData and Viewmodel. But don't know how Android Livedata 与 RxJava Single 在房间数据库中不起作用 - Android Livedata with RxJava Single not work in Room Database 使用 Transformations.map 在 ViewModel 中处理 Room 数据库中的 LiveData - Processing LiveData from Room database in ViewModel with Transformations.map ViewModel中的LiveData如何使用转换观察存储库中的Livedata? - How can LiveData in ViewModel observe the Livedata in Repository using Transformations?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM