In the previous lesson you learned how to start a task on a thread managed by ThreadPoolExecutor. This final lesson shows you how to send data from the task to objects running on the user interface (UI) thread. This feature allows your tasks to do background work and then move the results to UI elements such as bitmaps.

在上一篇中,你已经学到了如火如荼通过ThreadPoolExecutor类来将一个任务交给一个线程处理。这篇文章将向你展示如何将来自任务中的数据发送给主线程。这将使得你的任务在后台运行,然后将运行的结果发送给主线程,来更新界面元素,比如在界面上显示bitmap。

Every app has its own special thread that runs UI objects such as View objects; this thread is called the UI thread. Only objects running on the UI thread have access to other objects on that thread. Because tasks that you run on a thread from a thread pool aren't running on your UI thread, they don't have access to UI objects. To move data from a background thread to the UI thread, use a Handler that's running on the UI thread.

每个应用都有它自己的主线程,用来控制界面控件,比如view控件。只有运行于主线程内部的控件才能访问同一线程中的其他控件。由于通过线程池来运行的任务不是运行于主线程,因此这些线程池的线程是不能访问UI界面的控件的。为了能昂线程池中的线程执行的结果能够影响主线程使用handler,它是运行在主线程的。

Define a Handler on the UI Thread


Handler is part of the Android system's framework for managing threads. A Handler object receives messages and runs code to handle the messages. Normally, you create a Handler for a new thread, but you can also create a Handler that's connected to an existing thread. When you connect a Handler to your UI thread, the code that handles messages runs on the UI thread.

Handler是安卓系统框架的一个部分,用来管理线程的。一个Handler对象如果接收到了一个消息,那么就会运行一些代码来处理这个消息。通常,你可以为一个新线程创建一个Handler,你也可以创建一个Handler用来连接一个已经存在的线程。不过,你要是将一个Handler连接到你的主线程,那么Handler运行的处理消息的代码就运行在主线程中。难道可以为一般的线程也创建对应的Handler

Instantiate the Handler object in the constructor for the class that creates your thread pools, and store the object in a global variable. Connect it to the UI thread by instantiating it with the Handler(Looper) constructor. This constructor uses a Looper object, which is another part of the Android system's thread management framework. When you instantiate a Handler based on a particular Looper instance, the Handler runs on the same thread as the Looper. For example:

在创建线程池的类的构造器中,初始化Handler对象,而且将这个Handler对象设置为一个全局变量。再通过Handler(Looper)构造器将Handler对象附属到主线程。Handler(Looper)构造器会使用一个Looper对象,Looper对象是安卓系统中线程管理框架的一个组成部分。当你使用一个Looper实例来初始化一个Handler对象,那么Handler对象将与Looper运行在同一个线程中。例如:

private PhotoManager() {
...
// Defines a Handler object that's attached to the UI thread
mHandler = new Handler(Looper.getMainLooper())
{
...

  Inside the Handler, override the handleMessage() method. The Android system invokes this method when it receives a new message
for a thread it's managing; all of the Handler objects for a particular thread receive the same message. For example:

在Handler内部,你要重写handleMessage()方法。当Handler接收到来自它附属的线程发来的消息时,系统会自动调用handleMessage方法。一个线程所有的Handler对象将会接收到同一个消息。例如;

/*
* handleMessage() defines the operations to perform when
* the Handler receives a new Message to process.
*/
@Override public void handleMessage(Message inputMessage) {
// Gets the image task from the incoming Message object.
PhotoTask photoTask = (PhotoTask) inputMessage.obj;
...
}
...
}
}
The next section shows how to tell the Handler to move data.

  

Move Data from a Task to the UI Thread


To move data from a task object running on a background thread to an object on the UI thread, start by storing references to the data and the UI object in the task object. Next, pass the task object and a status code to the object that instantiated the Handler. In this object, send a Message containing the status and the task object to the Handler. Because Handler is running on the UI thread, it can move the data to the UI object.

如果想将工作线程运行的数据传递给主线程中的某个对象,可以在任务中存储这个数据数据和这个对象的引用。然后呢,将这个任务还有执行这个任务后的状态码传递给创建handler的对象。在这个对象中,将包含这个状态码以及任务对象的消息发送给handler。由于handler运行在主线程中,因此它可以将任务产生的结果传递给主线程的对象。说的有点糊涂。

Store data in the task object

For example, here's a Runnable, running on a background thread, that decodes a Bitmap and stores it in its parent object PhotoTask. The Runnable also stores the status code DECODE_STATE_COMPLETED.

比如,有一个运行在线程中的任务,这个任务会解码一张图片,并将解码后的图片存储在它的父类phototask中。同时,这个任务也存储有状态码DECODE_STATE_COMPLETED.

// A class that decodes photo files into Bitmaps
class PhotoDecodeRunnable implements Runnable {
...
PhotoDecodeRunnable(PhotoTask downloadTask) {
mPhotoTask = downloadTask;
}
...
// Gets the downloaded byte array
byte[] imageBuffer = mPhotoTask.getByteBuffer();
...
// Runs the code for this task
public void run() {
...
// Tries to decode the image buffer
returnBitmap = BitmapFactory.decodeByteArray(
imageBuffer,
0,
imageBuffer.length,
bitmapOptions
);
...
// Sets the ImageView Bitmap
mPhotoTask.setImage(returnBitmap);
// Reports a status of "completed"
mPhotoTask.handleDecodeState(DECODE_STATE_COMPLETED);
...
}
...
}
...

  PhotoTask also contains a handle to the ImageView that displays the Bitmap. Even though references to the Bitmap and ImageView are in the same object, you can't assign the Bitmap to the ImageView, because you're not currently running on the UI thread.

phototask还包含着imageview的一个句柄,这个imageview将来是要显示解码后的图片的。尽管解码后的图片和imageview在同一个对象中,你也不能直接在这个对象中就让imageview显示图片,因为这个任务不是运行在主线程中。

Instead, the next step is to send this status to the PhotoTask object.

下一个步骤就是讲任务执行的状态码发送给phototask对象。

Send status up the object hierarchy

PhotoTask is the next higher object in the hierarchy. It maintains references to the decoded data and the View object that will show the data. It receives a status code from PhotoDecodeRunnable and passes it along to the object that maintains thread pools and instantiates Handler:

PhotoTask维护着图片的引用,也维护者显示这个图片的imageview的引用。他会从PhotoDecodeRunnable接受一个状态码,再将它传递给创建了线程池和初始化了handler的对象:

public class PhotoTask {
...
// Gets a handle to the object that creates the thread pools
sPhotoManager = PhotoManager.getInstance();
...
public void handleDecodeState(int state) {
int outState;
// Converts the decode state to the overall state.
switch(state) {
case PhotoDecodeRunnable.DECODE_STATE_COMPLETED:
outState = PhotoManager.TASK_COMPLETE;
break;
...
}
...
// Calls the generalized state method
handleState(outState);
}
...
// Passes the state to PhotoManager
void handleState(int state) {
/*
* Passes a handle to this task and the
* current state to the class that created
* the thread pools
*/
sPhotoManager.handleState(this, state);
}
...
}

  

Move data to the UI

From the PhotoTask object, the PhotoManager object receives a status code and a handle to the PhotoTask object. Because the status is TASK_COMPLETE, creates a Message containing the state and task object and sends it to the Handler:

PhotoManager类是PhotoTask类的成员,PhotoTask接受状态码,以及phototask的引用。因为解码任务已经完成,因此,PhotoTask创建一个消息,该消息包含了状态码和任务对象。

public class PhotoManager {
...
// Handle status messages from tasks
public void handleState(PhotoTask photoTask, int state) {
switch (state) {
...
// The task finished downloading and decoding the image
case TASK_COMPLETE:
/*
* Creates a message for the Handler
* with the state and the task object
*/
Message completeMessage =
mHandler.obtainMessage(state, photoTask);
completeMessage.sendToTarget();
break;
...
}
...
}

  Finally, Handler.handleMessage() checks the status code for each incoming Message. If the status code is TASK_COMPLETE, then the task is finished, and the PhotoTask object in the Message contains both a Bitmap and an ImageView. Because Handler.handleMessage()
is running on the UI thread, it can safely move the Bitmap to the ImageView:

最后,Handler.handleMessage()检查传入的每一个消息中的状态码。如果状态码是解码完成的状态码的话,任务就结束了。

private PhotoManager() {
...
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message inputMessage) {
// Gets the task from the incoming Message object.
PhotoTask photoTask = (PhotoTask) inputMessage.obj;
// Gets the ImageView for this task
PhotoView localView = photoTask.getPhotoView();
...
switch (inputMessage.what) {
...
// The decoding is done
case TASK_COMPLETE:
/*
* Moves the Bitmap from the task
* to the View
*/
localView.setImageBitmap(photoTask.getImage());
break;
...
default:
/*
* Pass along other messages from the UI
*/
super.handleMessage(inputMessage);
}
...
}
...
}
...
}
...
}

  核心就是线程发送给主线程的消息包含一个状态码以及一个对象。对象中包含图片,以及主线程中准备显示图片的控件。平时写的代码只是线程给主线程单独发送一个数字,图片是以全局变量保存的,然后让handler处理这个全局变量的图片。

Communicating with the UI Thread_翻译的更多相关文章

  1. Running Code on a Thread Pool Thread_翻译

    The previous lesson showed you how to define a class that manages thread pools and the tasks that ru ...

  2. Android UI线程和非UI线程

    Android UI线程和非UI线程 UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这 ...

  3. 使用WPF来创建 Metro UI程序

    本文转载:http://www.cnblogs.com/TianFang/p/3184211.html 这个是我以前网上看到的一篇文章,原文地址是:Building a Metro UI with W ...

  4. Struts2 UI标签

    表单标签的共同属性(该属性只在没有使用 simple 主题时才可以使用) form 标签  用来呈现 HTML 语言中的表单元素 默认情况下, form 标签将被呈现为一个表格形式的 HTML 表单. ...

  5. android脚步---如何看log之程序停止运行,和UI线程和非UI线程之间切换

    经常运行eclipse时,烧到手机出现,“停止运行”,这时候得通过logcat查log了.一般这种情况属于FATAL EXCEPTION,所以检索FATAL 或者 EXCEPTION,然后往下看几行 ...

  6. 为什么说android UI操作不是线程安全的

    转载于:http://blog.csdn.net/lvxiangan/article/details/17218409#t2 UI线程及Android的单线程模型原则 使用Worker线程 Commu ...

  7. Android 线程池系列教程(5)与UI线程通信要用Handler

    Communicating with the UI Thread 上一课 下一课 1.This lesson teaches you to Define a Handler on the UI Thr ...

  8. Sending Operations to Multiple Threads_翻译

    The speed and efficiency of a long-running, data-intensive operation often improves when you split i ...

  9. 如何分析解决Android ANR

    来自: http://blog.csdn.net/tjy1985/article/details/6777346 http://blog.csdn.net/tjy1985/article/detail ...

随机推荐

  1. 通过异步程序调用(APC)实现的定时功能

    定时器是一个在特定时间或者规则间隔被激发的内核对象.结合定时器的异步程序调用可以允许回调函数在任何定时器被激发的时候执行.本文的例子代码显示了如何实现. 使用本定时器时,你需要把常量_WIN32_WI ...

  2. Qt5 结构及模块组成?

    作为一个Qt的粉丝,对将于明年发布的Qt5充满了期待.可是想想Qt5将发生的巨大变化,心底又有点不安.Qt5到底会变成什么样呢? 看看近期Qt5的一些大动作: 从 QtCore中移除 QSetting ...

  3. 多线程基础理论--C#

    1.主线程 进程创建时,默认创建一个线程,这个线程就是主线程.主线程是产生其他子线程的线程,同时,主线程必须是最后一个结束执行的线程,它完成各种关闭其他子线程的操作.尽管主线程是程序开始时自动创建的, ...

  4. 【操作系统】关于Linux桌面操作系统

    以前是Win+Ubuntu+黑苹果,周末想体验一下deepin,于是简单安装了一下,安装过程很简单,这里不再描述.安装之后,第一次打开系统,确实很惊艳,赏心悦目的操作系统. 之前用Ubuntu时候,C ...

  5. uni-app中Vuex的引用

    //store 中 store.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const store = new Vue ...

  6. 函数式编程里的Materialization应该翻译成什么?

    Materialization是函数式编程里的一个专业术语, 用于特指函数式编程中查询被实际执行并生成结果的这一过程. 首先, 搜了一下中文资料, 暂时没有对该词的中文翻译,  CSDN\博客园\阿里 ...

  7. PHP实现图片(文件)上传

    这几天整理做过的php项目,感觉这个经常会用到,传上来共享一下咯 首先,前端界面 1.表单的首行需要加上enctype="multipart/form-data",需要上传的图片必 ...

  8. 使用fastjson读取超巨json文件引起的GC问题

    项目中需要将巨量数据生成的json文件解析,并写入数据库,使用了 alibaba 的 fastjson,在实践过程中遇到了 GC 问题,记录如下: 数据大约为70万条,文件大小在3~4G左右,使用 f ...

  9. 简单介绍几种Java后台开发常用框架组合

    01 前言 Java框架一直以来都是面试必备的知识点,而掌握Java框架,不管在成熟的大公司,快速发展的公司,还是创业阶段的公司,都能对当前正在开发中的系统有整体的认知,从而更好的熟悉和学习技术,这篇 ...

  10. PhpStorm 配置 PHPUnit

    配置说明 全局安装phpunit代码 composer global require phpunit/phpunit 该代码会自动保存在 /User/你的用户名/.composer/vendor/ph ...