`

[转载]Android UI 的更新及其线程模型

 
阅读更多

让我们通过一个交通状况查询Activity来讨论下Android 的UI 界面更新问题:
当用户输入区域名称,然后单击按钮进行查询后,程序会调用相应接口获得指定区域的交通状况摘要。当网络出现异常或者服务繁忙的时候都会使访问网络的动作很耗时,这时,Android会提示一个程序无法响应的异常,该对话框会询问用户是继续等待还是强行退出程序,这样就大大的降低用户体验。所以我们需要参试以别的方式来实现:

2.1 创建子线程更新UI
    显然如果你的程序需要执行耗时的操作的话,如果像上例一样由主线程来负责执行该操作是错误的。所以我们需要在onClick方法中创建一个新的子线程来负责调用相应借口来获得交通信息数据:
    public void onClick(View v) {
       //创建一个子线程执行从网络上获取交通信息的操作
       new Thread() {
           @Override
           public void run() {
              //获得用户输入的区域名称
              String zone = editText.getText().toString();
              //调用Google 交通API查询指定区域的交通情况
              String traffic = getTrafficByZone(zone);
              //把交通息显示在title上
              setTitle(traffic);
           }
       }.start();
    }

但是你会发现Android会提示程序由于异常而终止。为什么会出错呢?在LogCat中打印的日志信息就会发现这样的错误日志:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
错误信息不难看出Android禁止其他子线程来更新由UI线程创建的UI组件。本例中显示交通信息的title实际是就是一个由UI thread所创建的TextView,所以参试在一个子线程中去更改TextView的时候就出错了。这显示违背了单线程模型的原则:Android UI操作并不是线程安全的, 并且这些操作必须在UI线程中执行。啥意思,就是说如果由多个线程都对UI组件进行操作,无法保证其正确行为。

什么是线程安全?
  如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
  或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
  线程安全问题都是由全局变量及静态变量引起的。
  若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。

2.2 Message Queue
在单线程模型下,为了解决类似的问题,Android设计了一个Message Queue(消息队列),线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍:
l  Message Queue
Message Queue用来存放通过Handler发布的消息。消息队列通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列。Android在第一启动程序时会默认会为UI thread创建一个关联的消息队列,用来管理程序的一些上层组件,activities,broadcast receivers 等等。你可以在自己的子线程中创建Handler与UI thread通讯。

2  Handler
通过Handler你可以发布或者处理一个消息或者是一个Runnable的实例。没个Handler都会与唯一的一个线程以及该线程的消息队列管理。当你创建一个新的Handler时候,默认情况下,它将关联到创建它的这个线程和该线程的消息队列。也就是说,如果你通过Handler发布消息的话,消息将只会发送到与它关联的这个消息队列,因而也只能处理该消息队列中的消息。
主要的方法有:
a)   public final boolean sendMessage(Message msg)
把消息放入该Handler所关联的消息队列,放置在所有当前时间前未被处理的消息后。
b)   public void handleMessage(Message msg)
关联该消息队列的线程将通过调用Handler的handleMessage方法来接收和处理消息,通常需要子类化Handler来实现handleMessage。

3  Looper
Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。
a)   可以通过Looper类的静态方法Looper.myLooper得到当前线程的Looper实例,如果当前线程未关联一个Looper实例,该方法将返回空。
b)   可以通过静态方法Looper. getMainLooper方法得到主线程的Looper实例
线程,消息队列,Handler,Looper之间的关系可以通过一个图来展示:

现在将把交通信息的案例通过消息队列来重新实现:

private EditText editText;
    private Handler messageHandler;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        editText = (EditText) findViewById(R.id.weather_city_edit);
        Button button = (Button) findViewById(R.id.goQuery);
        button.setOnClickListener(this);
        //得到当前线程的Looper实例,由于当前线程是UI线程也可以通过Looper.getMainLooper()得到
        Looper looper = Looper.myLooper();
        //此处甚至可以不需要设置Looper,因为 Handler默认就使用当前线程的Looper
        messageHandler = new MessageHandler(looper);
    }

    @Override
    public void onClick(View v) {
        //创建一个子线程去做耗时的网络操作
        new Thread() {
            @Override
            public void run() {
                //活动用户输入的区域名称
                String zone = editText.getText().toString();
                //调用Google 交通API查询指定城市的交通情况
                String trafficInfo = getTrafficInfoByZone(zone);
                //创建一个Message对象,并把得到的交通信息赋值给Message对象
                Message message = Message.obtain();
                message.obj = trafficInfo;
                //通过Handler发送携带有交通情况的消息
                messageHandler.sendMessage(message);
            }
        }.start();
    }

    //子类化一个Handler
    class MessageHandler extends Handler {
        public MessageHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            //处理收到的消息,把交通信息显示在title上
            setTitle((String) msg.obj);
        }
    }

现在程序已经可以成功运行,因为Handler的handleMessage方法是由关联有该消息队列的UI 线程调用的,从而回避了线程安全问题。

 

 

原文转载: Android UI 的更新及其线程模型

分享到:
评论

相关推荐

    浅谈android线程模型

    ### 浅谈Android线程模型:深入理解与实践 #### 引言 随着智能手机的普及和技术的不断进步,Google的Android操作系统成为了移动设备领域的重要力量。Android不仅为用户提供了丰富的功能,也为开发者提供了广阔的...

    Xamarin.Android 非UI线程更新UI

    总结起来,Xamarin.Android的非UI线程更新UI涉及了对Android线程模型的理解以及各种线程间通信技术的运用。通过Handler、RunOnUiThread、AsyncTask等方法,我们可以确保UI操作始终在主线程中安全执行。遵循最佳实践...

    android 更新 UI 线程 handler

    在Android开发中,更新UI(用户界面)是一个常见的任务,但是由于Android的主线程(也称为UI线程)是负责处理用户交互和绘制界面的,所以直接在后台线程进行UI更新可能会导致程序崩溃或者界面卡顿。为了解决这个问题...

    Android 线程更新UI

    在学习和实践中,理解Android线程模型和正确更新UI的方法对于提升应用性能和用户体验至关重要。确保遵循最佳实践,避免主线程阻塞,合理安排任务执行,以及正确处理线程间的通信。通过实践和调试,你将能熟练掌握...

    android asynctask的fragment更新UI(附线程管理)

    首先,让我们了解Android的线程模型。主线程,也称为UI线程,负责处理用户交互和更新UI。由于Android系统的安全机制,任何耗时的操作都不能在主线程中执行,否则会导致应用无响应(ANR)。因此,我们需要在后台线程...

    Android的线程使用来更新UI

    在Android开发中,由于UI操作必须在主线程(也称为UI线程)中进行,因此在后台线程处理任务并更新界面时需要遵循特定的机制。以下是对标题和描述中涉及知识点的详细解释: 1. **线程模型**: Android系统采用单...

    android多线程模型和service分析

    ### Android多线程模型与Service深入解析 #### 线程与进程概念解析 在深入探讨Android中的多线程模型之前,我们先明确一下基本概念。在计算机科学中,**进程**(Process)与**线程**(Thread)是两个核心概念。进程是...

    UI界面多线程简单实现和线程捕获异常问题

    但是,需要注意的是,由于UI操作必须在主线程中进行,因此当后台线程需要更新UI时,需要使用`Control.Invoke`或`Control.BeginInvoke`方法来调度到主线程执行。例如: ```csharp this.Invoke(new Action(() => { /...

    UI线程

    在Android系统中,UI线程特别受到重视,因为Android应用遵循单一UI线程模型。这意味着所有对用户界面的修改必须在UI线程内完成。如果其他线程尝试更新UI,Android会抛出`android.os.NetworkOnMainThreadException`或...

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

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

    [毕设季 android开发]第六讲:Android线程及UI更新

    在Android应用开发中,线程管理和UI更新是至关重要的部分,因为Android系统为每个应用程序分配一个主线程,也称为UI线程。主线程主要负责处理用户界面的交互,包括绘制和更新视图。然而,执行耗时的操作(如网络请求...

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

    为了解决这个问题,Android提供了多种异步处理机制,其中一种常见的方式是使用`Thread`配合`Handler`来实现非UI线程执行耗时任务并更新UI。本篇将详细介绍这一方法。 首先,我们需要理解Android的线程模型。Android...

    Android系统Surface机制的SurfaceFlinger服务的线程模型分析报告.doc

    本分析报告将深入探讨SurfaceFlinger服务的线程模型,主要包括Binder线程、UI渲染线程和控制台事件监控线程三者的运作机制及其相互作用。\n\n首先,SurfaceFlinger服务的启动始于System进程的主线程,该主线程会创建...

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

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

    Android----线程实现图片移动

    首先,我们需要理解Android的线程模型。主线程,也被称为UI线程,负责处理所有的用户交互,如触摸事件和UI更新。为了在不影响主线程的情况下执行耗时的操作,我们通常会创建子线程。在这个场景中,我们将在线程中...

    对Android App UI线程的一点理解

    在Android应用开发中,UI线程,也被称为主线程,是负责处理用户界面更新和事件响应的核心线程。本文将深入探讨Android App UI线程的工作原理、重要性以及如何优化其性能。 首先,理解Android UI线程的工作机制至关...

    Android系统Surface机制的SurfaceFlinger服务的线程模型分析.doc

    总结来说,Android系统Surface机制的SurfaceFlinger服务通过精细设计的线程模型,实现了高效的UI渲染和系统级的通信。Binder线程负责处理跨进程通信,UI渲染线程专注于UI的合成与渲染,而控制台事件监控线程则保证了...

    Android Handler定时更新UI

    Android应用的主要执行线程被称为“主线程”或“UI线程”,它负责处理用户交互和更新UI。然而,长时间运行的任务(如网络请求或数据库操作)不应在主线程中执行,因为这可能导致应用无响应。为了解决这个问题,...

    android 线程模型

    在Android系统中,主线程,也被称为UI线程,是应用程序的核心,它负责处理所有的用户交互和界面更新。如果在主线程中执行耗时操作,如数据库查询或网络请求,会导致UI线程被阻塞,从而影响用户体验,甚至触发系统的...

Global site tag (gtag.js) - Google Analytics