Communicating with the UI Thread_翻译
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_翻译的更多相关文章
- 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 ...
- Android UI线程和非UI线程
Android UI线程和非UI线程 UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这 ...
- 使用WPF来创建 Metro UI程序
本文转载:http://www.cnblogs.com/TianFang/p/3184211.html 这个是我以前网上看到的一篇文章,原文地址是:Building a Metro UI with W ...
- Struts2 UI标签
表单标签的共同属性(该属性只在没有使用 simple 主题时才可以使用) form 标签 用来呈现 HTML 语言中的表单元素 默认情况下, form 标签将被呈现为一个表格形式的 HTML 表单. ...
- android脚步---如何看log之程序停止运行,和UI线程和非UI线程之间切换
经常运行eclipse时,烧到手机出现,“停止运行”,这时候得通过logcat查log了.一般这种情况属于FATAL EXCEPTION,所以检索FATAL 或者 EXCEPTION,然后往下看几行 ...
- 为什么说android UI操作不是线程安全的
转载于:http://blog.csdn.net/lvxiangan/article/details/17218409#t2 UI线程及Android的单线程模型原则 使用Worker线程 Commu ...
- Android 线程池系列教程(5)与UI线程通信要用Handler
Communicating with the UI Thread 上一课 下一课 1.This lesson teaches you to Define a Handler on the UI Thr ...
- Sending Operations to Multiple Threads_翻译
The speed and efficiency of a long-running, data-intensive operation often improves when you split i ...
- 如何分析解决Android ANR
来自: http://blog.csdn.net/tjy1985/article/details/6777346 http://blog.csdn.net/tjy1985/article/detail ...
随机推荐
- android 获取 cpu 频率信息
cpu的频率信息可以在/sys/devices/system/cpu/cpu0/cpufreq/路径下读取 比如最高频率路径为:/sys/devices/system/cpu/cpu0/cpufreq ...
- UWP入门(四)--设置控件样式
原文:UWP入门(四)--设置控件样式 官方定义:可以使用 XAML 框架通过多种方式自定义应用的外观. 通过样式可以设置控件属性,并重复使用这些设置,以便保持多个控件具有一致的外观. 可分享至不同e ...
- 利用BLCR加速android的启动(zygote加入checkpoint支持)
目前基于android4.2.2基线代码的blcr扩展,编译和启动是没有问题了,但是一重启就挂了. 弄这个有段时间了,很纠结,没有个可靠的结果,但是研究到现在,又舍不得放弃. 我想除了shuaiwen ...
- PRML Chapter4
超平面(hyperplane) 超平面:超平面是n维欧氏空间中余维度等于一的线性子空间,也就是说必须是(n-1)维度.这是平面中的直线.三维空间中平面的推广(n大于3才被称为"超" ...
- 预处理器#include 指令
预处理器发现 #include 指令后,就会寻找后跟的文件名并把这个文件的内容包含到当前文件中.被包含文件中的文本将替换源代码文件中的#include指令,就像你把被包含文件中的全部内容键入到源文件中 ...
- Understand the Qt containers(有对应表)
Container classes are one of the cornerstones of object-oriented programming, invaluable tools that ...
- Linux编辑器Vim和Emacs入门
sudo 命令 debian系统没有自带,需要安装: apt-get install sudo 安装位置为 /usr/bin/sudo,对应配置文件为 /etc/sudoers sudoers授权格式 ...
- 14 CSS权重深入
<!-- 继承说明: (1)进行样式选择时,不指定标签的话,该选择器是继承来的. (2)继承的选择器的优先级为0,和标签选择器的优先级无可比性. --> <!DOCTYPE html ...
- Hyperledger Fabric1.4的多机部署
之前的文章深入解析Hyperledger Fabric启动的全过程主要讲解了Fabric的网络搭建,以及启动的整体流程,但是都是通过单机完成的.而区块链本身就是去中心化的,所以最终还是要完成Fabri ...
- Centos7离线安装mysql8
linux版本:Centois7 mysql版本:5.7 一.安装 1.下载mysql离线安装包 下载地址:https://dev.mysql.com/downloads/mysql/ 选择如下: [ ...