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. delphi中使用词霸2005的动态库XdictGrb.dll实现屏幕取词

    近日来,在网上发现关于屏幕取词技术的捷径,搜索很长时间,发现实现方式以VB出现的居多,但是通过Delphi来实现的却好象没有看到,自己参考着VB的相关代码琢磨了一下通过delphi来实现的方式. 其实 ...

  2. Win8 Metro(C#)数字图像处理--2.50图像运动模糊

    原文:Win8 Metro(C#)数字图像处理--2.50图像运动模糊  [函数名称] 图像运动模糊算法    MotionblurProcess(WriteableBitmap src,int  ...

  3. ubuntu下建立golang的build脚本

    在不在os中设置gopath,goroot的情况下 建立build.sh文件,文件内容如下: export GOARCH="386"export GOBIN="/home ...

  4. SqlServer 监控发布中未分发的命令数

    原文:SqlServer 监控发布中未分发的命令数 对于查看未分发的命令数,我们通常这样查看. 然而当服务器有很多发布时,一个个打开查看就很麻烦 当然,如果想用脚本查看就更方便了,运行下面的语句 -- ...

  5. WPF WindowChrome 自定义窗口

    1.wpf自定义窗口: WindowChrome类描述:https://msdn.microsoft.com/zh-cn/library/system.windows.shell.windowchro ...

  6. git服务器创建,冲突解决,远程仓库获取指定文件

    1.git服务器创建 在公司多人协作开发的情况下,不能简单地使用github,因为github是互联网公开的,这种情况公司的代码的保密性就会丧失了.这种情况下,需要创建git服务器. 登录服务器,使用 ...

  7. 中国2017 Google 开发者大会第二天简单回顾

    昨天早晨发布了第一天的开发者大会回顾文章后,就匆匆忙忙赶去会场继续享受高科技的盛宴,接下来简单回顾一下第二天的大会参与情况. 昨天早晨下着小雨,并带着微风,在外面还是挺冷的,这里不得不给工作人员点个赞 ...

  8. isHiden和isVisible的区别(可是有nativeEvent进行设置)

    之前一直对isHiden和isVisible的区别比较模糊,都是乱用的.今天因需要仔细看了一下. 1.isHiden只是返回部件的隐藏属性,并不能表示部件当前的真实状态.比如A部件有个子部件B,而A处 ...

  9. qt获得本地IP的方法,qt中域名解析的方法

    本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 环境:Ubuntu10.04 + Qt4.7.0 Linux获得本地IP的方法,我尝试了两种 1.用QH ...

  10. Qt实现小功能之列表无限加载(创意很不错:监听滚动条事件,到底部的时候再new QListWidgetItem)

    概念介绍 无限加载与瀑布流的结合在Web前端开发中的效果非常新颖,对于网页内容具备较好的表现形式.无限加载并没有一次性将内容全部加载进来,而是通过监听滚动条事件来刷新内容的.当用户往下拖动滚动条或使用 ...