Skip to content

Latest commit

 

History

History
353 lines (307 loc) · 12.3 KB

将操作发送到多个线程——(5)与UI线程通信.md

File metadata and controls

353 lines (307 loc) · 12.3 KB

将操作发送到多个线程——与UI线程通信

前一篇文章将操作发送到多个线程——在线程池中的线程中运行代码,向您展示如何在ThreadPoolExecutor管理的线程上启动任务。本文将向您展示如何将任务中的数据发送给在用户界面(UI)线程上运行的对象。这允许您的任务执行后台工作,然后将结果返回到UI元素(如bitmap)。

每个应用程序都有自己的特殊线程来运行UI对象(例如View对象),这个线程称为UI线程。只有在UI线程上运行的对象才能访问该线程上的其他对象。由于您在线程池中的线程中运行的任务未在UI线程上运行,因此它们无权访问UI中的对象。要将数据从后台线程发送到UI线程,请使用在UI线程上运行的Handler

在UI线程定义一个Handler

HandlerAndroid系统Framework层的一部分,其作用是管理线程。 Handler对象接收消息并运行代码来处理消息。通常,您为新线程创建一个Handler,但您也可以创建一个连接到现有线程的Handler。将Handler连接到UI线程时,处理消息的代码会在UI线程上运行。

在构造创建线程池的类的过程中实例化Handler对象,并将对象存储在全局变量中。通过使用Handler(Looper)构造函数将其实例化,将其连接到UI线程。此构造函数使用Looper对象,这是Android系统的线程管理框架的另一部分。当您基于特定的Looper实例实例化Handler时,Handler在与Looper相同的线程上运行。例如:

  • kotlin

  • object PhotoManager {
    ...
        private val handler: Handler = Handler(Looper.getMainLooper())
        ...
    }
  • java

  • private PhotoManager() {
    ...
        // Defines a Handler object that's attached to the UI thread
        handler = new Handler(Looper.getMainLooper()) {
        ...

Handler中,重写handleMessage()方法,Android系统会在接收到新message的时候调用handleMessage()方法,一个线程的所有Handler都会接收到相同的消息。比如:

  • kotlin

  • object PhotoManager {
        private val handler: Handler = object : Handler(Looper.getMainLooper()) {
            /*
             * handleMessage() defines the operations to perform when
             * the Handler receives a new Message to process.
             */
            override fun handleMessage(inputMessage: Message) {
                // Gets the image task from the incoming Message object.
                val photoTask = inputMessage.obj as PhotoTask
                ...
            }
        }
        ...
    }
  • java

  •         /*
             * 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对象发送到其handleMessage()中。因为HandlerUI线程上运行,所以它可以将数据传送到UI对象。

后台线程的对象中存储数据

例如,这是一个在后台线程上运行的Runnable,它解码Bitmap并将其存储在其父对象PhotoTask中, Runnable还存储状态代码DECODE_STATE_COMPLETED

  • kotlin

  • const val DECODE_STATE_COMPLETED: Int = ...
    
    // A class that decodes photo files into Bitmaps
    class PhotoDecodeRunnable(
            private val photoTask: PhotoTask,
            // Gets the downloaded byte array
            private var imageBuffer: ByteArray = photoTask.getByteBuffer()
    ) : Runnable {
        ...
        // Runs the code for this task
        override fun run() {
            ...
            // Tries to decode the image buffer
            BitmapFactory.decodeByteArray(
                    imageBuffer,
                    0,
                    imageBuffer.size,
                    bitmapOptions
            )?.also { returnBitmap ->
                ...
                // Sets the ImageView Bitmap
                photoTask.image = returnBitmap
            }
            // Reports a status of "completed"
            photoTask.handleDecodeState(DECODE_STATE_COMPLETED)
            ...
        }
        ...
    }
  • java

  • // A class that decodes photo files into Bitmaps
    class PhotoDecodeRunnable implements Runnable {
        ...
        PhotoDecodeRunnable(PhotoTask downloadTask) {
            photoTask = downloadTask;
        }
        ...
        // Gets the downloaded byte array
        byte[] imageBuffer = photoTask.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
            photoTask.setImage(returnBitmap);
            // Reports a status of "completed"
            photoTask.handleDecodeState(DECODE_STATE_COMPLETED);
            ...
        }
        ...
    }
    ...

PhotoTask还包含一个显示BitmapImageView的句柄。但即使BitmapImageView的引用位于同一对象中,也无法将Bitmap分配给ImageView,因为您当前没有在UI线程上运行。

所以,下一步是将数据发送到PhotoTask对象。

将数据发送到对象层

PhotoTask是层次结构中的下一个更高级的对象。它维护对已解码数据的引用以及显示数据的View对象。它从PhotoDecodeRunnable接收状态代码并将其传递给维护线程池的对象并实例化Handler

  • kotlin

  • // Gets a handle to the object that creates the thread pools
    class PhotoTask() {
        ...
        private val photoManager: PhotoManager = PhotoManager.getInstance()
        ...
        fun handleDecodeState(state: Int) {
            // Converts the decode state to the overall state.
            val outState: Int = when(state) {
                PhotoDecodeRunnable.DECODE_STATE_COMPLETED -> PhotoManager.TASK_COMPLETE
                ...
            }
            ...
            // Calls the generalized state method
            handleState(outState)
        }
        ...
        // Passes the state to PhotoManager
        private fun handleState(state: Int) {
            /*
             * Passes a handle to this task and the
             * current state to the class that created
             * the thread pools
             */
            PhotoManager.handleState(this, state)
        }
        ...
    }
  • java

  • public class PhotoTask {
        ...
        // Gets a handle to the object that creates the thread pools
        photoManager = 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
             */
            photoManager.handleState(this, state);
        }
        ...
    }

将数据传送到UI

PhotoManager对象从PhotoTask对象接收状态代码和PhotoTask对象的句柄。由于状态为TASK_COMPLETE,因此创建一个包含状态码和要传送数据的Message,并将其发送给Handler

  • kotlin

  • object PhotoManager {
        ...
        // Handle status messages from tasks
        fun handleState(photoTask: PhotoTask, state: Int) {
            when(state) {
                ...
                TASK_COMPLETE -> { // The task finished downloading and decoding the image
                    /*
                     * Creates a message for the Handler
                     * with the state and the task object
                     */
                    handler.obtainMessage(state, photoTask)?.apply {
                        sendToTarget()
                    }
                }
                ...
            }
            ...
        }
  • java

    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 =
                            handler.obtainMessage(state, photoTask);
                    completeMessage.sendToTarget();
                    break;
                ...
            }
            ...
        }

    最后,Handler.handleMessage()检查每个传入消息的状态代码。如果状态代码为TASK_COMPLETE,则任务结束,Message中的PhotoTask对象包含BitmapImageView,因为Handler.handleMessage()UI线程上运行,所以它可以安全地将Bitmap移动到ImageView

  • kotlin

  •     object PhotoManager {
            ...
            private val handler: Handler = object : Handler(Looper.getMainLooper()) {
    
                override fun handleMessage(inputMessage: Message) {
                    // Gets the image task from the incoming Message object.
                    val photoTask = inputMessage.obj as PhotoTask
                    // Gets the ImageView for this task
                    val localView: PhotoView = photoTask.getPhotoView()
                    ...
                    when (inputMessage.what) {
                        ...
                        TASK_COMPLETE -> localView.setImageBitmap(photoTask.image)
                        ...
                        else -> super.handleMessage(inputMessage)
                    }
                    ...
                }
                ...
            }
            ...
        ...
        }
    ...
    }
  • java

  •     private PhotoManager() {
            ...
                handler = 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);
                        }
                        ...
                    }
                    ...
                }
                ...
        }
    ...
    }

更多信息

关于多线程的更多内容,请参阅进程和线程——概述