Androidの最適な実践の性能-マルチスレッド


コードを単独スレッドで実行
参照先:http://developer.android.com/training/multiple-threads/define-runnable.html Runnableオブジェクトは、インターフェースであり、中にはrunメソッドしかありません.この言葉は、必ずしもサブスレッド内で実行されなくても、UIスレッドで実行されても良いという意味です.コードの一部を実行するために使用される場合、通常はタスク(Task)と呼ばれる.Thread類とRunnable類は非常に強力な基礎類であり、それらは強力なAndroidベースのHandlerThread、AyncTaskとIntentServiceの基礎であり、ThreadPool Exectorの基礎でもあります.このクラスの自動管理スレッドとタスクキューは、複数の非同期タスクを並列に行うこともできます.
Runnableを定義します.
public class PhotoDecodeRunnable implements Runnable {
    ...
    @Override
    public void run() {
        /*
         * Code you want to run on the thread goes here
         */
        ...
    }
    ...
}
ルン()方法を実現する
設計上、Runnableオブジェクトは、一般的に、new Thread(new Runnable{}のようなサブスレッド内で動作するように設計されている.以下の例では、Process.set ThreadPriorityを起動すると、THREAD_に入る.PRIORITY_BACKGROUNDは、Runnable対象のスレッドとUIスレッドのリソース競争を減らすことができる.あなたもThread.current Threadを呼び出すべきで、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を実行するなら、前のページの内容は十分です.もしあなたが別のデータセットでtaskを繰り返し実行するなら、一度だけtaskを実行して、IntentServiceはあなたの要求を満たすことができます.リソースを自動的に最大化したり、複数のタブを同時に実行したりするためには、複数のスレッド管理オブジェクトが必要です.ThreadPool Exectorを使って、アイドルスレッドを使ってキューの中のtaskを実行します.あなたに必要なことはキューにタスクを追加します.スレッドプールは複数のタスキを並行して実行することができますので、あなたのコードはスレッドの安全を確保します.
スレッドプール(Thread Pool)オブジェクトを定義します.
スレッドプールに静的変数を使う
アプリでは、スレッドプールは、単一の例を必要とする場合がある.
public class PhotoManager {
    ...
    static  {
        ...
        // Creates a single static instance of PhotoManager
        sInstance = new PhotoManager();
    }
    ...
prvateを使用した構造方法
構造方法を私有化すると、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を実行します.
オンラインプログラムクラスにタスキを追加してタスクキューに与えます.
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) {
                ...
            }
        ...
        }
    }
スレッドのパラメータを決定します.
ThreadPool Exectorオブジェクトを初期化するには、以下のようなパラメータが必要です.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カーネルの数を反映していないかもしれない.一部の装置は、システム負荷に応じて既に1つ以上のカーネルのcpuをオフにしており、これらの装置に対して、available Processors()は利用可能なカーネルの数を返しており、この数字は一般にカーネルの総数より小さい.
2、アクティブ時間と時間単位の活動時間とは、スレッドが閉じる前にアイドル状態を維持する時間のことです.この時間の単位はTimeUnitの定数によって決まります.
3、ジョブキューThreadPoolExectorが持つジョブキューの中にはRunnableオブジェクトがあります.ThreadPool Exectorを初期化する時に、BlockingQueインターフェースを実現した行列が入ってきます.アプリの需要を満たすために、既存のインターフェースを実現したクラスを選択してもいいです.以下はLinked BlockingQueの例です.
public class PhotoManager {
    ...
    private PhotoManager() {
        ...
        // A queue of Runnables
        private final BlockingQueue mDecodeWorkQueue;
        ...
        // Instantiates the queue of Runnables as a LinkedBlockingQueue
        mDecodeWorkQueue = new LinkedBlockingQueue();
        ...
    }
    ...
}
スレッドを作成
ThreadPool Exectorを呼び出した構造方法ThreadPool Exector()はスレッド池を作成します.
  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からThreadPool Exector.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スレッド上でHandlerを定義する.
HandlerはAndroid Fraamewarkで管理スレッドの一部です.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のハンドルもあります.これらは同一のオブジェクトであるにもかかわらず、UIスレッドにいないので、BitmapをImageViewに表示することはできない.
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.handlee Message()でMessageの状態を検出し、状態がTASK_であればCOMPLETEは、taskが完了したことを表し、PhotoTaskのImageViewにはBitmapオブジェクトが表示されます.Handler.handlee Message()はUIスレッドで動作しているので、今はImageViewでbitmapを表示することが許可されています.