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

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

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

AndroidRuntime: FATAL EXCEPTION: Thread-176Process: com.example.joy.messagetest, PID: 11201android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:635)at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:87)

Android实现View更新有两组方法,分别是invalidate和postInvalidate。前者在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页面:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.joy.messagetest.MainActivity"><TextViewandroid:id="@+id/tv_test"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:text="Hello World!" /><Buttonandroid:id="@+id/btn_test2"android:layout_below="@id/btn_test1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:text="Handler发送消息"/><Buttonandroid:id="@+id/btn_test3"android:layout_below="@id/btn_test2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:text="Handler.Post"/><Buttonandroid:id="@+id/btn_test4"android:layout_below="@id/btn_test3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:text="View.Post"/><Buttonandroid:id="@+id/btn_test5"android:layout_below="@id/btn_test4"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:text="Activity.RunOnUIThread"/></RelativeLayout>

java代码如下:

package com.example.joy.messagetest;import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;public class MainActivity extends AppCompatActivity implements View.OnClickListener {private TextView mTvTest;private Button mBtnTest1;private Button mBtnTest2;private Button mBtnTest3;private Button mBtnTest4;private Button mBtnTest5;private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (msg.what == 100) {mTvTest.setText("由Handler发送消息");}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();new Thread(new Runnable() {@Overridepublic void run() {mTvTest.setText("子线程可以更新UI");}}).start();}private void initView() {mTvTest = (TextView) findViewById(R.id.tv_test);mBtnTest2 = (Button) findViewById(R.id.btn_test2);mBtnTest3 = (Button) findViewById(R.id.btn_test3);mBtnTest4 = (Button) findViewById(R.id.btn_test4);mBtnTest5 = (Button) findViewById(R.id.btn_test5); mBtnTest2.setOnClickListener(this);mBtnTest2.setOnClickListener(this);mBtnTest3.setOnClickListener(this);mBtnTest4.setOnClickListener(this);mBtnTest5.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_test2:   //通过发送消息new Thread(new Runnable() {@Overridepublic void run() {mHandler.sendEmptyMessage(100);}}).start();break;case R.id.btn_test3:  //通过Handler.post方法new Thread(new Runnable() {@Overridepublic void run() {mHandler.post(new Runnable() {@Overridepublic void run() {mTvTest.setText("handler.post");}});}}).start();break;case R.id.btn_test4:  //通过 view.post方法new Thread(new Runnable() {@Overridepublic void run() {mTvTest.post(new Runnable() {@Overridepublic void run() {mTvTest.setText("view.post");}});}}).start();break;case R.id.btn_test5:  //通过 activity 的 runOnUiThread方法new Thread(new Runnable() {@Overridepublic void run() {runOnUiThread(new Runnable() {@Overridepublic void run() {mTvTest.setText("runOnUIThread");}});}}).start();break;default:break;}}
}

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

Android 子线程更新主线程UI视图相关推荐

  1. 在子线程更新主线程的UI组件

    1.实例化一个 private Handler handlerBublishTopic = new Handler(Looper.getMainLooper()); 2. handlerBublish ...

  2. 守护线程C语言windows,C言语如何利用子线程刷新主线程

    C言语如何利用子线程刷新主线程 你知道C言语如何利用子线程刷新主线程吗?使用子线程进行时间操作和加法操作,然后刷新主线程的控件显示结果.下面是小编为大家带来的关于C言语如何利用子线程刷新主线程的知识, ...

  3. OpenGauss线程管理-主线程-Postmaster(1)

    OpenGauss线程管理-主线程-Postmaster(1) 主线程postmaster负责内存.全局信息.信号.线程池等的初始化,用来创建其他子线程,OpenGauss是单进程多线程,在程序启动时 ...

  4. java中主线程首先执行_java经典面试题:子线程先运行30次主线程,主线程40次,如此循环50次?...

    最近偶遇这道题,网上相似的题都是循环次数不一样.然而我百度搜到的论坛或者博客感觉都不太对,运行有穿插.请给出正确结果. 我们假使所有人都引入了业务对象. 并且我有疑问?感觉题目本意不是new Thre ...

  5. 每个java程序都至少有一个线程给主线程,java程序在主线程中判断各个子线程状态的操作,该如何解决...

    java程序在主线程中判断各个子线程状态的操作 每个子线程在队列为空时会wait等待其他线程添加新url到队列,到最后所有子线程都取不到url时也会都wait住,要在主线程中判断如果所有的子线程都是w ...

  6. Java-主线程捕获子线程异常并回滚子线程及主线程

    最近有个需求-当删除旧数据然后保存新数据时,只有删除旧数据成功同时保存新数据成功才可以不然的话就回滚,因为新数据保存那数据量比较大由子线程完成,然后删除数据是在主线程完成. 在方法上加上如下注解,看似 ...

  7. 3.13_显示界面线程是主线程,其他的都是子线程

    只要是自己开的线程就都属于子线程,在子线程里面要去修改主线程中的控件显示,或者界面刷新时,就都要用到invoke和beginInvoke,调用者就是这个控件,说明要将其委托的逻辑处理方法方法结果传到主 ...

  8. python 子线程阻塞主线程,Python多处理/线程阻塞主线程

    我正在尝试用Python编写一个程序.我想写的是一个脚本,它会立即向用户返回友好的消息,但会在后台生成一个很长的子进程,该子进程处理多个不同的文件并将它们写入一个原始文件.我已经做了一些关于线程和处理 ...

  9. 【sfu】network线程和主线程

    这里是就是指worker线程,所有任务都跑在这. 这个worker应该就是网络线程了. 这里的线程id 是 0x00006d70 主线程是 0x00011454 WrtcAgentPc::asyncT ...

最新文章

  1. 奇淫异巧之 PHP 后门
  2. 在Centos 5.2下编译安装LAMP
  3. 有趣java_有趣的Java
  4. Visual C++ 时尚编程百例009(响应键盘)
  5. 制作Geek风格的投影片
  6. java自定义异常(Exception、throws、try-catch)
  7. 富文本支持粘贴excel表格_Anki插件-OneNote importer(富文本批量导入)
  8. Homebrew太慢,如何挂代理加速
  9. java 全局钩子_[原]在全局鼠标钩子中模拟鼠标右键单击
  10. SG90舵机的电路连接和驱动(树莓派)
  11. matlab eemd输出,如何使用eemd工具包
  12. 第二章 计算机的运算方法
  13. 自动驾驶之-MATLAB环境下基于深度学习的语义分割
  14. 如何修改Outlook数据文件(.ost)的存放位置
  15. 五千来字小作文,是的,我们是有个HTTP。
  16. 导图解房(01)黄金圈法则解读 买房这件事儿
  17. L1、L2、smooth L1三类损失函数
  18. 计算机专业对数学英语要求高吗,大学专科计算机专业对英语数学的要求高么
  19. 一文读懂java内部类
  20. spring成神之路第二十九篇:BeanFactory 扩展(BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor)...

热门文章

  1. 游戏控 简要性格分析
  2. 知识点滴 - 性格分析-四类法
  3. SAP ABAP 代码修改自动比较对象版本一致
  4. This Week in Spring - March 17th, 2020
  5. 大数据|Hive和数据仓库
  6. android模拟支付宝芝麻信用(上)
  7. 《十五》微信小程序中的插件
  8. Windows 杀死80端口进程
  9. 【软件测试】金3银4,APP面试题经验分享
  10. 为了注册一个当前可注册的最短的QQ英文账号邮箱,我做了什么?(2020.08.20编写,今天才发现文章没有发出来,一直在草稿箱)