在非View或非ViewModel类中观察LiveData

时间:2019-07-12 18:56:00

标签: java android-livedata

我有一个应用程序,允许用户将其凭据输入Fragment,然后在Room数据库中插入或更新记录。交易由CredentialsRepository处理。

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

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

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

这是我的凭据存储库:

    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的创建速度比从数据库中检索数据的速度快,而我的网络服务最初是使用空或未知用户凭据创建的。这是正确的假设吗?

1 个答案:

答案 0 :(得分:0)

问题是我没有设置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的值,以便在我的视图处于活动状态时可以立即进行网络调用。我还在DAO中创建了一个查询,该查询返回了没有包装在LiveData对象中的UserCredentials。有关如何使用此解决方案的信息,请参阅此Stackoverflow post

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