2016/02/15

Android 的執行緒

Android 的執行緒是以 Linux pthread 及 Java Thread 實作,從APP的角度來看,有 UI、Binder、Background 執行緒。

分類

  1. UI Thread 在 APP 開始時被啟動,在 Linux process 生命週期內保持存活,UI Thread 是 APP 的 Main Thread,用來執行 Android Component 以及更新 UI
  2. Binder Thread 用在不同 process 的 thread 之間互相溝通,每一個 process 會維護一個 thread pool,處理從其他 process 進來的 requests,包含系統服務、Intent、Content Provider、Service
  3. Background Thread APP 建立的執行緒都是背景執行緒,他是 UI Thread 的 child

adb shell ps

用以下的指令可以查看 android device 上的 process 資訊。

# 顯示 thread 資訊
adb shell ps -t
# 顯示 fg, bg
adb shell ps -P

APP 裡面的執行緒跟 Linux 的 niceness value 相對應,可以用這兩個方式調整 Thread priority

java.lang.Thread
    setPriority(int priority)

android.os.Process
    Process.setThreadPriority(int priority)
    Process.setThreadPriority(int threadId, int priority)
Thread.setPriority(int) Linux niceness
1 (Thread.MIN_PRIORITY) 19
2 16
3 13
4 10
5 (Thread.NORM_PRIORITY) 0
6 -2
7 -4
8 -5
9 -6
10 -8

Android 控制群組

對 APP 來說最重要的兩個控制群組是 foreground 及 background,foreground 表示目前的 APP 是在前景,可視狀態,按下 Home 就會讓 APP 裡面所有的 Thread 都變成 background group。

如果用以下指令,可降低 Thread priority 並讓這個 thread 永遠都在 background group。

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)

Android 的 IPC

當 thread 之間沒有共用的記憶體區塊時,android 透過 binder framework 進行 IPC。

Linux 的 IPC 有: 信號、管道、訊息佇列、semaphore與共用記憶體,而 Android RPC 已經被 binder framework 取代。

RPC 的步驟為 1. marshalling: 封裝方法及資料 2. 將 marshal 的資料傳送到遠端 process 3. 在遠端 process 進行 unmarshalling,並執行 RPC 4. 將處理結果 return value 回傳給原始的 process

Binder

binder 讓 APP 在不同 process 的 thread 之間互相傳遞資料及進行 RPC,資料以 android.os.Parcel 物件組成,內容包含參數以及實作 android.os.Parcelable 介面的自訂物件,由 transact() 直到 onTransact() 到伺服器行程。

Transaction Thread Pool 可同時處理 16個 RPC。

IPC 是雙向的,也可以設定 IBinder.FLAG_ONEWAY 進行單向沒有回傳值的 RPC。

AIDL

AIDL: Android Interface Definition Language 當 process 想要開放功能給其他 process 使用時,可編輯 .aidl 檔案,然後產生 IPC 的 java codes。

AIDL 透過 Proxy 及 Stub 進行 RPC 的通訊過程如下

  1. 同步 RPC 在 .aidl 定義介面,然後用工具產生 Proxy 與 Stub。
interface ISynchronous {
    String getThreadName()
}

由於 Stub 可能會發生:(1) 執行時間很久 (2) Blocking (3) 呼叫共用資料的狀況,最好在客戶端要用 worker thread 進行 RPC 呼叫,而不要用 UI thread。

  1. 非同步 RPC

可定義整個介面都是非同步的 oneway

oneway interface IAsynchronousInterface {
    void method1();
    void method2();
}

也可以只定義某個方法是 oneway

interface IAsynchronousInterface {
    oneway void method1();
    void method2();
}

使用 binder 進行訊息傳遞

在不同 process 的 thread 可透過 binder 傳遞訊息

sample code

WorkerThreadService 是在伺服器行程中執行,與客戶端的 Activity 溝通,Service 會實作 Messenger,將它傳遞給 Activity,而 Activity 會回傳 Message 物件給 Service。

public class WorkerThreadService extends Service {

    private static final String TAG = "WorkerThreadService";
    WorkerThread mWorkerThread;
    Messenger mWorkerMessenger;

    @Override
    public void onCreate() {
        super.onCreate();
        // 產生 Service 時,就建立 worker thread,bind 過來的客戶端也是使用 worker thread
        mWorkerThread = new WorkerThread();
        mWorkerThread.start();
    }

    // Worker thread has prepared a looper and handler.
    private void onWorkerPrepared() {
        Log.d(TAG, "onWorkerPrepared");
        mWorkerMessenger = new Messenger(mWorkerThread.mWorkerHandler);
        synchronized(this) {
            notifyAll();
        }
    }

    // 繫結過來的客戶端會收到 messenger 的 IBinder 物件,客戶端才能跟 server 溝通
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        synchronized (this) {
            while (mWorkerMessenger == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    // Empty
                }
            }
        }
        return mWorkerMessenger.getBinder();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mWorkerThread.quit();
    }

    private class WorkerThread extends Thread {

        Handler mWorkerHandler;

        @Override
        public void run() {
            Looper.prepare();
            mWorkerHandler = new Handler() {
                // worker thread 的 message handler
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case 1:
                            try {
                                // 雙向訊息溝通的機制,將新的 Message 傳回 Activity
                                msg.replyTo.send(Message.obtain(null, msg.what, 0, 0));
                            } catch (RemoteException e) {
                                Log.e(TAG, e.getMessage());
                            }
                            break;
                        case 2:
                            Log.d(TAG, "Message received");
                            break;
                    }

                }
            };
            onWorkerPrepared();
            Looper.loop();
        }

        public void quit() {
            mWorkerHandler.getLooper().quit();
        }
    }
}
  1. 單向訊息

在客戶端,Activity繫結到伺服器行程中的 service 並傳遞訊息

public class MessengerOnewayActivity extends Activity {

    private boolean mBound = false;
    private Messenger mRemoteService = null;

    private ServiceConnection mRemoteConnection = new ServiceConnection() {

        public void onServiceConnected(ComponentName className, IBinder service) {
                // 連上 service,由伺服器端回傳的 binder 建立 Messenger
            mRemoteService = new Messenger(service);
            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            mRemoteService = null;
            mBound = false;
        }
    };

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger_client);
    }

    public void onBindClick(View v) {
          // 繫結到遠端服務
        Intent intent = new Intent("com.eat.chapter5.ACTION_BIND");
        bindService(intent, mRemoteConnection, Context.BIND_AUTO_CREATE);

    }

    public void onUnbindClick(View v) {
        if (mBound) {
            unbindService(mRemoteConnection);
            mBound = false;
        }

    }

    public void onSendClick(View v) {
        if (mBound) {
            try {
                // 點擊按鈕時傳遞訊息
                mRemoteService.send(Message.obtain(null, 2, 0, 0));
            } catch (RemoteException e) {
                // Empty
            }
        }
    }
}
  1. 雙向訊息

傳遞的 messenge 在 Message.replyTo 裡面存放指向 Messenger 的 reference,可透過它建立雙向溝通機制。

public class MessengerTwowayActivity extends Activity {
    private static final String TAG = "MessengerTwowayActivity";
    private boolean mBound = false;
    private Messenger mRemoteService = null;

    private ServiceConnection mRemoteConnection = new ServiceConnection() {

        public void onServiceConnected(ComponentName className, IBinder service) {
            mRemoteService = new Messenger(service);
            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            mRemoteService = null;
            mBound = false;
        }
    };

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger_client);
    }

    public void onBindClick(View v) {
        Intent intent = new Intent("com.eat.chapter5.ACTION_BIND");
        bindService(intent, mRemoteConnection, Context.BIND_AUTO_CREATE);
    }

    public void onUnbindClick(View v) {
        if (mBound) {
            unbindService(mRemoteConnection);
            mBound = false;
        }

    }

    public void onSendClick(View v) {
        if (mBound) {
            try {
                Message msg = Message.obtain(null, 1, 0, 0);
                // 建立被傳送到遠端的 Messenger,此 Messenger 存放目前執行緒的 Handler reference,它會執行來自其他 process 的訊息
                msg.replyTo = new Messenger(new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        Log.d(TAG, "Message sent back - msg.what = " + msg.what);
                    }
                });
                mRemoteService.send(msg);
            } catch (RemoteException e) {
                Log.e(TAG, e.getMessage());
            }
        }
    }
}

Reference

Android 高效能多執行緒

沒有留言:

張貼留言