0


Android 子线程更新主线程UI视图

  1. 消息机制,对于Android开发者来说,应该是非常熟悉。对于处理有着大量交互的场景,采用消息机制,是再好不过了。在Android开发中,子线程不能更新主线程UI,而主线程又不能进行耗时操作(例:网络请求),一种常用的处理方法就是,在子线程中进行耗时操作,完成之后发送消息,通知主线程更新UI。或者使用异步任务,异步任务的实质也是对消息机制的封装。

我们在子线程去更改主线程视图时,都会抛异常:

Only the original thread that created a view hierarchy can touch its views.

  1. AndroidRuntime: FATAL EXCEPTION: Thread-176
  2. Process: com.example.joy.messagetest, PID: 11201
  3. android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
  4. at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:635)
  5. at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:87)
  1. Android实现View更新有两组方法,分别是invalidatepostInvalidate。前者在UI线程中使用,后者在非UI线程即子线程中使用。在子线程调用 invalidate 方法会导致线程不安全。熟悉View工作原理的人都知道,invalidate 方法会通知 view 立即重绘,刷新界面。假如,现在我用 invalidate 在子线程中刷新界面,同时UI线程也在用 invalidate 刷新界面,这样会不会导致界面的刷新不能同步?这就是invalidate不能在子线程中使用的原因。

  但是我们可以在子线程执行某段代码,需要更新UI的时候去通知主线程,让主线程来更新。如何做呢?常见的方法,除了前面提到的在UI线程创建Handler,在子线程发送消息到UI线程,通知UI线程更新UI,还有 handler.post(Runnable r)、 view.post(Runnable r)、activity.runOnUIThread(Runnable r)等方法。发现其实它们的实现原理都还是一样,最终都是通过Handler发送消息来实现的。

xml页面:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:paddingBottom="@dimen/activity_vertical_margin"
  7. android:paddingLeft="@dimen/activity_horizontal_margin"
  8. android:paddingRight="@dimen/activity_horizontal_margin"
  9. android:paddingTop="@dimen/activity_vertical_margin"
  10. tools:context="com.example.joy.messagetest.MainActivity">
  11. <TextView
  12. android:id="@+id/tv_test"
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:layout_centerHorizontal="true"
  16. android:text="Hello World!" />
  17. <Button
  18. android:id="@+id/btn_test2"
  19. android:layout_below="@id/btn_test1"
  20. android:layout_width="wrap_content"
  21. android:layout_height="wrap_content"
  22. android:layout_centerHorizontal="true"
  23. android:text="Handler发送消息"/>
  24. <Button
  25. android:id="@+id/btn_test3"
  26. android:layout_below="@id/btn_test2"
  27. android:layout_width="wrap_content"
  28. android:layout_height="wrap_content"
  29. android:layout_centerHorizontal="true"
  30. android:text="Handler.Post"/>
  31. <Button
  32. android:id="@+id/btn_test4"
  33. android:layout_below="@id/btn_test3"
  34. android:layout_width="wrap_content"
  35. android:layout_height="wrap_content"
  36. android:layout_centerHorizontal="true"
  37. android:text="View.Post"/>
  38. <Button
  39. android:id="@+id/btn_test5"
  40. android:layout_below="@id/btn_test4"
  41. android:layout_width="wrap_content"
  42. android:layout_height="wrap_content"
  43. android:layout_centerHorizontal="true"
  44. android:text="Activity.RunOnUIThread"/>
  45. </RelativeLayout>

java代码如下:

  1. package com.example.joy.messagetest;
  2. import android.os.AsyncTask;
  3. import android.os.Handler;
  4. import android.os.Looper;
  5. import android.os.Message;
  6. import android.support.v7.app.AppCompatActivity;
  7. import android.os.Bundle;
  8. import android.view.View;
  9. import android.widget.Button;
  10. import android.widget.TextView;
  11. import android.widget.Toast;
  12. public class MainActivity extends AppCompatActivity implements View.OnClickListener {
  13. private TextView mTvTest;
  14. private Button mBtnTest1;
  15. private Button mBtnTest2;
  16. private Button mBtnTest3;
  17. private Button mBtnTest4;
  18. private Button mBtnTest5;
  19. private Handler mHandler = new Handler() {
  20. @Override
  21. public void handleMessage(Message msg) {
  22. super.handleMessage(msg);
  23. if (msg.what == 100) {
  24. mTvTest.setText("由Handler发送消息");
  25. }
  26. }
  27. };
  28. @Override
  29. protected void onCreate(Bundle savedInstanceState) {
  30. super.onCreate(savedInstanceState);
  31. setContentView(R.layout.activity_main);
  32. initView();
  33. new Thread(new Runnable() {
  34. @Override
  35. public void run() {
  36. mTvTest.setText("子线程可以更新UI");
  37. }
  38. }).start();
  39. }
  40. private void initView() {
  41. mTvTest = (TextView) findViewById(R.id.tv_test);
  42. mBtnTest2 = (Button) findViewById(R.id.btn_test2);
  43. mBtnTest3 = (Button) findViewById(R.id.btn_test3);
  44. mBtnTest4 = (Button) findViewById(R.id.btn_test4);
  45. mBtnTest5 = (Button) findViewById(R.id.btn_test5);
  46. mBtnTest2.setOnClickListener(this);
  47. mBtnTest2.setOnClickListener(this);
  48. mBtnTest3.setOnClickListener(this);
  49. mBtnTest4.setOnClickListener(this);
  50. mBtnTest5.setOnClickListener(this);
  51. }
  52. @Override
  53. public void onClick(View v) {
  54. switch (v.getId()) {
  55. case R.id.btn_test2: //通过发送消息
  56. new Thread(new Runnable() {
  57. @Override
  58. public void run() {
  59. mHandler.sendEmptyMessage(100);
  60. }
  61. }).start();
  62. break;
  63. case R.id.btn_test3: //通过Handler.post方法
  64. new Thread(new Runnable() {
  65. @Override
  66. public void run() {
  67. mHandler.post(new Runnable() {
  68. @Override
  69. public void run() {
  70. mTvTest.setText("handler.post");
  71. }
  72. });
  73. }
  74. }).start();
  75. break;
  76. case R.id.btn_test4: //通过 view.post方法
  77. new Thread(new Runnable() {
  78. @Override
  79. public void run() {
  80. mTvTest.post(new Runnable() {
  81. @Override
  82. public void run() {
  83. mTvTest.setText("view.post");
  84. }
  85. });
  86. }
  87. }).start();
  88. break;
  89. case R.id.btn_test5: //通过 activity 的 runOnUiThread方法
  90. new Thread(new Runnable() {
  91. @Override
  92. public void run() {
  93. runOnUiThread(new Runnable() {
  94. @Override
  95. public void run() {
  96. mTvTest.setText("runOnUIThread");
  97. }
  98. });
  99. }
  100. }).start();
  101. break;
  102. default:
  103. break;
  104. }
  105. }
  106. }

只有初始创建视图的线程才能触碰这些视图,也就是说只有主线程才能更新UI。


本文转载自: https://blog.csdn.net/minusn/article/details/126589121
版权归原作者 老杜_d 所有, 如有侵权,请联系我们删除。

“Android 子线程更新主线程UI视图”的评论:

还没有评论