java - 在非 View 或非 ViewModel 类中观察 LiveData

标签 java android-livedata

我有一个应用程序,它允许用户将他们的凭据输入到片段中,然后片段插入或更新 Room 数据库中的记录。事务由 CredentialsRepository 处理。

在网络方面,我有一个 DataRepository,它与 NetworkServiceController 一起使用来创建对我的远程 API 的调用。为了访问远程 API,我有一个拦截器,它将用户凭据添加到每个调用。

我需要的是在应用程序加载时以及 Room 数据库中的数据发生变化时加载这些凭据。我可以在我的登录屏幕( View )中使用 LiveData 来执行此操作,但我如何使用 LiveData 在我的 DataRepository 中观察用户凭据数据?

如何将我的 DataRepository 类注册为观察者?现在我的应用程序崩溃了……我很确定我注册观察者的方式是错误的。

这是我的 CredentialsRepository:

    import androidx.lifecycle.LiveData;
    import ca.thirdgear.homeautomation.HomeAutomationApp;
    import ca.thirdgear.homeautomation.dao.UserCredentialsDAO;
    import ca.thirdgear.homeautomation.entity.UserCredentials;
    import javax.inject.Inject;
    import javax.inject.Singleton;

    /**
     * Repository Class for data from the Room Database
     */
    @Singleton
    public class CredentialsRepository
    {
        @Inject
        UserCredentialsDAO credentialsDAO;

        @Inject
        public CredentialsRepository()
        {
            HomeAutomationApp.getAppComponent().inject(this);
        }

        public void setCredentials(String userEmail, String password)
        {
            //create UserCredentials object with the credentials provided from the UI.
            UserCredentials newOrUpdatedUserCredentials = new UserCredentials(userEmail, password);

            //insert user credentials into the database
            Runnable myRunnableObject = new SetUserCredentials(credentialsDAO, newOrUpdatedUserCredentials);
            new Thread(myRunnableObject).start();
        }

        public LiveData<UserCredentials> getCredentials()
        {
            return credentialsDAO.getUser();
        }
    }

这是我的 DataRepositoryClass

    /**
     * Repository class for handling network calls to REST API
     */
    @Singleton
    public class DataRepository
    {
        //Network components
        @Inject
        NetworkServiceController networkServiceController;

        @Inject
        UserCredentialsDAO credentialsDAO;

        @Inject
        CredentialsRepository credRepo;

        private HomeAutomationAPI homeAutomationAPIService;
        private MutableLiveData<String> zoneOneState;
        private MutableLiveData<String> zoneTwoState;
        private MutableLiveData<String> garageDoorState;
        private MutableLiveData<String> adtSystemState;
        private MutableLiveData<String> panelTemperature;
        private String username;
        private String password;


        @Inject
        public DataRepository()
        {
            //Inject Network Backend Components & Room DAO
            HomeAutomationApp.getAppComponent().inject(this);

            //create credentials observer
            final Observer<UserCredentials> credentialsObserver = new Observer<UserCredentials>() {
                @Override
                public void onChanged(UserCredentials userCredentials)
                {
                    //update string values for credentials
                    username = userCredentials.getUsername();
                    password = userCredentials.getPassword();

                    //call createNetworkService() to create a new network service object
                    createNetworkService();
                }
            };

            //register the observer
            //this does not work...app crashes
            credRepo.getCredentials().observeForever(credentialsObserver);

            zoneOneState = new MutableLiveData<>();
            zoneTwoState = new MutableLiveData<>();
            garageDoorState = new MutableLiveData<>();
            adtSystemState = new MutableLiveData<>();
            panelTemperature = new MutableLiveData<>();

            //Poll the server every 5 seconds on a separate thread for changes in the IO
            Thread thread = new Thread(new Runnable(){
                public void run()
                {
                    // Moves the current Thread into the background
                    android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);

                    while(true)
                    {
                        try {
                            sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        getIoStatus();
                    }
                }
            });
            thread.start();
        }

        /*
             Create the network service when there are changes in the user credentials data.
         */
          private void createNetworkService()
          {
              homeAutomationAPIService = networkServiceController.createService(HomeAutomationAPI.class, username, password);
          }


        ... deleted methods not relevant ...

    }

编辑: 观察者注册可以像这样在 DataRepository 中发生:

credRepo.getCredentials().observeForever(credentialsObserver);

或者像这样:

credentialsDAO.getUser().observeForever(credentialsObserver);

但是,我需要通过调用在构造函数中创建网络服务:

createNetworkService();

否则应用程序仍然会崩溃。

我的应用程序现在可以在添加上面的代码行的情况下运行,但是产生了一个新问题。当应用程序在新进程中启动时,需要几秒钟的时间才能获得任何网络数据。我的猜测是创建 DataRepository 的速度比从数据库中检索数据的速度快,并且我的网络服务最初是使用空的或未知的用户凭据创建的。这是一个正确的假设吗?

最佳答案

问题是我没有设置 Room 来允许主线程查询使用:

allowMainThreadQueries()

用法:

public static CredentialsDatabase getDatabase(final Context context)
{
    if (INSTANCE == null)
    {
        synchronized (CredentialsDatabase.class)
        {
            if (INSTANCE == null)
            {
                INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                        CredentialsDatabase.class, "credentials_database")
                        .addCallback(initializeCallback)
                        //allow main thread queries only used for DataRepository instantiation as
                        //credentials are required to set up network services.
                        .allowMainThreadQueries()
                        .build();
            }
        }
    }
    return INSTANCE;
}

我需要在创建 DataRepository 时将 UserCredentials 的值保存在内存中,这样我就可以在我的 View 处于 Activity 状态时立即进行网络调用。我还在我的 DAO 中创建了一个查询,该查询返回未包装在 LiveData 对象中的 UserCredentials。有关我如何得出此解决方案的信息,请参阅此 Stackoverflow post .

将来我还会创建一个日志文件以方便故障排除。

关于java - 在非 View 或非 ViewModel 类中观察 LiveData,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57012639/

相关文章:

java - 是否可以在不将 jar 添加到 lib 文件夹的情况下在 tomcat 中创建自定义领域?

android - LiveData 无法通知它的观察者 PagedList 对象的变化

MVVM - 自定义模型的 MutableLiveData 没有通过数据绑定(bind)更新到 ViewModel 并且始终为空

android - 用于实例化 ViewModel 的首选 Fragment 生命周期方法

java - 如何制作 Activity 来显示从服务获取的数据?

java - 如何将 JavaRDD<Row> 转换为 JavaRDD<List<String>>?

java - 如何在 JFace TreeViewer 中禁用可折叠

java - 数据结构ArrayList中的高效搜索

java - 合并 JPA 实体返回旧值

Android ViewModel 无法在 fragment 更改后继续存在