我可以在 Observer 中添加 Observer 吗?我实际上尝试过,但内部观察者无法正常工作。有时其中一个工作,有时没有人工作
Can I add Observer inside Observer? I actually tried but inner observers not working properly. Sometimes one of them work, sometimes no one work
我使用了 Android 架构组件(ViewModel、LiveData)并使用 Room 使用 Repository 从本地 Sqlite 数据库中获取数据。最外层的观察者就好了,只有内层的观察者有问题。
查看代码:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
BranchDetailsViewModel branchDetailsViewModel = new BranchDetailsViewModel(application);
Observer<Bank> bankObserver = new Observer<Bank>() {
@Override
public void onChanged(Bank bank) {
if(bank!= null)
binding.tvBank.setText(bank.getBank());
}
};
branchDetailsViewModel.getBranchById(branchIid).observe(this, new Observer<Branch>() {
@Override
public void onChanged(Branch branch) {
branchDetailsViewModel.getBankById(branch.getBankId()).observe(getViewLifecycleOwner(), bankObserver);
branchDetailsViewModel.getCityById(branch.getCityId()).observe(requireActivity(), new Observer<City>() {
@Override
public void onChanged(City city) {
if(city != null)
binding.tvCity.setText(city.getCity() + " cty");
}
});
branchDetailsViewModel.getDistrictById(branch.getDistrictId()).observe(requireActivity(), new Observer<District>() {
@Override
public void onChanged(District district) {
if(district != null)
binding.tvDistrict.setText(district.getDistrict());
}
});
branchDetailsViewModel.getStateById(branch.getStateId()).observe(requireActivity(), new Observer<State>() {
@Override
public void onChanged(State state) {
if(state!= null)
binding.tvState.setText(state.getState());
}
});
}
});
}
我尝试过使用命名和匿名观察者。我不明白这里有什么问题。
有时会抛出 NullPointerExceptions。有时更改语言环境失败。
04-09 15:48:45.182 2096-2113/com.appsbharti.bharatbankdetails E/SQLiteDatabase: Failed to open database '/data/data/com.appsbharti.bharatbankdetails/databases/bank_details.db'.
android.database.sqlite.SQLiteException: Failed to change locale for db '/data/data/com.appsbharti.bharatbankdetails/databases/bank_details.db' to 'en_IN'.
at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:386)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:218)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:804)
at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:92)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:53)
at androidx.room.SQLiteCopyOpenHelper.getWritableDatabase(SQLiteCopyOpenHelper.java:90)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
at com.appsbharti.bharatbankdetails.Daos.HistoryDao_Impl.insert(HistoryDao_Impl.java:45)
at com.appsbharti.bharatbankdetails.Repositories.HistoryRepository.run(HistoryRepository.java:37)
at androidx.room.TransactionExecutor.run(TransactionExecutor.java:45)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
Caused by: android.database.sqlite.SQLiteDiskIOException: disk I/O error (code 1802)
at android.database.sqlite.SQLiteConnection.nativeExecute(Native Method)
at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:548)
at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:364)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:218)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:804)
at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:92)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:53)
at androidx.room.SQLiteCopyOpenHelper.getWritableDatabase(SQLiteCopyOpenHelper.java:90)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
at com.appsbharti.bharatbankdetails.Daos.HistoryDao_Impl.insert(HistoryDao_Impl.java:45)
at com.appsbharti.bharatbankdetails.Repositories.HistoryRepository.run(HistoryRepository.java:37)
at androidx.room.TransactionExecutor.run(TransactionExecutor.java:45)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
还有一个问题是我不确定我应该在 Fragment 中使用哪个作为 Observer 的所有者,requireActivity()
或 getActivity()
(我收到警告说这可能 return 为 null,所以我没用过)或者getViewLifecycleOwner()
(获取fragment的生命周期所有者对象)。
如果你在一个Fragment里面,你不能通过this
来观察,你应该通过viewlifecycleOwner
。在您的情况下,您正在设置观察以从房间获取分支的值,然后根据分支,您的其他观察者被调用。
这意味着您的内部观察者将在您的 branch
观察者被调用后观察。
当您将观察者绑定到片段的生命周期时,观察者会根据片段本身的生命周期工作。
将它与 lifecycleowner 绑定应该可以,主要是因为 this
在 fragment 的情况下不起作用。
实际上,viewlifecycleOwner
和 this
被接受。对于内部,您必须调用 YourFragmnent.this
我看到您没有调用任何函数来更新内部 LiveData 值。
并且你不应该包裹嵌套的观察者,因为内部观察者将被调用几次对应于外部观察者被调用的次数。
只需要调用更新LiveData值的函数即可触发内部观察者的onChanged回调
请阅读本文以避免多次消耗数据https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
- 请使用
YouFragmentClassName.this
或viewLifecycleOwner
- 添加日志以检查您的
onChange
回调是否收到。
- 如果您仍然得到空值,请检查您的
viewModel
代码。
好的问题是我没有在 synchronized
块中获取 AppDatabase。
在 AppDatabse class 中,我更改了 newInsance
方法:
public static AppDatabase getInstance(final Context context) {
dbInstance = buildDatabaseInstance(context);
return dbInstance;
}
到
public static AppDatabase getInstance(final Context context) {
if (dbInstance == null)
synchronized (AppDatabase.class) {
if (dbInstance == null)
dbInstance = buildDatabaseInstance(context);
}
// dbInstance = buildDatabaseInstance(context);
return dbInstance;
}
现在所有观察者运行顺利
我使用了 Android 架构组件(ViewModel、LiveData)并使用 Room 使用 Repository 从本地 Sqlite 数据库中获取数据。最外层的观察者就好了,只有内层的观察者有问题。
查看代码:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
BranchDetailsViewModel branchDetailsViewModel = new BranchDetailsViewModel(application);
Observer<Bank> bankObserver = new Observer<Bank>() {
@Override
public void onChanged(Bank bank) {
if(bank!= null)
binding.tvBank.setText(bank.getBank());
}
};
branchDetailsViewModel.getBranchById(branchIid).observe(this, new Observer<Branch>() {
@Override
public void onChanged(Branch branch) {
branchDetailsViewModel.getBankById(branch.getBankId()).observe(getViewLifecycleOwner(), bankObserver);
branchDetailsViewModel.getCityById(branch.getCityId()).observe(requireActivity(), new Observer<City>() {
@Override
public void onChanged(City city) {
if(city != null)
binding.tvCity.setText(city.getCity() + " cty");
}
});
branchDetailsViewModel.getDistrictById(branch.getDistrictId()).observe(requireActivity(), new Observer<District>() {
@Override
public void onChanged(District district) {
if(district != null)
binding.tvDistrict.setText(district.getDistrict());
}
});
branchDetailsViewModel.getStateById(branch.getStateId()).observe(requireActivity(), new Observer<State>() {
@Override
public void onChanged(State state) {
if(state!= null)
binding.tvState.setText(state.getState());
}
});
}
});
}
我尝试过使用命名和匿名观察者。我不明白这里有什么问题。
有时会抛出 NullPointerExceptions。有时更改语言环境失败。
04-09 15:48:45.182 2096-2113/com.appsbharti.bharatbankdetails E/SQLiteDatabase: Failed to open database '/data/data/com.appsbharti.bharatbankdetails/databases/bank_details.db'.
android.database.sqlite.SQLiteException: Failed to change locale for db '/data/data/com.appsbharti.bharatbankdetails/databases/bank_details.db' to 'en_IN'.
at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:386)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:218)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:804)
at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:92)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:53)
at androidx.room.SQLiteCopyOpenHelper.getWritableDatabase(SQLiteCopyOpenHelper.java:90)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
at com.appsbharti.bharatbankdetails.Daos.HistoryDao_Impl.insert(HistoryDao_Impl.java:45)
at com.appsbharti.bharatbankdetails.Repositories.HistoryRepository.run(HistoryRepository.java:37)
at androidx.room.TransactionExecutor.run(TransactionExecutor.java:45)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
Caused by: android.database.sqlite.SQLiteDiskIOException: disk I/O error (code 1802)
at android.database.sqlite.SQLiteConnection.nativeExecute(Native Method)
at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:548)
at android.database.sqlite.SQLiteConnection.setLocaleFromConfiguration(SQLiteConnection.java:364)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:218)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:804)
at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:221)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:92)
at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:53)
at androidx.room.SQLiteCopyOpenHelper.getWritableDatabase(SQLiteCopyOpenHelper.java:90)
at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
at com.appsbharti.bharatbankdetails.Daos.HistoryDao_Impl.insert(HistoryDao_Impl.java:45)
at com.appsbharti.bharatbankdetails.Repositories.HistoryRepository.run(HistoryRepository.java:37)
at androidx.room.TransactionExecutor.run(TransactionExecutor.java:45)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
还有一个问题是我不确定我应该在 Fragment 中使用哪个作为 Observer 的所有者,requireActivity()
或 getActivity()
(我收到警告说这可能 return 为 null,所以我没用过)或者getViewLifecycleOwner()
(获取fragment的生命周期所有者对象)。
如果你在一个Fragment里面,你不能通过this
来观察,你应该通过viewlifecycleOwner
。在您的情况下,您正在设置观察以从房间获取分支的值,然后根据分支,您的其他观察者被调用。
这意味着您的内部观察者将在您的 branch
观察者被调用后观察。
当您将观察者绑定到片段的生命周期时,观察者会根据片段本身的生命周期工作。
将它与 lifecycleowner 绑定应该可以,主要是因为 this
在 fragment 的情况下不起作用。
实际上,viewlifecycleOwner
和 this
被接受。对于内部,您必须调用 YourFragmnent.this
我看到您没有调用任何函数来更新内部 LiveData 值。 并且你不应该包裹嵌套的观察者,因为内部观察者将被调用几次对应于外部观察者被调用的次数。
只需要调用更新LiveData值的函数即可触发内部观察者的onChanged回调
请阅读本文以避免多次消耗数据https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
- 请使用
YouFragmentClassName.this
或viewLifecycleOwner
- 添加日志以检查您的
onChange
回调是否收到。 - 如果您仍然得到空值,请检查您的
viewModel
代码。
好的问题是我没有在 synchronized
块中获取 AppDatabase。
在 AppDatabse class 中,我更改了 newInsance
方法:
public static AppDatabase getInstance(final Context context) {
dbInstance = buildDatabaseInstance(context);
return dbInstance;
}
到
public static AppDatabase getInstance(final Context context) {
if (dbInstance == null)
synchronized (AppDatabase.class) {
if (dbInstance == null)
dbInstance = buildDatabaseInstance(context);
}
// dbInstance = buildDatabaseInstance(context);
return dbInstance;
}
现在所有观察者运行顺利