`

android线程相关2

 
阅读更多

让我们通过一个交通状况查询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线程结束——合理的结束你想结束的线程

    总结,结束Android线程时,关键在于优雅、安全地中断操作,而不是简单粗暴地停止。合理的线程管理可以提升用户体验,避免ANR,并保持应用程序的稳定运行。正确使用Thread、AsyncTask、Handler和Looper等工具,结合...

    浅谈android线程模型

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

    Android多线程文件上传

    在Android应用开发中,文件上传是一项常见的任务,尤其是在处理大文件或者需要提高用户交互体验时,多线程技术显得尤为重要。本主题聚焦于"Android多线程文件上传",我们将探讨如何利用多线程技术来优化文件上传过程...

    Android开发中的多线程编程技术

    2. AsyncTask:轻量级的多线程解决方案,适合短时间的后台任务,自动管理线程生命周期,但不推荐在新版本的Android中使用。 3. IntentService:专为执行单一任务的后台服务设计,自动在单独的工作线程中运行,完成后...

    android 线程之间通过Handler发送消息

    首先,理解Android线程模型至关重要。Android应用的主要工作线程被称为UI线程或主线程,它负责处理用户界面的更新和事件响应。后台线程通常用于执行耗时任务,避免阻塞UI线程。为了在后台线程和主线程之间交换数据和...

    Android线程间通信的Message机制

    Android线程间通信的Message机制Android线程间通信的Message机制Android线程间通信的Message机制Android线程间通信的Message机制Android线程间通信的Message机制

    android 线程间通讯

    在Android系统中,线程间通信(Inter-Thread Communication,简称ITC)是应用程序开发中的重要环节,尤其在处理耗时操作或者优化UI性能时显得至关重要。线程间通信允许不同线程之间交换数据和执行任务,以确保主线程...

    android 线程暂停/恢复/退出demo

    在Android开发中,多线程是必不可少的一部分,特别是在处理耗时任务时,如网络请求、数据库操作或大计算量的任务。...通过实践这个“android 线程暂停/恢复/退出demo”,开发者可以加深对Android线程管理的理解。

    从现实生活中理解android 线程消息机制

    在Android系统中,线程消息机制是一个核心概念,它确保了应用程序的高效运行和用户体验的流畅性。这个机制主要由三部分组成:Handler、Looper和Message Queue。下面我们将从现实生活中的例子来深入理解这个机制,并...

    android多线程管理

    在Android开发中,多线程管理是至关重要的技术,它涉及到应用的性能、用户体验以及资源的有效利用。Android系统默认在主线程(UI线程)执行所有的用户交互,如果在这个线程中执行耗时操作,会导致应用程序无响应...

    Android 中三种启用线程的方法总结

    首先说明Android的CPU分配的最小单元是线程,Handler一般是在某个线程里创建的,因而Handler和Thread就是相互绑定的,一一对应。 而Runnable是一个接口,Thread是Runnable的子类。所以说,他俩都算一个进程。 ...

    Android多线程分段下载源码

    2. **任务分配**:每个线程需要知道自己的起始和结束位置,以便正确地请求服务器上的数据块。这通常通过计算文件大小和线程数来确定。 3. **HTTP请求**:使用`HttpURLConnection`或`OkHttp`等库发起HTTP GET请求,...

    Android 线程+View的使用

    在Android开发中,线程和View的交互是十分常见的需求,因为Android的UI操作必须在主线程中进行,而耗时的操作(如网络请求、大数据处理等)则应该放在子线程中。本实例主要探讨如何在子线程中更新View,通过两种方式...

    Delphi XE5例子中的一个Android线程使用的代码AnonymousThread.rar

    在Delphi XE5开发环境中,Android应用程序的多线程编程是提高应用性能和响应性的重要手段。这个名为"AnonymousThread"的示例项目旨在教你如何在Android平台上创建和管理线程,特别是在Delphi XE5环境下。让我们深入...

    Android多线程操作

    在Android应用开发中,多线程是一个至关重要的概念,它涉及到如何在后台处理耗时任务,以避免阻塞主线程并提升用户体验。标题"Android多线程操作"和描述"Android多线程开发实例,对使用多线程的用户有一定的参考价值...

    android多线程demo(很清晰很详细)

    本文将深入探讨Android多线程的相关知识点,基于“android多线程demo(很清晰很详细)”的描述,我们将围绕这个示例进行讲解。 1. **线程基础**: - **主线程**:Android应用的UI线程,负责处理用户交互和更新界面。...

    android 单线程 多线程下载

    2. **多线程下载**: 多线程下载则将大文件分割成多个部分,每个部分由一个独立的线程负责下载。这样可以提高下载速度,因为多个线程可以同时工作,即使某个线程的下载速度慢,其他线程也能继续下载,从而提高了...

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

    2. **图片绘制**: 在Android中,我们可以使用`Canvas`和`Bitmap`对象来绘制图片。首先,我们需要加载图片资源到`Bitmap`,然后在`Canvas`上进行绘制。`Canvas`提供了各种绘制方法,如`drawBitmap()`,可以让我们指定...

    Android-android简单的链式线程切换工具类简单适用可用于学习线程相关知识

    本文将深入探讨由用户分享的"ThreadUtils"工具类,它为Android开发者提供了一种简单的链式线程切换方法,有助于理解和应用线程相关知识。 首先,让我们了解Android中的线程模型。主线程,也称为UI线程,负责处理与...

    android线程启动方法源代码

    本文将深入解析Android线程启动的方法,并通过源代码示例帮助初学者理解和掌握这一关键技能。 Android系统基于Java,因此其线程机制遵循Java的基本规则,但同时也有一些特定于Android平台的特性。在Android中,主要...

Global site tag (gtag.js) - Google Analytics