Загрузка данных из базы данных комнаты перед запросами API с использованием сопрограмм - PullRequest
0 голосов
/ 31 января 2020

Мне нужно добиться следующей логики c. Когда приложение запускается, загрузите userId из общих настроек, затем загрузите пользовательский объект из базы данных Room, чтобы, наконец, иметь возможность делать запросы на модификацию с использованием токена доступа от пользовательского объекта.

Вот то, чем я сейчас являюсь.

Инициализация репозитория пользователя с помощью Dagger при запуске приложения

class App : Application() {
    companion object {
        lateinit var userRepository: UserRepository
    }
    override fun onCreate() {
        super.onCreate()
        appComponent = DaggerAppComponent.factory().create(applicationContext)
        userRepository = appComponent.userRepository()
    }
}

Загрузка userId из sharedPrefs, а затем загрузка пользователя из БД при инициализации репозитория

@Singleton
class UserRepository @Inject constructor(
    private val dao: UserDao,
    private val sharedPrefs: SharedPreferences
) {
    var user: User
    private var userId: Int

    init {
        userId = sharedPrefs.getInt(PREFS_KEY_UID, 0)
        GlobalScope.launch(Dispatchers.IO) {
            user = dao.geUser(userId)
        }
    }
}

Перехват запросов на модификацию вставить токен в заголовок

interface Webservice {
    companion object Factory {
        private val headerInterceptor = Interceptor() {
            val newRequest = it.request().newBuilder()
                .addHeader("Authorization", "Bearer ${App.userRepository.user.token}")
                .build()
            it.proceed(newRequest)
        }
        fun create(): Webservice {
            val client = OkHttpClient.Builder()
                .addInterceptor(headerInterceptor)
                .build();
            val retrofit = retrofit2.Retrofit.Builder()
                .client(client)
                .baseUrl(BASE_URL)
                .build()
            return retrofit.create(Webservice::class.java)
        }
    }
}

Первый веб-запрос выполняется при создании ViewModel в MainActivity

class MainViewModel @Inject constructor(
    private val repository: DataRepository
): ViewModel() {
    val ticket: LiveData<Data> = repository.getData(viewModelScope)
}

@Singleton
class DataRepository @Inject constructor(
    private val webservice: Webservice,
    private val dao: DataDao
) {
    fun getData(scope: CoroutineScope): LiveData<Data> {
        refreshData(scope)
        return dao.getData()
    }
    suspend fun refreshData(scope: CoroutineScope) {
        scope.launch(Dispatchers.IO) {
            ...
            val response = webservice.getData().awaitResponse()
            ...
            dao.insert(response.body()!!)
            ...
        }
    }
}

Возможно, вы заметили, что я использую сопрограммы.

Как мне убедиться, что user загружено из базы данных до отправки веб-запроса?

Ответы [ 2 ]

0 голосов
/ 01 февраля 2020

Вот мое решение на данный момент. Спасибо Марко Топольнику за комментарии.

Просто замените GlobalScope.launch на runBlocking вот так

init {
    userId = sharedPrefs.getInt(PREFS_KEY_UID, 0)
    runBlocking(Dispatchers.IO) {
        user = dao.geUser(userId)
    }
}

Операция в моем случае легкая, поэтому без spla sh экран был нужен.

0 голосов
/ 31 января 2020

, чтобы быть уверенным, что пользователь был загружен из БД, вы можете использовать Deferred с async компоновщиком сопрограмм и вызывать функцию await при доступе к пользовательской переменной. Например:

val user: Deferred<User>

init {

   userId = sharedPrefs.getInt(PREFS_KEY_UID, 0)
   user = GlobalScope.aync {
             dao.geUser(userId)
          }
}

....

val user = repository.user.await()
...