带有 LiveData、存储库和视图模型的 Room 数据库如何协同工作? [英] How does the Room Database with LiveData, repository, and viewmodel work together?

查看:21
本文介绍了带有 LiveData、存储库和视图模型的 Room 数据库如何协同工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 Android Studio 开发的新手,其中大部分内容都很简单.似乎这些概念是相当新的,而且 Google 的文档要么写得不好,要么只是让我非常困惑.即使在这里查看其他问题也不足以为我澄清这一点.

I am new to Android Studio development and most of it is straight-forward. It seems these concepts are fairly new and Google's documentation for them are either poorly written or just confusing me greatly. Even looking on here at others questions hasn't been sufficient to clear this up for me.

到目前为止,我在我的应用程序中所拥有的是用户注册或登录,其中用户的字符保存到数据库中或从数据库中检索.在当前状态下,登录和注册与数据库一起工作,插入和检索他们的角色会相应地更新 UI.

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.

我有一些片段会改变角色统计数据,并且在使用 ViewModel 对角色使用观察者进行更改后,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. 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.

我尝试做的主要目标是将字符更改保存到数据库,并使用新更改更新 UI.

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.

我已经从数据库中删除了回调逻辑,重构了我的一些 Dao 查询并相应地更新了 repo 和 viewmodel,并添加了一个 findById 查询.我已经使用我的字符类将数据绑定添加到我的 home 片段的 xml 中.

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. I have added databinding to my home fragment's xml using my character class.

我的数据库:

@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;
    }
}

我的 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: I added asynctasks to update and delete.

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;
        }
    }
}

我的 ViewModel:通过 id 而不是电子邮件找到 getPlayer.

My ViewModel: getPlayer is found by id instead of email.

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);
    }
}

此代码是我添加了数据绑定的主片段:使用 getArguments 从我的活动中获取 Id,在 onChanged() 中调用 binding.setPlayer(player) 使一切正常更新数据库和 UI.在我为播放器设置了一些东西后,我更新了播放器

This code is my home fragment with the databinding added: 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. 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. I feel like I have the wrong approach about the observer and how its supposed to be called. 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.

这篇关于带有 LiveData、存储库和视图模型的 Room 数据库如何协同工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆