• Android最佳实践之性能


    在单独线程执行代码

    參考地址:http://developer.android.com/training/multiple-threads/define-runnable.html
    Runnable对象,是一个接口,里面仅仅有一个run方法。它仅仅是表示一段能够执行的代码。

    说这句话,是说明它并不一定要执行在子线程中。它也能够执行在UI线程

    假设它用来执行一段代码,通常被称为一个任务(Task)。
    Thread类和 Runnable类。是非常强大的基础类,它们是强大的Android基础类HandlerThread, AsyncTaskIntentService的基础,也是ThreadPoolExecutor的基础。

    这个类自己主动管理线程和任务队列。甚至能够并行多个异步任务。

    定义一个Runnable

    public class PhotoDecodeRunnable implements Runnable {
        ...
        @Override
        public void run() {
            /*
             * Code you want to run on the thread goes here
             */
            ...
        }
        ...
    }

    实现run()方法

    在设计上,Runnable对象一般设计在子线程中执行,比方new Thread(new Runnable{})中。
    以下的演示样例中。一開始调用Process.setThreadPriority()方法,传入THREAD_PRIORITY_BACKGROUND。这样能够降低Runnable对象所在线程和UI线程的资源竞争。


    你也应该调用Thread.currentThread()。保存一个引用,指向Runnable所在的线程。

    class PhotoDecodeRunnable implements Runnable {
    ...
        /*
         * Defines the code to run for this task.
         */
        @Override
        public void run() {
            // Moves the current Thread into the background
            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
            ...
            /*
             * Stores the current Thread in the PhotoTask instance,
             * so that the instance
             * can interrupt the Thread.
             */
            mPhotoTask.setImageDecodeThread(Thread.currentThread());
            ...
        }
    ...
    }

    管理多线程

    參考地址:http://developer.android.com/training/multiple-threads/create-threadpool.html
    假设你仅仅执行task(Runnable)一次,那么上一篇的内容足以;假设你要在不同的数据集中反复执行一个task,但一次也仅仅能执行一个task,IntentService满足你的需求。为了自己主动将资源运用最大化、或同一时候执行多个task,你须要一个多线程管理对象。使用ThreadPoolExecutor,它使用闲置的线程执行队列中的task,你须要做的事就是向队列中加入任务。
    一个线程池能够并行执行多个task,所以你要确保你的代码是线程安全的

    定义一个线程池(Thread Pool)对象

    对线程池使用静态变量

    在app中,线程池可能须要单例的:

    public class PhotoManager {
        ...
        static  {
            ...
            // Creates a single static instance of PhotoManager
            sInstance = new PhotoManager();
        }
        ...

    使用private的构造方法

    将构造方法私有化,则不用synchronized块来闭包构造方法:

    public class PhotoManager {
        ...
        /**
         * Constructs the work queues and thread pools used to download
         * and decode images. Because the constructor is marked private,
         * it's unavailable to other classes, even in the same package.
         */
        private PhotoManager() {
        ...
        }

    使用线程池类中的方法来执行task

    在线程池类中加入一个task给任务队列:

    public class PhotoManager {
        ...
        // Called by the PhotoView to get a photo
        static public PhotoTask startDownload(
            PhotoView imageView,
            boolean cacheFlag) {
            ...
            // Adds a download task to the thread pool for execution
            sInstance.
                    mDownloadThreadPool.
                    execute(downloadTask.getHTTPDownloadRunnable());
            ...
        }

    在UI线程初始化一个Handler

     private PhotoManager() {
        ...
            // Defines a Handler object that's attached to the UI thread
            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) {
                    ...
                }
            ...
            }
        }

    确定线程池參数

    初始化一个ThreadPoolExecutor对象,须要以下这些參数:
    1、池的初始化size和最大的池size
    在线程池中能够使用的线程的数量主要取决于你的设备可用CPU内核的数量:

    public class PhotoManager {
    ...
        /*
         * Gets the number of available cores
         * (not always the same as the maximum number of cores)
         */
        private static int NUMBER_OF_CORES =
                Runtime.getRuntime().availableProcessors();
    }

    这个数字可能不反映设备物理CPU内核的数量。

    一些设备依据系统负载已经关闭一个或多个内核的cpu,对于这些设备,availableProcessors()返回的是可用的内核数,这个数字一般小于内核总数。

    2、活跃时间和时间单位
    活跃时间指一个线程在关闭之前保持空暇的时间。这个时间的单位由TimeUnit中的常量决定。

    3、任务队列
    ThreadPoolExecutor持有的任务队列里面是Runnable对象。初始化ThreadPoolExecutor时要传入一个实现了BlockingQueue接口的队列。为满足app需求。你能够选择已有的实现了这个接口的类,以下是LinkedBlockingQueue的样例:

    public class PhotoManager {
        ...
        private PhotoManager() {
            ...
            // A queue of Runnables
            private final BlockingQueue<Runnable> mDecodeWorkQueue;
            ...
            // Instantiates the queue of Runnables as a LinkedBlockingQueue
            mDecodeWorkQueue = new LinkedBlockingQueue<Runnable>();
            ...
        }
        ...
    }

    创建一个线程池

    调用ThreadPoolExecutor的构造方法ThreadPoolExecutor()来创建一个线程池:

      private PhotoManager() {
            ...
            // Sets the amount of time an idle thread waits before terminating
            private static final int KEEP_ALIVE_TIME = 1;
            // Sets the Time Unit to seconds
            private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
            // Creates a thread pool manager
            mDecodeThreadPool = new ThreadPoolExecutor(
                    NUMBER_OF_CORES,       // Initial pool size
                    NUMBER_OF_CORES,       // Max pool size
                    KEEP_ALIVE_TIME,
                    KEEP_ALIVE_TIME_UNIT,
                    mDecodeWorkQueue);
        }

    完整代码下载ThreadSample.zip

    在线程池的一个线程上执行代码

    參考地址:http://developer.android.com/training/multiple-threads/run-code.html#StopThread
    你加入了一个task到任务队列中,当线程池中有线程空暇时,则会执行队列中的task。为节省CPU资源,也能够中断正在执行的线程。

    在池里一个线程中执行代码

    传一个RunnableThreadPoolExecutor.execute()方法中,可開始执行一个任务。这种方法将task加入到这个线程池的工作队列中,当有空暇的线程时,就会取出队列中的任务进行执行:

    public class PhotoManager {
        public void handleState(PhotoTask photoTask, int state) {
            switch (state) {
                // The task finished downloading the image
                case DOWNLOAD_COMPLETE:
                // Decodes the image
                    mDecodeThreadPool.execute(
                            photoTask.getPhotoDecodeRunnable());
                ...
            }
            ...
        }
        ...
    }

    中断(Interrupt)执行代码

    要结束一个task。你须要中断这个task所在的线程。为了能这样做。在创建task的时候你要保存task所在线程的句柄:

    class PhotoDecodeRunnable implements Runnable {
        // Defines the code to run for this task
        public void run() {
            /*
             * Stores the current Thread in the
             * object that contains PhotoDecodeRunnable
             */
            mPhotoTask.setImageDecodeThread(Thread.currentThread());
            ...
        }
        ...
    }

    调用Thread.interrupt()来中断一个线程。注意,Thread对象是由系统控制的。能够在app进程之外来改动它们。因此,你须要在中断它之前锁住线程中的訪问,在訪问的地方加synchronized代码块。比如:

    public class PhotoManager {
        public static void cancelAll() {
            /*
             * Creates an array of Runnables that's the same size as the
             * thread pool work queue
             */
            Runnable[] runnableArray = new Runnable[mDecodeWorkQueue.size()];
            // Populates the array with the Runnables in the queue
            mDecodeWorkQueue.toArray(runnableArray);
            // Stores the array length in order to iterate over the array
            int len = runnableArray.length;
            /*
             * Iterates over the array of Runnables and interrupts each one's Thread.
             */
            synchronized (sInstance) {
                // Iterates over the array of tasks
                for (int runnableIndex = 0; runnableIndex < len; runnableIndex++) {
                    // Gets the current thread
                    Thread thread = runnableArray[taskArrayIndex].mThread;
                    // if the Thread exists, post an interrupt to it
                    if (null != thread) {
                        thread.interrupt();
                    }
                }
            }
        }
        ...
    }

    大多数情况下,Thread.interrupt()会直接停止线程。然而,它仅仅会停止waiting的线程。而不会中断正使用CPU和网络任务的线程。为了防止拖慢或锁定系统,你应该在执行某个操作前推断是否中断了:

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

    完整代码下载ThreadSample.zip

    UI线程的交互

    參考地址:http://developer.android.com/training/multiple-threads/communicate-ui.html
    在Android中一般使用Handler,在子线程中将结果发送到UI线程,然后在UI线程操作UI。

    在UI线程上定义一个Handler

    Handler是Android Framework中管理线程的一部分。一个Handler对象接收消息然后执行一些代码处理这些消息。

    一般,在一个新线程中创建一个Handler,你也能够在一个已有的线程中创建Handler。当在UI线程创建Handler。那么 它处理的代码也执行在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()) {
        ...

    覆写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.

    将数据从Task移动到UI线程

    为了将数据从执行在后台线程的task中移动到UI线程,一開始我们要在task类中存数据和UI对象的引用。然后。将task对象和状态码传给被Handler实例化的对象中。然后发送一个包括task和状态码的Message给handler。

    由于Handler执行在UI线程。所以它能够将数据送到UI对象中。

    // 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显示,由于它们不在UI线程。

    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);
        }
        ...
    }

    PhotoManager接收一个状态码和一个PhotoTask对象的句柄,由于状态是TASK_COMPLETE,创建一个包括状态和Task对象的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,则表示task已完毕。PhotoTask中的ImageView须要显示当中的Bitmap对象。由于Handler.handleMessage()执行在UI线程,如今ImageView显示bitmap是同意的。

  • 相关阅读:
    算法:javascript截取字符串
    【转】解决memcached启动失败
    Ubuntu 16.04 安装 Apache, MySQL, PHP7
    phpstorm2016.3+xdebug调试
    (转)微信开发连接SAE数据库
    php图片上传服务器
    大数据整体市场规模达1000亿,金融、政务等行业应用占据七成份额
    “AI智客计划”
    人工智能 :眼纹识别技术大显神通,一眼认出你
    AI 芯片,是金山还是泡沫?
  • 原文地址:https://www.cnblogs.com/llguanli/p/8452936.html
Copyright © 2020-2023  润新知