我需要实现以下逻辑。当应用程序启动时,从共享的首选项中加载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
,然后在存储库初始化时从数据库加载用户
@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)
}
}
}
创建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
?
为了确保已从db加载用户,可以在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()
这是我目前的解决方案。感谢Marko Topolnik的想法。
在存储库中,我用自定义的初始化函数替换了init
块
fun init() {
userId = sharedPrefs.getInt(PREFS_KEY_UID, 0)
user = dao.geUser(userId)
}
并在MainActivity
布局膨胀之前运行它并阻塞
override fun onCreate(savedInstanceState: Bundle?) {
runBlocking(Dispatchers.IO) {
App.userRepository.init()
}
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
}
该操作非常轻巧,因此不需要启动屏幕。