標籤:因此 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, AsyncTask和 IntentService的基礎,也是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資源,也能夠中斷正在執行的線程。
在池裡一個線程中執行代碼
傳一個Runnable到ThreadPoolExecutor.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最佳實務之效能 - 多線程