Android线程池操作

来源:互联网 发布:上海美工培训班 编辑:程序博客网 时间:2024/06/14 21:49

Android多线程操作

1. 声明代码运行在线程

new Thread(new Runnable() {        @Override    public void run() {    // 输入你想运行在线程的代码    }}).start();

实现Runnable接口,实现run()方法,把Runnable任务关联到线程对象,启动线程,会自动执行Runnable任务中run()方法!

2.创造一个多线程的管理器

  • 如果想要仅仅执行一次多线程任务,可能上面的代码就是你的最好选择,他足够简洁且耦合度低。
  • 如果一个任务在不同的数据集合重复的执行,而你却只需要执行一次,那么IntentService会满足你的需要!
  • 当资源的可得到时自动执行,或者允许多任务同时运行,你需要提供一个线程的管理容器————线程池

你需要做的是,实现 ThreadPoolExecutor实例,当线程池的线程处于free状态时,从队列中取出一个任务执行。而运行任务,只需要把任务添加到线程队列中!

A thread pool can run multiple parallel instances of a task, so you should ensure that your code is thread-safe. Enclose variables that can be accessed by more than one thread in a synchronized block. This approach will prevent one thread from reading the variable while another is writing to it. Typically, this situation arises with static variables, but it also occurs in any object that is only instantiated once. To learn more about this, read the Processes and Threads API guide.

线程池可以运行多个并行的任务实例,所以你应该确保你的代码是线程安全的把代码放在同步代码块中可以保证 变量 不被一个以上的线程得到
这种方法保证变量在被一个线程读取时不会被另一个线程得到!
通常情况下,常用静态变量作为同步锁,但是任何只被初始化过一次的对象都可以被当作同步锁!

定义线程池类

初始化 ThreadPoolExecutor 类,运用单例模式

  1. 运用静态变量表示线程池
    通常情况下,你的APP只需要一个线程池实例,为了有单个的控制对于移动平台上捉襟见肘的CPU和网络资源!
   static {        // 建立一个静态单例实例        sInstance = new PhotoManager();    }    public static PhotoManager getInstance() {        return sInstance;    }
  1. 构造函数权限设置为private
    构造函数私有化保证线程池是一个单例对象,那意味着
    which means that you don’t have to enclose accesses to the class in a synchronized block
private PhotoManager(){   ....}
  1. 调用线程池实例的方法开启任务
    把任务Runnable方法线程的执行队列中。
// Called by the PhotoView to get a photo    static public PhotoTask startDownload(ImageView imageView, boolean cacheFlag) {// 添加图片下载任务到线程池     sInstance.mDownloadThreadPool.execute(downloadTask                .getHTTPDownloadRunnable());    }
  1. 在构造函数中初始化一个Handler对象,连接到UI主线程
    Handler允许你的程序安全的调用UI视图(View 对象)的方法.大多数的UI对象只有在UI主线程才能安全更新。
private PhotoManager(){        //在构造函数中初始化一个Hanlder,连接到UI主线程         mHandler = new Handler(Looper.getMainLooper()) {                /*                 * handleMessage() defines the operations to perform when                 * the Handler receives a new Message to process.                 */                @Override                public void handleMessage(Message inputMessage) {              ....                             };

定义线程池类的参数

  1. 初始化线程池的size和最大的size
    The initial number of threads to allocate to the pool, and the maximum allowable number. The number of threads you can have in a thread pool depends primarily on the number of cores available for your device.
     /**     * 得到可得到的核心数,通常情况下不是设备的物理核心数     */    private static int NUMBER_OF_CORES = Runtime.getRuntime()            .availableProcessors();
  1. 线程的存活时间和时间单位
    当线程处于闲置状态下,多久会被关闭。同时还有对于时间单位的定义,是 TimeUnit的一个常量。
private PhotoManager(){            ...                    // 设置空闲线程被终止前的等待时间            final int KEEP_ALIVE_TIME = 1;            // 设置时间代为,这里设置为秒            final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;            ...}
  1. 任务队列
    ThreadPoolExecutor的传入队列存放Runnable对象。为了启动一个线程运行任务代码,线程池从先进先出队列中得到一个Runnable对象,并把它关联到线程池的一个空闲线程中!
    当你创造线程池对象时间,你需要提供任何实现了BlockingQueue interface接口的队列作为参数。Java提供了几种常用接口实现
private PhotoManager(){            ...            // 一个Runnables执行任务队列            final BlockingQueue<Runnable> mDecodeWorkQueue;            // 初始化任务队列作为 LinkedBlockingQueue            mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();            ... }

创造线程池

为了创建一池的线程,调用ThreadPoolExecutor()初始化一个线程池。线程池创建和管理一系列线程组。当ThreadPoolExecutor初始化时,他会初始化线程池size的所有线程,并保持(和线程对象)连接,这也免去了频繁创建线程 销毁线程锁所带来的资源损耗

private PhotoManager(){           // 创建一个线程池            mDecodeThreadPool = new ThreadPoolExecutor(                    NUMBER_OF_CORES,       // Initial pool size                    NUMBER_OF_CORES,       // Max pool size                    KEEP_ALIVE_TIME,                    KEEP_ALIVE_TIME_UNIT,                    mDecodeWorkQueue);            ... }

3.在线程池运行代码

在线程池运行任务,你可以添加Runnable任务到线程池的任务队列!当线程可以得到,线程池从队列中得到任务Runnable,并在线程中运行!

这里同时会展示如何停止一个正在运行的任务。当任务启动时,你可能想停止它,但是发现这并不是必须的。相对于浪费进程时间,你可以取消正在运行任务的进程。例如,如果你正在从网络上下载图片并用缓存,如果发现一个图片已经在缓存上,你可能想要停止任务。这取决与你怎么写你的App,你不能在你启动下载前发现它!

在线程池的线程上运行任务

为了在线程池的线程上运行任务,传递任务Runnable对象给ThreadPoolExecutor的execute()方法。
这个方法调用,把Runnable任务放到任务队列,当闲置线程可得到,线程池管理者在线程上执行等待时间最久的任务

public void handleState(PhotoTask photoTask, int state) {        switch (state) {            // 下载图片任务结束            case DOWNLOAD_COMPLETE:            // 编码图片                mDecodeThreadPool.execute(                        photoTask.getPhotoDecodeRunnable());        }        switch (state) {        // 下载和编码图片的任务执行完成        case TASK_COMPLETE:            /*             * 创造一个包含状态码和任务对象的Message,传递Handler             */            Message completeMessage =                    mHandler.obtainMessage(state, photoTask);            completeMessage.sendToTarget();            break;    }    }

当ThreadPoolExecutor线程池在线程上执行一个Runnable任务对象,它自动调用Runnable的run()方法!

中断运行的代码

为了停止一个任务,你需要运行任务的线程。To prepare to do this, you need to store a handle to the task’s thread when you create the task. 为此,当你创建一个任务时,你需要储存任务运行任务线程的控制器(手柄,控制)!

class PhotoDecodeRunnable implements Runnable {        private PhotoTask mPhotoTask;        private byte[] imageBuffer;        public static final int DECODE_STATE_COMPLETED = 1;        PhotoDecodeRunnable(PhotoTask downloadTask) {            mPhotoTask = downloadTask;            // 得到下载的字节数组            imageBuffer = mPhotoTask.getByteBuffer();        }        // 定义运行在这个任务中的代码        public void run() {            /*             * 在包含 PhotoDecodeRunnable的对象中储存当前线程             */            mPhotoTask.setImageDecodeThread(Thread.currentThread());            ...        }

为了中断一个线程,你可以调用Thread.interrupt()。注意,线程对象为系统控制,系统可以修改它们到你App的进程外! For this reason, you need to lock access on a thread before you interrupt it, by placing the access in a synchronized block.因此,在你中断线程之前,你需要通过用同步代码块,锁住对于得到线程的获得。

Notice that Thread objects are controlled by the system, which can modify them outside of your app’s process. For this reason, you need to lock access on a thread before you interrupt it, by placing the access in a synchronized block.

/**     * 中断运行线程     */    public static void cancelAll() {        /*         * 创造一个和线程池队列相同大小的Runnable数组         */        Runnable[] runnableArray = new Runnable[mDecodeWorkQueue.size()];        // 用队列中Runnable对象填充数组        mDecodeWorkQueue.toArray(runnableArray);        // 得到数组的大小,以便遍历        int len = runnableArray.length;        /*         * 遍历数组中的Runnable对象,中断它们的线程         */        synchronized (sInstance) {            // 遍历Runnable任务数组            for (int runnableIndex = 0; runnableIndex < len; runnableIndex++) {                // 得到当前线程                Thread thread = runnableArray[runnableIndex].mThread;                // 如果线程存在,中断它                if (null != thread) {                    thread.interrupt();                }            }        }    }

在大多数情况下, Thread.interrupt()立即停止线程。然而,它只会停止处于等待状态下的线程,不会打断CPU或者网络密集任务。为了避免减慢或者阻塞系统,在尝试任何操作之前,你应该为任何未决定的打断请求进行测试!

To avoid slowing down or locking up the system, you should test for any pending interrupt requests before attempting an operation

/* * Before continuing, checks to see that the Thread hasn't * been interrupted */if (Thread.interrupted()) {    return;}...// Decodes a byte array into a Bitmap (CPU-intensive)BitmapFactory.decodeByteArray(        imageBuffer, 0, imageBuffer.length, bitmapOptions)

4.和UI主线程通信

这里将展示从后台线程任务传递数据给运行在UI主线程的对象(View UI对象)。这个功能允许你的任务执行后台操作,传递结果给UI对象,例如Bitmap!

每一个APP都有一个运行UI对象(例如View对象)的特殊进程,我们叫它UI主线程!只有运行在UI主线程的对象,可以得到运行在线程的其他对象!因为你的任务运行在线程池里的线程,没有运行在UI主线程,所以它不能得到UI对象!为了从后台线程移动数据到UI主线程,用运行在UI主线程的Handler实现!

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.

定义一个运行在UI主线程的Handler

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是Android系统框架管理线程的一部分,一个Handler接受Message,运行代码处理Message!通常情况下,你为一个新的线程创建一个Handler,但是你也可以创建一个和已经存在的线程相连接的Handler!当你连接Handler到UI主线程,Handler处理Message的代码运行在UI主线程!

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.

线程池的构造方法中初始化Handler,然后存储这个对象作为一个全局变量!通过初始化Handler(Looper looper)构造方法,连接Handler**连接到UI 主线程!构造函数用一个Looper对象,Looper是Android线程管理框架的另外一个部分。当你用一个特殊的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.
在Handler中,覆盖handleMassge()方法。当Handler接受线程管理的Message时,Android系统执行handleMessage()方法,一个特殊线程的所有Handler对象接受相同的Message!

        /*         * 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;            ...        }    ...    }}

从后台任务移动数据到UI主线程

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.

为了从运行在后台的程序的数据传递给UI主线程的对象(主要是View UI对象)。首先存储数据的引用和UI对象;然后传递Message.obj对象和Message.what状态码给实例化Handler的对象。在这个对象中,传递包含状态码和对象的Message给Handler。因为Handler运行在UI主线程,所有它可以更新数据到UI对象!

  • 任务对象中存储对象

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.
Instead, the next step is to send this status to the PhotoTask object.

```/**     * 图片后台编码任务     *      * @author QT     *      */    class PhotoDecodeRunnable implements Runnable {        private PhotoTask mPhotoTask;        private byte[] imageBuffer;        public static final int DECODE_STATE_COMPLETED = 1;        PhotoDecodeRunnable(PhotoTask downloadTask) {            mPhotoTask = downloadTask;            // 得到下载的字节数组            imageBuffer = mPhotoTask.getByteBuffer();        }        // 定义运行在这个任务中的代码        public void run() {            /*             * 在包含 PhotoDecodeRunnable的对象中储存当前线程             */            mPhotoTask.setImageDecodeThread(Thread.currentThread());            /*             * 在继续执行之前,先确定线程是否被中断             */            if (Thread.interrupted()) {                return;            }            BitmapFactory.Options bitmapOptions=new BitmapFactory.Options();            // 编码字节数组到bitmap对象,cpu密集型操作            // Tries to decode the image buffer            Bitmap returnBitmap = BitmapFactory.decodeByteArray(imageBuffer, 0,                    imageBuffer.length, bitmapOptions);            // Sets the ImageView Bitmap            mPhotoTask.setImage(returnBitmap);            // 报告 完成 状态            mPhotoTask.handleDecodeState(DECODE_STATE_COMPLETED);        }    }
  • 发送对象层次结构的状态

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是层次结构的另一个高级对象。它包括了已编码数据的引用将要展示数据的视图对象。它接受从PhotoDecodeRunnablec传来的状态码,然后传递它给包含线程池和实例化的Handler的对象 PhotoManager。

package com.bestPractice.global;import android.graphics.Bitmap;import android.widget.ImageView;import com.bestPractice.global.PhotoManager.PhotoDecodeRunnable;public class PhotoTask {    // 得到创建线程池的对象    private static PhotoManager sPhotoManager = PhotoManager.getInstance();    public static Thread mThread;    private Bitmap image;    private ImageView imageView;    private byte[] byteBuffer;    public void handleDecodeState(int state) {        int outState = 0;        // 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);    }    // 传递状态给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);    }    public ImageView getImageView() {        // TODO Auto-generated method stub        return this.imageView;    }    public Bitmap getImage() {        // TODO Auto-generated method stub        return this.image;    }    public byte[] getByteBuffer() {        return byteBuffer;    }    public void setByteBuffer(byte[] byteBuffer) {        this.byteBuffer = byteBuffer;    }    public void setImageDecodeThread(Thread currentThread) {        mThread = currentThread;    }    public void setImage(Bitmap returnBitmap) {        this.image = returnBitmap;    }    public Runnable getPhotoDecodeRunnable() {        return null;    }}
  • 移动数据到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

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;            ...        }        ...    }
  • 更新UI

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 i 1045 n 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.handlerMessage()检查每一个送入消息的状态码。如果状态码是TASK_COMPLETE,那么任务已经执行完成。PhotoTask对象包含Bitmap和ImageView。因为Handler在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);                    }                    ...                }                ...            }            ...    }...}
0 0
原创粉丝点击