android中用线程更新UI,报错only the original thread that created a view hierarchy can touch its views

本文介绍在Android开发中如何正确更新UI,特别是在需要高频率更新UI的场景下,如毫秒级时钟应用。文章通过实例讲解了直接在子线程更新UI导致的问题,并演示了使用Handler机制将更新操作回调到主线程的方法。
部署运行你感兴趣的模型镜像

        Android开发中难免要更新UI,比如做一个时钟,时间总要动吧?这时间就要更新UI,但很多时间不方便把更新UI的代码放在主线程中,就比如说做一个精确度到毫秒级的时钟,因为精度是毫秒级的,那么更新时间的频率也要是毫秒级的,不然所谓的毫秒级就没什么意义了。这样一来,就要在很短的时间就更新一次UI,如果放在主线程里做,那么主线程的大部分工作是耗在这了,程序就会很卡,所以一般就会放到一个子线程中去做这些事,这样主线程就不卡了。但是,问题是不能直接在子线程中直接更新UI,先来看一下代码。

        先建一个TextView :

<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=".MainActivity" >
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />
</RelativeLayout>

        然后用它来显示当前时间,精确到毫秒,因为精确到毫秒,所以要频繁地更新UI,因此要开一个线程,不然在主线程里做这么频繁的操作,程序要卡死咯。如果直接在线程里更新UI如下:

public class MainActivity extends Activity {
	TextView tv;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tv=(TextView)findViewById(R.id.tv);
		MyThread thread=new MyThread();
		thread.start();
	}
	
	class MyThread extends Thread{
		@Override
		public void run() {
			while(true){;
				SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
				tv.setText(df.format(new Date()));
				try {
					Thread.sleep(10);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}

        这样会报错,only the original thread that created a view hierarchy can touch its views,不能直接在非主线程里更新,解决方法是用handler,代码如下:

public class MainActivity extends Activity {
	TextView tv;
	Handler handler;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tv=(TextView)findViewById(R.id.tv);
		handler=new Handler(){
			@Override
			public void handleMessage(Message msg) {
				SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
				tv.setText(df.format(new Date()));
			}
		};
		MyThread thread=new MyThread();
		thread.start();
	}
	
	class MyThread extends Thread{
		@Override
		public void run() {
			while(true){
				handler.sendEmptyMessage(0);
				try {
					Thread.sleep(10);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}


您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

### Android 更新 UI 的解决方案 在 Android 开发中,`Only the original thread that created a view hierarchy can touch its views` 是一个常见的错误提示。这是因为 Android 的视图组件(如 `TextView`, `Button` 等)只能由创建它们的主线程(也称为 UI 线程)来操作。如果尝试从其他线程直接修改这些视图,则会抛出此异常。 为了安全地更新 UI 组件,可以采用以下几种方法: #### 方法一:使用 Handler 通过 `Handler` 将耗时任务的结果传递回主线程并执行 UI 更新逻辑。这种方式适用于需要在线程间通信的情况[^1]。 ```java final Handler myHandler = new Handler(); (new Thread(new Runnable() { @Override public void run() { final String res = doLongOperation(); // 执行耗时操作 myHandler.post(new Runnable() { // 切换到主线程 @Override public void run() { updateUI(res); // 安全地更新 UI } }); } })).start(); ``` #### 方法二:使用 AsyncTask(已废弃) 虽然官方已经不推荐使用 `AsyncTask`,但在某些旧项目中仍然可能遇到它。它的设计初衷是为了简化后台任务与 UI 交互的过程。 ```java class MyTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... params) { return doLongOperation(); // 耗时操作 } @Override protected void onPostExecute(String result) { super.onPostExecute(result); updateUI(result); // 主线程更新 UI } } new MyTask().execute(); ``` 注意:自 Android API Level 30 起,`AsyncTask` 已被标记为过时,建议改用更现代的方式替代[^4]。 #### 方法三:使用 Lambda 表达式配合 View.post() 对于简单的场景,可以直接利用 `View.post()` 来调度运行于主线程的任务。 ```java ((Button)findViewById(R.id.Button01)).setOnClickListener(v -> { new Thread(() -> { int result = doLongOperation(); // 子线程完成工作 v.post(() -> updateUI(result)); // 返回主线程更新界面 }).start(); }); ``` 这种方法简洁明了,在单次调用的情况下尤为适用[^3]。 #### 方法四:借助 LiveData 和 ViewModel 当应用架构较为复杂或者涉及数据绑定时,推荐使用 Jetpack 提供的 `LiveData` 和 `ViewModel` 组合方案。这种模式不仅能够实现响应式的 UI 更新机制,还能自动处理配置更改带来的生命周期问题。 ```kotlin // Kotlin 示例代码 val liveDataResult: MutableLiveData<String> = MutableLiveData() fun performBackgroundWork() { lifecycleScope.launch(Dispatchers.IO) { val result = doLongOperation() // IO 线程上的长时间计算 withContext(Dispatchers.Main) { liveDataResult.value = result // 发布新值给观察者 } } } liveDataResult.observe(this@YourActivity, Observer { updatedValue -> updateUI(updatedValue) // 接收通知后刷新显示内容 }) ``` 以上方式均能有效规避跨线程访问引发的问题,开发者应根据实际需求选择最适合的技术手段[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值