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. Win8 Metro(C#)数字图像处理--2.52图像K均值聚类

    原文:Win8 Metro(C#)数字图像处理--2.52图像K均值聚类  [函数名称]   图像KMeans聚类      KMeansCluster(WriteableBitmap src,i ...

  2. Visual Studio 2017报表RDLC设计器与工具箱中Report Viewer问题

    原文:VS2017入门 RDLC入门之01 本系列所有内容为网络收集转载,版权为原作者所有. VS2017初始安装后和VS2015一样,都没有ReportDesigner/ReportViewer R ...

  3. Android零基础入门第8节:HelloWorld,我的第一趟旅程出发点

    原文:Android零基础入门第8节:HelloWorld,我的第一趟旅程出发点 经过前面几期的学习,我们知道了Android的前世今生,也大致了解了Android的系统架构和应用组件,然后花了几期来 ...

  4. C#字符串类型

    C#字符串类型(string)是一种引用类型,是System.String的别名,表示Unicode字符串. 两种表示方法: 1.“C#” 直接用双引号括起来. 2.使用@,@“c:\test”,可以 ...

  5. Tensorflow-常见报错解决方案

    1. AttributeError: 'module' object has no attribute 'SummaryWriter' tf.train.SummaryWriter 改为:tf.sum ...

  6. Ruby元编程:动态添加类属性及其实际应用

    上个星期测试道的Monkey老师和我聊到测试用例参数过多的问题,其实这样的问题在我这里也同样经历过.比如我的测试用例必须面对不同的测试环境,每个环境有无数的参数,开发的最初阶段,因为参数少,所以就放在 ...

  7. Java基础(二) 基本类型数据类型、包装类及自动拆装箱

    我们知道基本数据类型包括byte, short, int, long, float, double, char, boolean,对应的包装类分别是Byte, Short, Integer, Long ...

  8. Spring Schema扩展机制

    1:概述 Spring2.0开始,Spring提供XML Schema可扩展机制,用户可以自定义XML Schema文件,并自定义 XML Bean解析器,集成到Spring IOC容器中. 2:步骤 ...

  9. 解码mmo游戏服务器三:大地图同步(aoi)

    问题引入:aoi(area of interest).在大地图中,玩家只需要关心自己周围的对象变化,而不需要关心距离较远的对象的变化.所以大地图中的数据不需要全部广播,只要同步玩家自己视野范围的消息即 ...

  10. 使用spring容器干掉if-else

    spring容器干掉if-else 场景说明 最近新做一个项目,需要对不同店铺的商品做不同处理.例如storeA需要进行handleA操作,storeB需要进行handleB操作,如此类推 大家很容易 ...