概要
前面的教程指导您如何在线程池线程上运行代码,向您展示如何在由其管理的线程上启动任务 ThreadPoolExecutor。本最后一课向您展示了如何将数据从任务发送到在用户界面(UI)线程上运行的对象。此功能允许您的任务执行后台工作,然后将结果移至UI元素(如位图)。
每个应用程序都有自己的特殊线程来运行诸如对象之类的UI View 对象; 这个线程被称为UI线程。只有在UI线程上运行的对象才能访问该线程上的其他对象。因为您在线程池中的线程上运行的任务 未在UI线程上运行,所以他们无法访问UI对象。要将数据从后台线程移动到UI线程,请使用在UI线程Handler上运行的数据。
在UI线程上定义一个处理程序
Handler是Android系统管理线程框架的一部分。甲 Handler对象接收消息并运行的代码来处理消息。通常,您Handler为一个新线程创建一个线程,但您也可以创建一个Handler与现有线程连接的线程。当你连接Handler到你的UI线程时,处理消息的代码在UI线程上运行。
Handler在创建线程池的类的构造函数中 实例化对象,并将该对象存储在全局变量中。通过使用Handler(Looper) 构造函数实例化它,将它连接到UI线程。这个构造函数使用一个Looper对象,这是Android系统线程管理框架的另一部分。当你Handler基于一个特定的Looper实例来实例化一个 实例的时候, Handler它就像在同一个线程上运行Looper。例如
private PhotoManager() {
...
// Defines a Handler object that's attached to the UI thread
mHandler = new Handler(Looper.getMainLooper()) {
...
在里面Handler,覆盖handleMessage()方法。当Android系统收到它正在管理的线程的新消息时调用此方法; 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;
...
}
...
}
}
下一节将介绍如何告诉Handler移动数据。
将数据从任务移至UI线程
要将数据从后台线程上运行的任务对象移动到UI线程上的对象,请首先将对数据和UI对象的引用存储在任务对象中。接下来,将任务对象和状态码传递给实例化该对象的对象Handler。在这个对象中,发送一个Message包含状态和任务对象给Handler。由于Handler正在UI线程上运行,因此它可以将数据移动到UI对象。
将数据存储在任务对象中
例如,下面是一个Runnable在后台线程上运行的×××Bitmap,它将其解码 并存储在其父对象中PhotoTask。该Runnable还存储状态代码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还包含ImageView显示的句柄Bitmap。即使到引用Bitmap,并ImageView在同一个对象,你不能分配Bitmap到ImageView,因为你目前没有在UI线程上运行。
相反,下一步是将此状态发送给PhotoTask对象。
向对象层次发送状态
PhotoTask是层次结构中的下一个更高级的对象。它保持对解码数据和View将显示数据的对象的引用。它接收一个状态码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);
}
...
}
将数据移动到用户界面
从PhotoTask对象中,PhotoManager对象接收状态码和PhotoTask对象的句柄。因为状态是 TASK_COMPLETE,创建一个Message包含状态和任务对象并将其发送到Handler:
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;
...
}
...
}
最后,Handler.handleMessage()检查每个传入的状态码Message。如果状态码是 TASK_COMPLETE,则任务完成,并且该PhotoTask对象Message包含a Bitmap和an ImageView。由于 Handler.handleMessage()正在UI线程上运行,因此可以安全地将其Bitmap移至 ImageView:
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);
}
...
}
...
}
...
}
...
}
更多信息
要了解更多关于Android上的多线程操作的信息,请参阅过程和线程概述指南。
示例应用
要尝试本指南中的概念,请下载ThreadSample。
Lastest Update:2018.04.17
联系我
QQ:94297366
微信打赏:https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ
公众号推荐: