`

关于在非UI线程中更新UI控件不会crash的问题

阅读更多

昨天同事问我一个问题,在Thread中去更新一个Button的状态可行吗?我说当然不行。他告诉我说,那为什么他的程序不会奔溃,我过去一看果然没有奔溃,甚是奇怪。难道是我记错了?于是我Google了一下。发现Android的开发文档中确实说这样是不行的啊。

http://developer.android.com/guide/components/processes-and-threads.html

 

  1. Do not access the Android UI toolkit from outside the UI thread

 

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();
}

 it violates the second rule of the single-threaded model: do not access the Android UI toolkit from outside the UI thread—this sample modifies the ImageView from the worker thread instead of the UI thread. This can result in undefined and unexpected behavior, which can be difficult and time-consuming to track down.

 

 

再看看我同事的代码:

 

public class MainActivity extends Activity
{

	private Button btn;
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btn = (Button)findViewById(R.id.btn);
		startAThread();
	}
	
	private void startAThread()
	{
		new Thread(new Runnable() {
			
			@Override
			public void run()
			{
				btn.setText("Hello Michael!");
			}
		}).start();
	}
}

 

 

没有发现什么大的区别,唯一的区别就在于执行的地方不太一样,Google官方文档是在onClick的时候去执行,而同事的代码是在onCreate的时候执行。于是我做了一些修改,增加一个onClick事件:

 

 

public class MainActivity extends Activity
{

	private Button btn;
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btn = (Button)findViewById(R.id.btn);
		
		btn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v)
			{
				startAThread();
			}
		});
		startAThread();
	}

	private void startAThread()
	{
		new Thread(new Runnable() {
			
			@Override
			public void run()
			{
				btn.setText("Hello Michael!");
			}
		}).start();
	}

}

 

 

果然,程序在onClick的时候crash了。

但是为什么呢?在onCreate的时候去不会crash呢?

 

我们看一下错误:

 

这个错误相信大家都有遇到过,意思就是说不能在非UI线程中去更新UI控件。顺着这个错误,

到源码中去查找一下ViewRootImpl.java这个文件。你可以看到这个异常:

 

void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

 

 

这个方法就是用来判断更新UI控件的时候是否在UI线程中进行,如果不是的话就会报错。也就是onCreate的时候这个判断没有被执行到,因此onCreate中启动的Thread并不会导致程序的crash。

 

不知道这算不算Google的一个bug呢,虽然这样没有什么问题,但是也不建议大家这样做。因为这样可能会卡住UI线程,而且也没有必要这样做,Activity.runOnUiThread,View.Post,或者麻烦一点用AsyncTask,Handler这样的方式来处理都是Google比较推荐的做法。

 

 

 

 

  • 大小: 7.8 KB
分享到:
评论

相关推荐

    C# 线程更新UI界面

    特别是在UI(用户界面)开发中,为了保证界面的流畅性,通常我们会将耗时操作放在后台线程进行,而更新UI则需要在主线程(也称为UI线程)中完成。这是因为UI控件不是线程安全的,多个线程并发访问可能导致数据不一致...

    C# 跨线程访问UI线程控件

    C# 跨线程访问UI线程控件 在C#中,由于使用线程和调用UI的线程属于两个不同的线程,如果在线程中直接设置UI元素的属性,此时就会出现跨线程错误。    下面介绍两种解决方案  第一种:使用控件自带的Invoke或者...

    c#多线程中子线程动态改变ui控件

    特别是在C#多线程编程中,子线程更新UI控件是一个常见的问题。本文将总结C#子线程更新UI控件的方法,介绍两种常用的方法,以便读者更好地理解和应用。 一、使用控件自身的Invoke/BeginInvoke方法 Control类实现了...

    C#子线程更新UI控件的方法实例总结

    在C#编程中,特别是在开发桌面应用程序(如WinForms或WPF)时,经常会遇到需要在子线程中更新UI控件的情况。由于UI界面通常运行在主线程中,为保证用户界面的响应性和避免线程冲突,我们需要遵循特定的规则来安全地...

    WPF 使用线程更新UI

    为了解决这个问题,WPF引入了多线程技术,允许开发者在后台线程处理耗时任务,然后通过特定的方式安全地更新UI。 标题“WPF 使用线程更新UI”主要涉及到以下几个关键知识点: 1. **后台线程与主线程**:主线程是...

    qt编程_在子线程中更新UI界面

    为了解决这个问题,我们可以将这些操作放在子线程中执行,然后通过特定机制更新UI。以下是对这个主题的详细说明: 1. **线程基础知识**:在多线程编程中,线程是程序中的执行单元。主线程主要负责处理UI交互,而子...

    C#WinForm跨线程更新UI的四种方法

    当后台线程需要更新UI时,首先创建一个Delegate,然后调用控件的Invoke方法,这样可以确保在UI线程中安全地执行更新操作。 示例代码: ```csharp private delegate void UpdateTextDelegate(string text); ...

    UI 线程 和 工作线程 的实现

    为了在UI线程中安全地更新控件,我们需要使用消息机制,如发送自定义消息到主线程。可以使用`PostMessage()`或`SendMessage()`函数,但前者更推荐,因为它是非阻塞的,不会阻塞工作线程。 5. **在主线程中接收并...

    WPF后台线程更新UI

    - **线程安全问题**:由于.NET Framework的UI控件不支持多线程访问,后台线程直接修改UI会导致数据竞争和同步问题。 **2. Dispatcher的使用** - **Invoke方法**:后台线程可以通过调用`Dispatcher`的`Invoke`方法将...

    用多线程更新UI

    在这个例子中,`DownloadData`方法在后台线程运行,下载完成后,`RunWorkerCompleted`事件在UI线程中触发,调用`BackgroundTaskCompleted`方法,该方法使用`BeginInvoke`更新UI控件。 除了`Invoke`和`BeginInvoke`...

    WinForm C# 多线程编程并更新界面(UI)

    下面我们来讨论如何在 WinForm 中使用 C# 实现多线程编程并更新界面(UI)。 多线程编程 多线程编程是一种使应用程序可以同时执行多个任务的技术。它可以提高应用程序的响应速度和用户体验。在 WinForm 中,我们...

    WinForm后台线程与UI线程通讯

    由于.NET Framework的安全机制,直接在非UI线程修改UI组件可能会导致线程不安全的问题,因此需要正确地进行线程间通信。本文将详细探讨如何在WinForm应用中实现在后台线程与UI线程之间的通信,并确保UI更新的安全。 ...

    Winform实现多线程异步更新UI(进度及状态信息)

    `DoWork`事件在后台线程中运行,`ProgressChanged`和`RunWorkerCompleted`事件在UI线程中运行,确保了UI的安全更新。 7. **异常处理**:在`Worker_DoWork`方法中,我们检查了`CancellationPending`属性以允许用户...

    c#一个简单的线程实例 委托控制控件

    本实例将探讨如何使用C#创建一个简单的线程,并通过委托来控制UI控件,确保在非UI线程中安全地更新界面。 标题中的“c#一个简单的线程实例 委托控制控件”表明我们将学习如何启动一个新的线程,并使用委托来与UI...

    浅谈C#跨线程调用窗体控件(比如TextBox)引发的线程安全问题

    浅谈C#跨线程调用窗体控件引发的线程安全问题 C#跨线程调用窗体控件时可能会引发线程安全问题,例如当...在C#中跨线程调用窗体控件时,需要确保以线程安全方式访问控件,以避免出现不一致的状态和其他线程相关的问题。

    UI线程

    在IT行业中,UI线程(User Interface Thread)是应用程序中负责处理用户交互的线程,尤其是在多线程环境中。UI线程通常被称为主线程,它的主要任务是更新和响应用户界面的操作,例如点击按钮、输入文本等。由于用户...

    c#子线程操作UI控件的简单委托 包含带参数和不带参数源码

    这是因为UI更新必须在主线程(也称为UI线程)中进行,以保证线程安全和避免界面卡顿。本文将深入探讨如何在C#中使用委托实现子线程对UI控件的操作,包括带参数和不带参数的场景,并提供源码实例。 1. 跨线程操作UI...

    VC多线程中控制界面控件的几种方法

    在多线程编程中,我们经常需要在工作线程中执行某些操作后更新UI线程中的控件状态。最简单的方式之一是通过成员函数作为回调函数来实现。这种方法的核心思想是在主线程(通常是UI线程)中定义一个成员函数,然后在...

    MFC子线程中更新控件内容的两种办法

    然而,直接在子线程中更新UI控件可能会导致程序崩溃或不稳定,这是因为UI控件的操作通常需要在主线程中进行。因此,在MFC中,我们需要采用合适的方法确保UI更新操作在主线程中执行。本文将详细介绍MFC中更新控件内容...

Global site tag (gtag.js) - Google Analytics