`
JasonShieh
  • 浏览: 531659 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

UI线程

阅读更多
Android线程模型
Android中的线程模型
这篇文章将讨论Android应用程序中使用的线程模型,并讨论如何确保应用程序最佳的UI呈现(通过创建工作者线程来处理耗时的操作,而不是在主线程里处理)。这篇文章还将阐述与运行在主线程中的UI组件交互的API以及创建托管的工作者线程的API。

UI线程

当应用程序启动后,系统创建了一个叫做“main”的线程。主线程,也叫UI线程,非常重要,因为它负责分发事件给构件,包括绘制事件。也是这个线程,在这里才能与Android UI工具包中的组件进行交互。

例如,当你触摸屏幕上的一个按钮时,UI线程会分发一个触摸事件给构件,然后,构件会设定自己为被按下的状态,并抛出一个显示无效的请求给事件队列。UI线程队列请求并通知构件绘制自己。

单线程模型会导致性能低下,除非你的程序很好地实现。特别是,当所有的操作都在单一的线程中进行,耗时的操作(如网络访问、数据查询)会阻塞UI。在耗时操作执行时,没有任何事件可以分发,包括绘制的事件。从用户的视觉来看,应用程序被挂起了。更糟糕的是,如果UI线程阻塞超过一定的时间(现在大约是5秒钟),系统会给用户呈现一个糟糕的“应用程序无响应”(ANR)对话框。

如果你想看这有多糟糕,你可以写一个简单的应用程序,在一个Button的OnClickListener函数中调用Thread.sleep(2000)。按钮在回到它正常状态之前,保持被按下的状态2秒钟。当这种情况发生时,用户很容易认为应用程序慢。

总之,对于应用程序UI的响应性来说,保证UI线程不被阻塞是至关重要的。如果你有耗时的操作,你应该确保在另外的线程(后台或工作者线程)中执行。

下面有一个例子,点击事件处理函数中,从网络上下载一个图片,并显示到ImageView上:

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
      Bitmap b = loadImageFromNetwork();
      mImageView.setImageBitmap(b);
    }
  }).start();
}
乍一看,这段代码能很好的解决你的问题,因为它不会阻塞UI线程。遗憾的是,它违背了UI的单线程模型:Android UI工具包不是线程安全的,必须在UI线程中进行操作。在上面的代码片段里,ImageView是在工作者线程中操作的,因此,这会引发可拍的问题。跟踪和修正这些Bug可能是困难且耗时的。

Android提供了一些方法,能在其它线程中访问UI线程。你可能对其中的一些已经很熟悉了,但这里是一份较为全面的列表:

· Activity.runOnUiThread(Runnable)

· View.post(Runnable)

· View.postDelayed(Runnable, long)

· Handler

你可以使用这些类和方法中的任一来修正上面的例子代码:

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
      final Bitmap b = loadImageFromNetwork();
      mImageView.post(new Runnable() {
        public void run() {
          mImageView.setImageBitmap(b);
        }
      });
    }
  }).start();
}
不幸的是,这些类和方法可能会使你的代码变得更加复杂并难以阅读。特别是,当你实现一个复杂的操作,而在这个操作中,需要频繁地更新UI。

为了解决这个问题,Android 1.5和它之后的平台提供了一个通用的类——AsyncTask,其简化了长时间运行任务的创建过程,而这些任务还能做到与UI进行交互。

在Android 1.0和1.1上,也有与AsyncTask类似的东西,叫做UserTask。它提供了相同的API,而你需要做的只是拷贝其中的代码。

AsyncTask的目的是帮助你管理线程。我们之前的例子可以很容易用AsyncTask进行改写:

public void onClick(View v) {
  new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
     protected Bitmap doInBackground(String... urls) {
         return loadImageFromNetwork(urls[0]);
     }

     protected void onPostExecute(Bitmap result) {
         mImageView.setImageBitmap(result);
     }
}
如你所见,AsyncTask必须继承使用。此外,还必须记住的是AsyncTask的实例必须在UI线程中创建并只能执行一次。你可以阅读AsyncTask的文档来全面了解如何使用,但这里,只是简要地描述它以及它的工作过程:

· 你可以使用泛型来指定参数、进度值和最终结果的类型

· doInBackground() 方法自动在工作者线程中执行

· onPreExecute(),onPostExecute()和onProgressUpdate()在主线程中调用

· doInBackground()中返回的值会发送给onPostExecute()方法

· 你可以在doInBackground()中随时调用publishProgress()来执行onProgressUpdate()

· 你可以任何时候从任何线程中取消任务

除了官方的文档,你还可以参考几个复杂例子的源代码,如Shelves(ShelvesActivity.java和AddBookActivity.java)和Photostream(LoginActivity,PhotostreamActivity.java和ViewPhotoActivity.java)。我们强烈地建议你阅读Shelves的源代码,来了解配置变更时任务的保存以及Activity销毁时如何正确地取消任务。

不管你是否使用AsyncTask,在单线程模型中始终要记住两条法则:

1. 不要阻塞UI线程

2. 确保只在UI线程中访问Android UI工具包

分享到:
评论

相关推荐

    UI 线程 和 工作线程 的实现

    在计算机编程中,尤其是开发图形用户界面(GUI)应用程序时,UI线程(用户界面线程)和工作线程的概念至关重要。UI线程是应用程序的主要线程,负责处理与用户交互相关的所有事件,如鼠标点击、键盘输入以及窗口的...

    无界面的UI线程交互

    在软件开发中,UI线程(用户界面线程)通常负责处理用户交互和更新界面,而后台线程则用于执行耗时的操作,如计算或网络通信,以避免阻塞UI,确保用户界面的流畅性。"无界面的UI线程交互"这个主题探讨的是在没有直接...

    MFC UI 线程的使用

    本篇文章将深入探讨"MFC UI线程的使用",包括CWinThread类的运用、UI线程间的消息传递机制,以及如何利用ON_THREAD_MESSAGE宏和PostThreadMessage函数来实现线程间的通信。 首先,我们来看MFC中的CWinThread类。...

    MFC创建UI线程

    MFC创建UI线程是应用程序设计中的一个重要环节,尤其对于那些需要处理用户界面更新和响应用户交互的复杂应用。线程允许应用程序同时执行多个任务,提高性能和用户体验。下面我们将深入探讨如何在MFC中创建UI线程以及...

    ndroid异步处理一:使用Thread+Handler实现非UI线程更新UI界面

    在Android开发中,UI线程(主线程)主要负责用户界面的绘制与交互,因此它必须保持流畅,避免被耗时操作阻塞。为了解决这个问题,Android提供了多种异步处理机制,其中一种常见的方式是使用`Thread`配合`Handler`来...

    Android-dialog库可以在任意类内调用子线程或ui线程内均可显示

    标题提到的“Android-dialog库”是Android开发中的一个第三方库,它提供了一种灵活的方式,使得开发者能够在任意类中方便地调用Dialog,并且支持在子线程或UI线程内显示,这极大地提高了代码的可复用性和执行效率。...

    工作线程 UI线程实例

    因为Android系统规定所有与UI相关的操作必须在UI线程中执行,所以保证其流畅运行是提升用户体验的关键。 而工作线程,顾名思义,是用来执行耗时任务的。这些任务可能包括网络请求、数据库操作、大量计算等。使用...

    Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面

    ### Android异步处理详解:使用Thread+Handler实现非UI线程更新UI界面 #### 概述 Android系统中,每一个应用程序都运行在一个独立的Dalvik虚拟机进程中。当应用程序启动时,系统会创建一个主线程(MainThread),也...

    OkHttp简单封装,支持进度UI线程回调

    【OkHttp简单封装,支持进度UI线程回调】 在Android应用开发中,网络请求是必不可少的部分,而OkHttp作为一款高效的网络库,被广泛应用。本项目"OkHttpDemo"对OkHttp进行了简单的封装,旨在简化网络操作,同时提供...

    WinForm后台线程与UI线程通讯

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

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

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

    多UI线程界面

    这种设计模式虽然简单,但当UI线程被长时间运行的任务阻塞时,就会出现标题中提到的问题:界面卡顿、控件响应慢,甚至整个界面假死。为了解决这个问题,开发者需要理解和掌握多UI线程的概念和技术。 多UI线程允许...

    VC 线程 创建 操作 同步 终止 UI线程等各种线程例子

    它们通常用于执行计算密集型任务或长时间运行的任务,以免阻塞UI线程。 8. **线程的挂起和恢复**: - `SuspendThread`函数可挂起一个线程,暂停其执行,而`ResumeThread`函数恢复已挂起线程的执行。 9. **线程的...

    WPFC#.net 跨线程更新UI 线程测试 解决界面卡死

    Thread 达到跨线程更新UI 虽然使用Dispatcher.Invoke 和模拟winform 里面的DoEvent 但是运行中关闭还是会有异常,而且耗资源高; 第二种 : DispatcherTimer 失败:UI还是会卡顿; 第三种 : Timer 建议使用、资源...

    安卓UI线程和子线程通讯更新UI实例

    在Android应用开发中,UI线程(也称为主线程)负责处理用户交互和绘制界面,而子线程通常用于执行耗时操作,如网络请求、数据处理等。由于Android系统的安全机制,直接在子线程中修改UI是不允许的,因此我们需要通过...

    安卓UI线程机制 ,在子线程中访问UI

    在Android系统中,UI线程(也称为主线程)是应用程序与用户交互的主要通道,它负责处理所有的用户界面更新和事件响应。理解Android的UI线程机制至关重要,因为错误地在非UI线程中操作UI组件会导致应用崩溃或出现不...

Global site tag (gtag.js) - Google Analytics