我正在尝试AsyncTask,发现了一个不寻常的情况,这是我的代码。
class MainActivity : AppCompatActivity() {
lateinit var textView:TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView = findViewById(R.id.textView)
val task = MyAsyncTask()
task.execute()
}
inner class MyAsyncTask:AsyncTask<Void,Void,String>() {
override fun doInBackground(vararg params: Void?): String {
//Thread.sleep(3000)
textView.text ="From AsyncTask"
return "hello"
}
}
}
它有效!!,请参阅Thread.sleep的注释,但如果我取消注释,那么我们知道的常见错误
Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
为什么会这样,如果有人知道,请分享您的反馈。谢谢
答案 0 :(得分:0)
是时候了。当您注释Thread.sleep(3000)时,您的视图尚未准备就绪并与您的活动相关联,因此此版本的代码仅更新了UI中尚不存在的视图。
但是,经过一段时间后,UI将启动,并且当您在doInBackground()中更新视图时,您将收到一个异常,指出只有创建视图层次结构的原始线程才能触摸其视图。这也证明了取消注释Thread.sleep(3000)
时遇到的异常是合理的答案 1 :(得分:0)
因为您在以下代码中调用Thread.sleep
:
inner class MyAsyncTask:AsyncTask<Void,Void,String>() {
override fun doInBackground(vararg params: Void?): String {
Thread.sleep(3000)
textView.text ="From AsyncTask"
...
}
}
它将在当前线程上运行,当前线程是AsyncTask的后台线程。您可以在Thread.sleep文档中看到这一点:
公共静态无效睡眠(长毫秒, int nanos)
使当前正在执行的线程进入睡眠状态(暂时停止 执行)指定的毫秒数加上指定的 纳秒数,取决于系统的精度和准确性 计时器和调度程序。该线程不会失去任何所有权 显示器。
因此,下一个代码也将切换到后台线程:
textView.text ="From AsyncTask"
这将引发异常。
答案 2 :(得分:0)
当您注释掉Thread.sleep(3000)
时,您的应用仍然可以正常运行而不会崩溃,因为当时textView
在屏幕上尚不可见。要确保textView
在屏幕上可见,可以使用addOnGlobalLayoutListener。现在,无论您是否注释掉Thread.sleep(3000)
,您的应用程序总是崩溃。
class MainActivity : AppCompatActivity() {
lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView = findViewById(R.id.textView)
textView.viewTreeObserver.addOnGlobalLayoutListener {
// This view is visible on screen
val task = MyAsyncTask()
task.execute()
}
}
inner class MyAsyncTask : AsyncTask<Void, Void, String>() {
override fun doInBackground(vararg params: Void?): String {
// Thread.sleep(3000)
textView.text = "From AsyncTask From AsyncTask From AsyncTask"
return "hello"
}
}
}
另一方面,当您不注释掉Thread.sleep(3000)
时,您的应用会在3秒后立即崩溃,因为那时textView
已经在屏幕上可见,这就是为什么您的应用崩溃
那么这个异常是从哪里来的。看看ViewRootImpl source code
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
requestLayout()
如果关于您视图的某些更改会影响大小,则 您应该调用requestLayout()。这将触发onMeasure和 onLayout不仅适用于此视图,而且一直沿 父视图。
当您更新textView
的文本时,其大小将改变,这就是为什么调用requestLayout
的原因,因此,它将调用checkThread
并抛出CalledFromWrongThreadException
。
答案 3 :(得分:-1)
作为例外明确指出:
只有创建视图层次结构的原始线程才能触摸其 视图。
AsyncTask
doInBackground
在独立的后台线程中运行,而该后台线程与Main Thread无关,现在所有与UI相关的工作都应在Main线程上完成,而不是settext
doInBackground
在onpostexecute
中完成。