使用协同程序在API请求之前从会议室数据库加载数据

时间:2020-01-31 13:13:25

标签: android kotlin retrofit2 android-room kotlin-coroutines

我需要实现以下逻辑。当应用程序启动时,从共享的首选项中加载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()
    }
}

从sharedPrefs加载userId,然后在存储库初始化时从db加载用户

@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)
        }
    }
}

Intercept Retrofit请求将令牌插入标头

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)
        }
    }
}

创建MainActivity的ViewModel时发出第一个Web请求

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()!!)
            ...
        }
    }
}

您可能已经注意到我在使用协程。

在发送Web请求之前,如何确保从数据库中加载了user

2 个答案:

答案 0 :(得分:0)

为确保已从数据库加载用户,可以将Deferredasync协程构建器一起使用,并在访问用户变量时调用await函数。例如:

val user: Deferred<User>

init {

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

....

val user = repository.user.await()

答案 1 :(得分:0)

这是我目前的解决方案。感谢Marko Topolnik的评论。

像这样用GlobalScope.launch替换runBlocking

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

在我看来,该操作重量轻,因此不需要启动屏幕。