Open fred-ye opened 10 years ago
有如下一段代码,定义了一个很普通的Activity,然后在其onCreate()方法中启了一个子线程去刷UI.
public class TestUIThreadActivity extends Activity { private TextView tvTest; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test_ui_thread); tvTest = (TextView)findViewById(R.id.tv_test); new ThreadOne().start(); } private class ThreadOne extends Thread { @Override public void run() { tvTest.setText(“ABC"); } } }
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:my="http://schemas.android.com/apk/res/com.example.test" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/tv_test" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=“AAA" /> </RelativeLayout>
程序运行的结果是ABC显示出来了,虽然是在子线程中刷的UI,但是android系统没有报任何异常信息。如果将子线程的启动放在onResume()方法中,结果也是一样。
ABC
但是如果我们把程序这样改一下,在程序中添加一个Button,点击Button的时候才会去刷新UI,更改文字的内容,结果又会是什么呢?
public class TestUIThreadActivity extends Activity { private TextView tvTest; private Button btnTest; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test_ui_thread); tvTest = (TextView)findViewById(R.id.tv_test); btnTest = (Button)findViewById(R.id.btn_test); btnTest.setOnClickListener(listener); } private View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View view) { new ThreadOne().start(); } }; private class ThreadOne extends Thread { @Override public void run() { tvTest.setText("bbb"); } } }
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:my="http://schemas.android.com/apk/res/com.example.test" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/tv_test" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="AAA" /> <Button android:id="@+id/btn_test" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/tv_test" android:text="Click Me"/> </RelativeLayout>
运行时发现有异常抛出:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
我们期待的在子线程中不能刷UI的异常出现了。 其实如果在第一段代码中,让子线程延迟5秒这个样子再去刷UI,同样也会报这个异常。那么为什么第一段代码中直接刷UI不会出现异常呢?我们很容易想到,当我们直接在onCreate方法中调用子线程刷UI时,此时UI层都还没有展示出来。所有UI的更新,都会调用到相应组件的invalidate()方法,最后会调用到ViewRootImpl中的invalidateChildInParent方法,在这个方法中会调checkThread()。直接执行时,是因为ViewRootImpl还没有创建出来。ViewRootImpl的创建是在onResume方法完成。
onCreate
onResume
有如下一段代码,定义了一个很普通的Activity,然后在其onCreate()方法中启了一个子线程去刷UI.
一段代码
Activity
很普通的布局文件
程序运行的结果是
ABC
显示出来了,虽然是在子线程中刷的UI,但是android系统没有报任何异常信息。如果将子线程的启动放在onResume()方法中,结果也是一样。但是如果我们把程序这样改一下,在程序中添加一个Button,点击Button的时候才会去刷新UI,更改文字的内容,结果又会是什么呢?
第二段代码
Activity中
布局文件中
运行时发现有异常抛出:
我们期待的在子线程中不能刷UI的异常出现了。 其实如果在第一段代码中,让子线程延迟5秒这个样子再去刷UI,同样也会报这个异常。那么为什么第一段代码中直接刷UI不会出现异常呢?我们很容易想到,当我们直接在
onCreate
方法中调用子线程刷UI时,此时UI层都还没有展示出来。所有UI的更新,都会调用到相应组件的invalidate()方法,最后会调用到ViewRootImpl中的invalidateChildInParent方法,在这个方法中会调checkThread()。直接执行时,是因为ViewRootImpl还没有创建出来。ViewRootImpl的创建是在onResume
方法完成。