Android最佳實務之效能 - 多線程

來源:互聯網
上載者:User

標籤:因此   html   管理   status   article   break   ref   general   rom   

在單獨線程執行代碼

參考地址: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 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顯示,由於它們不在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是同意的。

Android最佳實務之效能 - 多線程

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.