我有两个小部件:
<TextView
android:id="@+id/debugView"
android:layout_width="256dp"
android:layout_height="128dp"
android:layout_marginBottom="120dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/activityAuthorization_widgets_responseStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/activityAuthorization_widgets_login"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/activityAuthorization_widgets_inputPassword" />
有这个活动(未完成):
class ActivityAuthorization : AppCompatActivity() {
private val httpClient = HttpClient()
private lateinit var debugView: TextView
private lateinit var inputLogin: TextInputEditText
private lateinit var inputPassword: TextInputEditText
private lateinit var buttonLogIn: Button
private lateinit var showPassword: CheckBox
private lateinit var responseStatusView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_authorization)
supportActionBar?.hide()
binder()
}
@SuppressLint("SetTextI18n")
@OptIn(DelicateCoroutinesApi::class)
private fun binder() {
debugView = findViewById(R.id.debugView)
inputLogin = findViewById(R.id.activityAuthorization_widgets_inputLogin)
inputPassword = findViewById(R.id.activityAuthorization_widgets_inputPassword)
buttonLogIn = findViewById(R.id.activityAuthorization_widgets_login)
showPassword = findViewById(R.id.activityAuthorization_widgets_showPassword)
responseStatusView = findViewById(R.id.activityAuthorization_widgets_responseStatus)
buttonLogIn.setOnClickListener {
GlobalScope.launch(Dispatchers.IO) {
val response: HttpResponse =
httpClient.request(
URLS.AUTHORIZATION
) {
method = HttpMethod.Get
url {
parameters.append(
URLS.AUTHORIZATION_USERNAME,
inputLogin.text.toString()
)
parameters.append(
URLS.AUTHORIZATION_PASSWORD,
inputPassword.text.toString()
)
}
}
when (response.status.value) {
200 -> completeAuthorization(response.bodyAsText())
404 -> userNotFound()
else -> unprocessedResponse()
}
}
}
showPassword.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) inputPassword.setInputType(InputType.TYPE_CLASS_TEXT)
else inputPassword.setInputType(InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD)
}
}
@SuppressLint("SetTextI18n")
private fun completeAuthorization(responseText: String) {
val responseData = JsonIterator.deserialize(
responseText,
ResponsePackets.AC.Authorization.Done::class.java
)
try {
responseData?.apply {
debugView.text = "id: ${
responseData.data.id
}\ntoken: ${responseData.data.token}"
} ?: unprocessedResponse()
} catch (_: NullPointerException) { unprocessedResponse() }
}
@SuppressLint("SetTextI18n")
private fun userNotFound() {
responseStatusView.text = "123"
}
@SuppressLint("SetTextI18n")
private fun unprocessedResponse() {
responseStatusView.text = "123"
}
}
服务器(Python Django)返回三个之一(取决于结果)
return JsonResponse({
'status': 'Done',
'data': {
'id': id,
'token': helper.DBOperator.create_token(request.META.get('HTTP_USER_AGENT'), id)
}
}, status=200)
return JsonResponse({
'status': 'Refused',
'reason': 'BadRequest',
'description': 'UserNotFound'
}, status=404)
return JsonResponse({
'status': 'Refused',
'reason': 'BadRequest',
'description': 'LackOfArguments'
}, status=406)
好吧,429,但我出于调试目的将其注释掉了。
如果状态200通过并且数据正确(而且确实是正确的),那么就id
正常token
显示。没有错误发生。但如果返回的不是 200,那么我会切换到unprocessedResponse()
(暂时也是暂时的)并得到:
2023-10-08 23:03:39.732 5012-5180 AndroidRuntime com.gct.cl.android E FATAL EXCEPTION: DefaultDispatcher-worker-4
Process: com.gct.cl.android, PID: 5012
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:11586)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:2648)
at android.view.View.requestLayout(View.java:27623)
at android.view.View.requestLayout(View.java:27623)
at android.view.View.requestLayout(View.java:27623)
at android.view.View.requestLayout(View.java:27623)
at android.view.View.requestLayout(View.java:27623)
at android.view.View.requestLayout(View.java:27623)
at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3605)
at android.view.View.requestLayout(View.java:27623)
at android.widget.TextView.checkForRelayout(TextView.java:10904)
at android.widget.TextView.setText(TextView.java:6965)
at android.widget.TextView.setText(TextView.java:6751)
at android.widget.TextView.setText(TextView.java:6703)
at com.gct.cl.android.ActivityAuthorization.userNotFound(ActivityAuthorization.kt:106)
at com.gct.cl.android.ActivityAuthorization.access$userNotFound(ActivityAuthorization.kt:23)
at com.gct.cl.android.ActivityAuthorization$binder$1$1.invokeSuspend(ActivityAuthorization.kt:76)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@a649923, Dispatchers.IO]
据我了解,该错误是由于尝试responseStatusView
从发出请求的 Coruntine 进行访问而发生的。但为什么GET 200
我没有收到类似的状态错误呢?为了以防万一,我附加了模式类和存储类:
package com.gct.cl.android;
public class ResponsePackets {
static class AC {
static class Authorization {
static class Done {
String status;
AuthData data;
}
static class ClientError {
String status;
String reason;
String description;
}
static class NotFound {
String status;
String reason;
String description;
}
}
static class AuthData {
long id;
String token;
}
}
}
package com.gct.cl.android
object URLS {
const val AUTHORIZATION = "http://192.168.196.60:8082/auth" // Я подключаюсь через прокси
const val AUTHORIZATION_USERNAME = "username"
const val AUTHORIZATION_PASSWORD = "password"
}
UDP
我将文本输出移至debugView
:
@SuppressLint("SetTextI18n")
private fun completeAuthorization(responseText: String) {
val responseData = JsonIterator.deserialize(
responseText,
ResponsePackets.AC.Authorization.Done::class.java
)
try {
runOnUiThread {
Thread {
responseData?.apply {
debugView.text = "id: ${responseData.data.id}\ntoken: ${responseData.data.token}"
} ?: unprocessedResponse()
}.start()
}
} catch (_: NullPointerException) {
unprocessedResponse()
}
}
@SuppressLint("SetTextI18n")
private fun userNotFound() {
debugView.text = "123"
}
@SuppressLint("SetTextI18n")
private fun unprocessedResponse() {
debugView.text = "123"
}
Aaand...一切都按预期进行。
我尝试这样做:
@SuppressLint("SetTextI18n")
private fun userNotFound() {
runOnUiThread {
Thread {
debugView.setText(R.string.authorization_widgets_responseStatus_userNotFound.toString())
}
}
}
Aaand...同样的错误。该代码在 4 部手机和两个模拟器上进行了测试。结果到处都一样。