# #android #firebase #kotlin #kotlin-coroutines
Вопрос:
Первый вопрос здесь, я сделаю все, что в моих силах.
У меня есть класс данных, который извлекает объект данных с помощью firestore при создании. Я сделал некоторый код для сеттеров с помощью сопрограмм. Я не уверен в своем решении, но оно работает. Однако для добытчиков я изо всех сил пытаюсь дождаться инициализации.
При инициализации у меня есть обратный вызов для получения данных. Проблема в том, что обратный вызов всегда вызывается из основного потока, даже если я использую его в сопрограмме в другом потоке. Я проверяю это с помощью:
Log.d("THREAD", "Execution thread1: " Thread.currentThread().name)
Для сеттера я использую сопрограмму в useTask, чтобы не блокировать основной поток. И мьютекс для блокировки этой сопрограммы до тех пор, пока не будет выполнена инициализация в инициализации. Не уверен насчет ожидания инициализации, но это работает.
Но для геттера я просто хочу заблокировать основной поток (даже если это плохой дизайн, это первое решение) до завершения инициализации и возобновить геттер для получения значения. Но я не могу заблокировать основной поток, не блокируя также обратный вызов при инициализации, потому что они находятся в одном потоке.
Я прочитал много документации о сопрограмме, области действия, блокировке выполнения, потоке и т. Д., Но все путается в моей голове.
class Story(val id: String) : BaseObservable() {
private val storyRef = StoryHelper.getStoryRef(id)!!
private var isInitialized = false
private val initMutex = Mutex(true)
@get:Bindable
var dbStory: DbStory? = null
init {
storyRef.get().addOnCompleteListener { task ->
if (task.isSuccessful amp;amp; task.result != null) {
dbStory = snapshot.toObject(DbStory::class.java)!!
if (!isInitialized) {
initMutex.unlock()
isInitialized = true
}
notifyPropertyChanged(BR.dbStory)
}
}
}
fun interface StoryListener {
fun onEvent()
}
private fun useTask(function: (task: Task) -> Unit): Task {
val task = Task()
GlobalScope.launch {
waitInitialisationSuspend()
function(task)
}
return task
}
private suspend fun waitInitialisationSuspend()
{
initMutex.withLock {
// no op wait for unlock mutex
}
}
fun typicalSetFunction(value: String) : Task {
return useTask { task ->
storyRef.update("fieldName", value).addOnSuccessListener {
task.doEvent()
}
}
}
fun typicalGetFunction(): String
{
var result = ""
// want something to wait the callback in the init.
return result
}
}
Блокировка запуска, похоже, блокирует основной поток, поэтому я не могу его использовать, если обратный вызов все еще использует основной поток.
Та же проблема возникает, если я использую цикл while в основном потоке.
#1
runBlocking {
initMutex.withLock {
result = dbStory!!.value
}
}
#2
while (!isInitialized){
}
result = dbStory!!.value
#3
Потому что, возможно, обратный вызов в инициализации также находится в основном потоке. Я попытался запустить эту инициализацию в сопрограммах с диспетчером ввода-вывода, но безуспешно. Сопрограмма хорошо работает в другом потоке, но обратный вызов по-прежнему вызывается в основном потоке.
private val scope = CoroutineScope(Dispatchers.IO SupervisorJob())
scope.launch() {
reference.get().addOnCompleteListener { task ->
В геттере я должен работать с основным потоком. Решение, возможно, состоит в том, чтобы перевести выполнение обратного вызова в другой поток, но я не знаю, как это сделать. И, может быть, есть лучшее решение.
Другим решением будет возможность дождаться обратного вызова в главном потоке, не блокируя обратный вызов, но у меня нет решения для этого.
Есть какие-нибудь идеи ?
Ответ №1:
Я искал много решений, и вывод таков: не делайте этого.
Этот дизайн хуже, чем я думал. Android не хочет, чтобы вы блокировали основной поток даже на короткое время. Блокировка основного потока блокирует весь пользовательский интерфейс и механизм синхронизации, это действительно плохое решение.
Даже использование другого потока для обратного вызова (что вы можете сделать с исполнителем), я думаю, здесь плохая идея. Хороший способ дождаться окончания задачи при обратном вызове-получить задачу и использовать:
Tasks.await(initTask)
Но это запрещено в основном потоке. Android мешает вам делать плохой дизайн здесь.
Мы должны иметь дело с асинхронным способом управления базой данных firebase, это лучший способ сделать это. Я все еще могу использовать свой кэш для данных. Здесь я ждал, чтобы отобразить диалоговое окно с текстом, который я получаю в firebase. Таким образом, я могу просто асинхронно отображать диалоговое окно при извлечении текстовых данных. Если кэш доступен, он будет его использовать.
Имейте также в виду, что у firebase, похоже, есть какой-то API для использования кэша.