Наша команда разрабатывает проект с преимуществами Android Jetpack.
Демонстрационный код показывает вопрос, с которым мы сталкиваемся.Этот код можно найти по адресу https://github.com/viseator/TestRoomLivedata
Я создаю UserDao
:
@Dao
interface UserDao {
@Query("SELECT * FROM user WHERE uid = :uid LIMIT 1")
fun findUserById(uid: String?): Single<User>
@Query("SELECT * FROM user WHERE state = 1 LIMIT 1")
fun findLoginUserWithObserve(): LiveData<User>
@Query("SELECT * FROM user WHERE state =1 LIMIT 1")
fun findLoginUser(): Single<User>
@Update
fun update(vararg user: User)
}
Я также создал объект kotlin для управления состоянием пользователя.
I 'Наблюдая за живыми данными, возвращаемыми findLoginUserWithObserve()
, чтобы получить уведомление при изменении имени пользователя:
object AccountManager {
private const val DATA_BASE_NAME = "users"
val TAG = "AccountManager"
fun init(context: Application) {
sDb = databaseBuilder(context, UserDataBase::class.java, DATA_BASE_NAME).build()
sDao = sDb.userDao()
sDao.findLoginUserWithObserve().observeForever {
Log.d(TAG, "notified: $it")
}
}
private lateinit var sDb: UserDataBase
private lateinit var sDao: UserDao
fun findLoginUserWithObserve() = sDao.findLoginUserWithObserve()
fun logoutFlowable(): Single<Boolean> = sDao.findLoginUser().subscribeOn(
Schedulers.io()).map { user ->
user.state = User.STATE_NOT_LOGIN
sDao.update(user)
true
}
fun login(user: User) = logoutFlowable().subscribe({ doLogin(user) }, { doLogin(user) })
private fun doLogin(user: User) = sDao.findUserById(user.uid).subscribeOn(
Schedulers.io()).subscribe({ origin ->
origin.userName = user.userName
origin.state = User.STATE_HAVE_LOGIN
sDao.update(origin)
user.state = User.STATE_HAVE_LOGIN
}, {
user.state = User.STATE_HAVE_LOGIN
sDao.insert(user)
})
}
Я инициализирую AccountManager
в Applicaiton
, вызывая его метод init
, и создаю демонстрационную операцию:
class MainActivity : AppCompatActivity() {
private var i = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
AccountManager.login(User().apply {
userName = "user1"
uid = System.currentTimeMillis().toString()
})
button.setOnClickListener {
AccountManager.login(User().apply {
userName = "user${i++}"
uid = System.currentTimeMillis().toString()
})
}
}
}
Полагаю, однажды позвонят AccountManager.login()
, я получу уведомление, и он напечатает сообщение журнала.Но мы обнаружили, что больше не будем получать уведомления после GC.(Мы запускаем GC с помощью Android Studio Profiler)
Журнал сообщений
После изучения класса UserDao_Impl
, сгенерированного комнатой, мы обнаружили, что он создает наблюдателя и связывается с базой данныхпозвонив по номеру addWeakObserver()
:
@Override
public LiveData<User> findLoginUserWithObserve() {
final String _sql = "SELECT * FROM user WHERE state = 1 LIMIT 1";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
return new ComputableLiveData<User>(__db.getQueryExecutor()) {
private Observer _observer;
@Override
protected User compute() {
if (_observer == null) {
_observer = new Observer("user") {
@Override
public void onInvalidated(@NonNull Set<String> tables) {
invalidate();
}
};
__db.getInvalidationTracker().addWeakObserver(_observer);
}
Итак, мы задаемся вопросом, почему комната использует WeakObserver
, что делает проживающие данные возвращенными комнатой ненадежными?
PS: Мы используем Flowable
для излученияВ «liveata» теперь onNext()
, чтобы обойти это, onNext()
будет запускаться каждый раз, как и ожидалось.