Я пытаюсь получить ответ обратного вызова в режиме синхронизации, потому что значение ответа необходимо для работы всего приложения, без этого значения (токена) я не могу продолжать использовать приложение. Это мой объект-компаньон внутри интерфейса модернизации. Мне нужно установить токен перед созданием клиента.
Что я делаю не так?
РЕДАКТИРОВАТЬ :
Я ставлю как это Журналы, как вы пишете:
companion object {
private var mToken: String = ""
fun create(activity: Activity): MyPhoneInterface {
Log.d("tokenMyPhoneInterface", activity.localClassName)
getToken(activity)
Log.d("tokenMyPhoneInterface", "client token $mToken")
val client = OkHttpClient.Builder()
.addInterceptor { chain ->
val request = chain.request().newBuilder()
.addHeader("Authorization", mToken).build()
chain.proceed(request)
}.build()
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.baseUrl(BuildConfig.API_HOST)
.build()
return retrofit.create(MyPhoneInterface::class.java)
}
private fun getToken(activity: Activity) {
if (!activity.isFinishing && isJwtExpired(mToken)){
val latch = CountDownLatch(1)
(LoginManager(activity)).getToken(true, object : ServiceCallback<String> {
override fun success(token: String) {
Log.d("tokenMyPhoneInterface", "token $token")
mToken = token
latch.countDown()
}
override fun failure(serviceError: ServiceError) {
Log.d("tokenMyPhoneInterface", serviceError.errorMessage)
latch.countDown()
}
})
Log.d("tokenMyPhoneInterface", "before await ")
latch.await()
Log.d("tokenMyPhoneInterface", "after await")
}
}
}
Но я система заблокирована в latch.await (), а журналы:
05-14 18: 19: 00.127 848-848 / com.italy.myphone D / tokenMyPhoneInterface: view.splash.activity.Splash 05-14 18: 19: 00.131 848-848 / com.italy.myphone D / tokenMyPhoneInterface: перед ожиданием
ИЗМЕНИТЬ ответ2:
sealed class TokenResult {
class Success(val token: String) : TokenResult()
class Error(val serviceError: ServiceError) : TokenResult()}
suspend fun getToken(activity: Activity): TokenResult {
return suspendCancellableCoroutine { continuation ->
(LoginManager(activity)).getToken(true, object : ServiceCallback<String> {
override fun success(token: String) {
continuation.resume(TokenResult.Success(token))
}
override fun failure(serviceError: ServiceError) {
continuation.resume(TokenResult.Error(serviceError))
}
})
}}
И вот как я пытаюсь вызвать функцию приостановки:
companion object {
fun create(activity: Activity): MyPhoneInterface{
Log.d("tokenMyPhoneInterface", activity.localClassName)
var token = runBlocking {
return@runBlocking getToken(activity)
}
Log.d("tokenMyPhoneInterface", "obtained token")
Log.d("tokenMyPhoneInterface", "client token $tokenResult")
val client = OkHttpClient.Builder()
.addInterceptor { chain ->
val request = chain.request().newBuilder()
.addHeader("Authorization", "").build()
chain.proceed(request)
}.build()
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.baseUrl(BuildConfig.API_HOST)
.build()
return retrofit.create(MyPhoneInterface::class.java)
}
}
Это внутри интерфейса, и это код, который я использую для вызова объекта интерфейса / компаньона в действии:
private val mMyPhoneInterface by lazy {
MyPhoneInterface.create(activity)
}




Вам не следует использовать latch.count. Он равен 1, а latch.count > 1 - это false. Затем ваша функция возвращается. Просто используйте latch.await(), и он будет ждать одного из обратных вызовов.
Извините, если я ошибаюсь, у меня недостаточно репо для комментариев.
@AlessandroCandon Тогда можете ли вы обновить код и предоставить отформатированные журналы?
Спасибо) Тогда похоже, что ваша функция в действии не вызывает методы обратного вызова. Я имею в виду, что проблема не в этом коде.
Странно, почему активность это заблокирует? Я не думаю, что это возможно, потому что я только жду ответа обратного вызова, чтобы создать клиента. Вы знаете какой-нибудь обходной путь?
@AlessandroCandon Можете ли вы просто вызвать один из ваших методов обратного вызова в методе getToken() и вернуться? Это должно оставить некоторую информацию в logcat. И выложить несколько логов после звонка await().
Лучший способ получить ответ обратного вызова в режиме синхронизации - это использовать
Сопрограммы и функция suspendCancellableCoroutine
В вашем случае у вас может быть эта функция:
suspend fun getToken(activity: Activity): TokenResult {
return suspendCancellableCoroutine { continuation ->
(LoginManager(activity)).getToken(true, object : ServiceCallback<String> {
override fun success(token: String) {
continuation.resume(TokenResult.Success(token))
}
override fun failure(serviceError: ServiceError) {
continuation.resume(TokenResult.Error(serviceError))
}
})
}
}
sealed class TokenResult {
class Success(val token: String) : TokenResult()
class Error(val serviceError: ServiceError) : TokenResult()
}
А в вашем activity.onCreate:
override fun onCreate(savedInstanceState: Bundle?) = runBlocking {
super.onCreate(savedInstanceState)
val tokenResult = getToken(this)
if (tokenResult is Error){
finish()
return@runBlocking
}
//create client here with tokenResult.token value
}
Попробуйте и дайте мне знать ...
Обновлено: В этом примере я использую runBlocking, потому что getToken - это функция приостановки. В вашем собственном коде вы должны обрабатывать эту логику вне деятельности.
Обновлено: Чтобы включить сопрограммы в вашем проекте, добавьте следующие строки в свой файл gradle:
dependencies {
//other dependencies
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.22.5"
}
kotlin {
experimental {
coroutines "enable"
}
}
Мне нужно что-то импортировать? потому что студия Android выдает мне ошибку о suspendCancellableCoroutine
Разве не используются сопрограммы для обратной блокировки вызовов? Сначала вы настраиваете все для красивого асинхронного кода, а затем оборачиваете его в runBlocking. Он ничем не лучше CountDownLatch.
Использование функции runBlocking в этом примере просто для вызова функций приостановки прямо внутри функции onCreate (). Просто чтобы попробовать код.
А если я хочу, чтобы пользователь getToken не участвовал в активности? И когда я создаю клиента, я хочу вернуть его другой функции?
Просто вызовите getToken, когда захотите, в другой функции приостановки или внутри сопрограммы. Чтобы узнать больше о сопрограммах, прочтите это: github.com/Kotlin/kotlinx.coroutines/blob/master/…
Я добавляю ваше решение, но оно не работает, оно заблокировано на белой странице
Это связано с тем, что getToken долго отвечает, а активность блокируется функцией onCreate. Вы должны реализовать все это вне деятельности.
В конце я использую JavaRx для синхронизации обратного вызова .. это фрагмент.
fun getToken(loginManager: LoginManager): String {
return Single
.create(SingleOnSubscribe<String> { emitter ->
loginManager.getToken(object : TokenSimpleCallback {
override fun onSuccess(token: String) {
emitter.onSuccess(token)
}
override fun onFailure(loginServiceError: LoginServiceError) {
emitter.onError(Throwable(loginServiceError.toString()))
}
})
}).blockingGet()}
Спасибо, но это не работает, я меняю его, как вы пишете, но системный блок в ожидании без каких-либо действий ... Я также публикую это в своем ответе