本文共 5801 字,大约阅读时间需要 19 分钟。
前面的教程指导您如何在线程池线程上运行代码,向您展示如何在由其管理的线程上启动任务 ThreadPoolExecutor。本最后一课向您展示了如何将数据从任务发送到在用户界面(UI)线程上运行的对象。此功能允许您的任务执行后台工作,然后将结果移至UI元素(如位图)。
每个应用程序都有自己的特殊线程来运行诸如对象之类的UI View 对象; 这个线程被称为UI线程。只有在UI线程上运行的对象才能访问该线程上的其他对象。因为您在线程池中的线程上运行的任务 未在UI线程上运行,所以他们无法访问UI对象。要将数据从后台线程移动到UI线程,请使用在UI线程Handler上运行的数据。
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对象的引用存储在任务对象中。接下来,将任务对象和状态码传递给实例化该对象的对象Handler。在这个对象中,发送一个Message包含状态和任务对象给Handler。由于Handler正在UI线程上运行,因此它可以将数据移动到UI对象。
例如,下面是一个Runnable在后台线程上运行的×××Bitmap,它将其解码 并存储在其父对象中PhotoTask。该Runnable还存储状态代码DECODE_STATE_COMPLETED。
// A class that decodes photo files into Bitmapsclass 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上的多线程操作的信息,请参阅指南。
要尝试本指南中的概念,请下载。
Lastest Update:2018.04.17
QQ:94297366
微信打赏:
公众号推荐:
转载于:https://blog.51cto.com/4789781/2124451