Android 進程間通信

要講 Android 進程通信的話,就不得不先講講 Service. Service 是 Android 的四大組件之一,它主要的作用是后臺執行操作,Activity 屬于帶有 UI 界面跟用戶進行交互,而 Service 則沒有 UI 界面,所有的操作都是基于后臺運行完成。并且 Service 跟 Activity 一樣也是可以由其它的應用程序調用啟動的,而且就算用戶切換了應用程序,Service 依舊保持運行。一個組件如果與 Service 進行了綁定( bind ), 就可以跟 Service 進行數據的交互,并且也可以跟不同的進程之間進行交互 (IPC)。通常會使用到 Service 的情況有進行網絡請求,音樂的操控,文件的 I/O 操作等。

Service

聲明

在 Manifest 里聲明 Service, 類似于 Activity,所有的 Service 都要在 Manifest 里面進行聲明,如下:

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

啟動

Service 通常是通過以下兩種方式進行啟動

  • startService
  • bindService

Start Service

當組件(例如 activity)通過調用 startService() 來啟動 Service 的時候。一旦啟動后,Service 就會獨立的在后臺運行,即使調用的組件已經銷毀了,Service 還是可以繼續在后臺運行。一般情況下,只需要進行一次單獨的操作,不需要將操作后的結果返回給調用者的時候,會使用該方式啟動 Service。例如, 進行上傳或者下載操作 的時候,當操作完成后,Service 應該自行調用 stopService() 或 stopSelf() 來結束運行。

Bind Service

當組件(例如 activity)通過調用 bindService() 來啟動 Service 的時候。這種方式提供了 client - service 的接口,可以讓調用組件跟 Service 進行發送請求及返回結果的操作,設置可以進行進程間的通信 (IPC)。只要有一個組件對該 Service 進行了綁定,那該 Service 就不會銷毀。并且多個組件可以同時對一個 Service 進行綁定, 只有在所有進行了綁定的組件都解綁的時候,Service 才會銷毀 。

盡管兩種方式是分開討論的,但是并不是互斥的關系,使用 startService 啟動了 Service 后,也是可以通過 bindService 綁定的。

注意: 雖然 Service 是在后臺運行,但是其實還是在 主線程 里進行所有的操作的。Service 在啟動時除非單獨進行了定義否則并沒有在單獨的線程或者進程了而都是在 主線程 里。所以這表示任何能堵塞主線程的操作(例如音樂的播放或者網絡請求)都應該單獨開辟新的線程來進行操作,否則很容易出現 ANR 。

如果某個組件是通過調用 startService() 的方式來啟動了 Service, 那這個 Service 就會一直在后臺運行直到 Service 內部調用 stopSelf() 或某個組件調用 stopService() 來結束該 Service 。

如果某個組件是通過調用 bindService() 的方式來啟動了 Service, 那這個 Service 就會一直在后臺運行直到該組件與其解綁 。Service 在沒有任何組件綁定的時候,系統會將其銷毀

關于 Service 更多詳細的介紹可以查看這里

Service 生命周期

service 生命周期圖

AIDL (Android Interface Definition Language )

Android IPC 是通過 Binder 實現的,但是 Binder 相關的概念非常復雜,為了方便開發者 Google 就推出了 AIDL (安卓接口定義語言)。通過編寫 AIDL 文件,Android Studio 就可以幫我們生成 Binder 通信的相關代碼。開發者即使不了解 Binder 機制也可以實現 IPC 了。

關鍵字

oneway

正常情況下 Client 調用 AIDL 接口方法時會阻塞,直到 Server 進程中該方法被執行完。oneway 可以修飾 AIDL 文件里的方法,oneway 修飾的方法在用戶請求相應功能時不需要等待響應可直接調用返回,非阻塞效果,該關鍵字可以用來聲明接口或者聲明方法,如果接口聲明中用到了 oneway 關鍵字,則該接口聲明的所有方法都采用 oneway 方式。(注意,如果 Client 和 Server 在同一進程中, oneway 修飾的方法還是會阻塞)

in

非基本數據類型和 String 的參數類型必須加參數修飾符, in 的意思是只輸入,既最終 Server 端執行完后不會影響到參數對象

out

與 in 相反, out 修飾的參數只能由 Server 寫入并傳遞到 Client,而 Client 傳入的值并不會傳遞到 Server

inout

被 inout 修飾的參數,既可以從 Client 傳遞到 Server,也可以 Server 傳遞到 Client

AIDL 自動生成文件講解

Talk is cheap, show you the code.

interface ISocketService {

    int getState();

    oneway void registerCallback(in ISocketServiceCallback callback);
    oneway void unregisterCallback(in ISocketServiceCallback callback);

    oneway void runShadowSocks(in Config config);
}

IDE 自動生成的代碼如下

public interface ISocketService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.shadark.app.aidl.ISocketService {
        private static final java.lang.String DESCRIPTOR = "com.shadark.app.aidl.ISocketService";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.shadark.app.aidl.ISocketService interface,
         * generating a proxy if needed.
         */
        public static com.shadark.app.aidl.ISocketService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.shadark.app.aidl.ISocketService))) {
                return ((com.shadark.app.aidl.ISocketService) iin);
            }
            return new com.shadark.app.aidl.ISocketService.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getState: {
                    data.enforceInterface(DESCRIPTOR);
                    int _result = this.getState();
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_registerCallback: {
                    data.enforceInterface(DESCRIPTOR);
                    com.shadark.app.aidl.ISocketServiceCallback _arg0;
                    _arg0 = com.shadark.app.aidl.ISocketServiceCallback.Stub.asInterface(data.readStrongBinder());
                    this.registerCallback(_arg0);
                    return true;
                }
                case TRANSACTION_unregisterCallback: {
                    data.enforceInterface(DESCRIPTOR);
                    com.shadark.app.aidl.ISocketServiceCallback _arg0;
                    _arg0 = com.shadark.app.aidl.ISocketServiceCallback.Stub.asInterface(data.readStrongBinder());
                    this.unregisterCallback(_arg0);
                    return true;
                }
                case TRANSACTION_runShadowSocks: {
                    data.enforceInterface(DESCRIPTOR);
                    com.shadark.app.aidl.Config _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.shadark.app.aidl.Config.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.runShadowSocks(_arg0);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.shadark.app.aidl.ISocketService {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public int getState() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getState, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void registerCallback(com.shadark.app.aidl.ISocketServiceCallback callback) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((callback != null)) ? (callback.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_registerCallback, _data, null, android.os.IBinder.FLAG_ONEWAY);
                } finally {
                    _data.recycle();
                }
            }

            @Override
            public void unregisterCallback(com.shadark.app.aidl.ISocketServiceCallback callback) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((callback != null)) ? (callback.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_unregisterCallback, _data, null, android.os.IBinder.FLAG_ONEWAY);
                } finally {
                    _data.recycle();
                }
            }

            @Override
            public void runShadowSocks(com.shadark.app.aidl.Config config) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((config != null)) {
                        _data.writeInt(1);
                        config.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_runShadowSocks, _data, null, android.os.IBinder.FLAG_ONEWAY);
                } finally {
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getState = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_registerCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_unregisterCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_runShadowSocks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
    }

    public int getState() throws android.os.RemoteException;

    public void registerCallback(com.shadark.app.aidl.ISocketServiceCallback callback) throws android.os.RemoteException;

    public void unregisterCallback(com.shadark.app.aidl.ISocketServiceCallback callback) throws android.os.RemoteException;

    public void runShadowSocks(com.shadark.app.aidl.Config config) throws android.os.RemoteException;
}

IDE 用我們編寫的 AIDL 文件,幫我們做了如下這些事情:

1.創建了 ISocketService 的實現類 Stub 和 Stub 的子類 Proxy

2.Stub 類中實現了有 IBinder 對象轉換為 ISocketService 類型的 asInterface, asInterface 中通過 queryLocalInterface(DESCRIPTOR) 方法查看本進程是否有 ISocketService 在 Server 端的實現類(既判斷 Server 與 Client 是否在同一進程),如果是同一進程就直接返回 Server 端的 ISocketService 實現者,如果不在同一進程就返回代理對象

3.Proxy 類中實現了 AIDL 中定義的方法,根據 oneway、in、out、inout 修飾符來生成不同的代碼,決定是否向 binder 驅動寫入數據或者執行完后向方法參數回寫數據。注意:oneway 修飾一個方法后,該方法不阻塞 client 調用線程,但是方法沒有返回值,方法參數在執行方法執行完后也不會回寫。

4.Proxy 類中實現的方法最終通過 transact() 方法向 Binder 驅動寫入數據(運行在 Client 進程),最終 Stub 類中的 onTransact() 方法會被調用到(運行在 Server 進程),就這樣完成一次跨進程方法調用。

Binder 死亡處理

在進程間通信過程中,很可能出現一個進程死亡的情況。如果這時活著的一方不知道另一方已經死了就會出現問題。那我們如何在 A 進程中獲取 B 進程的存活狀態呢?

Android 肯定給我們提供了解決方式,那就是 Binder 的 linkToDeath 和 unlinkToDeath 方法, linkToDeath 方法需要傳入一個 DeathRecipient 對象, DeathRecipient 類里面有個 binderDied 方法,當 binder 對象的所在進程死亡, binderDied 方法就會被執行,我們就可以在 binderDied 方法里面做一些 異常處理,釋放資源 等操作了。

示例如下:

...

    @Override
    public void onServiceConnected(ComponentName name, IBinder binder) {

        try {
            mBinder = binder;
            binder.linkToDeath(SocketServiceManager.this, 0);
            mSocketService = ISocketService.Stub.asInterface(binder);
            registerCallback();
            mCallback.onServiceConnected();
        } catch (RemoteException e) {
            LogUtils.e(TAG, "onServiceConnected: " + e.getMessage());
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        unregisterCallback();
        mCallback.onServiceDisconnected();

        if (null != mBinder) {
            mBinder.unlinkToDeath(this, 0);
            mBinder = null;
        }
    }
...
private class SocketServiceManager implements IBinder.DeathRecipient {

    ...

        @Override
        public void binderDied() {
            mCallbackList.unregister(mClientCallBack);
            mClientCallBack = null;
            Logger.d(TAG,"client  is died");
        }

    ...
}

上面是在 Server 端對 Client 的回調接口的 Binder 對象設置的 DeathRecipient。在 Client 死亡時,解注冊 Client 的回調,并且置空。

Client 注冊回調接口

之前一直說的都是 Client 向 Server 的通信,那如果 Server 要調用 Client 呢?

一個比較容易想到的辦法就是通過 AIDL 在 Server 端設置一個 Client 的回調。這樣的話就相當于 Client 端是 Server 端的 Server 了。

有注冊回調就肯定有解注冊,但是 Client 端與 Server 不在一個進程,Server 是無法得知 Client 解注冊時傳入的回調接口是哪一個( Client 調用解注冊時,是通過 Binder 傳輸到 Server 端,所以解注冊時的回調接口是新創建的,而不是注冊時的回調接口)。為了解決這個問題,Android 提供了 RemoteCallbackList 這個類來專門管理 remote 回調的注冊與解注冊。

AIDL 類

interface ITaskCallback {   
    void actionPerformed(int actionId);  
}
interface ITaskBinder {   
    boolean isTaskRunning();   
    void stopRunningTask();   
    void registerCallback(ITaskCallback cb);   
    void unregisterCallback(ITaskCallback cb);   
}

Service 類

public class MyService extends Service {   
    private static final String TAG = "aidltest";  
    final RemoteCallbackList <ITaskCallback>mCallbacks = new RemoteCallbackList <ITaskCallback>(); 
    ...

    @Override  
    public IBinder onBind(Intent t) {  
        printf("service on bind");  
        return mBinder;   
    }  

    @Override  
    public boolean onUnbind(Intent intent) {   
        printf("service on unbind");  
        return super.onUnbind(intent);   
    }  

    void callback(int val) {   
        final int N = mCallbacks.beginBroadcast();  
        for (int i=0; i<N; i++) {   
            try {  
                mCallbacks.getBroadcastItem(i).actionPerformed(val);   
            }  
            catch (RemoteException e) {   
                // The RemoteCallbackList will take care of removing   
                // the dead object for us.     
            }  
        }  
        mCallbacks.finishBroadcast();  
    }  

    private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() {  

        public void stopRunningTask() {  

        }  

        public boolean isTaskRunning() {   
            return false;   
        }   

        public void registerCallback(ITaskCallback cb) {   
            if (cb != null) {   
                mCallbacks.register(cb);  
            }  
        }  

        public void unregisterCallback(ITaskCallback cb) {  
            if(cb != null) {  
                mCallbacks.unregister(cb);  
            }  
        }  
    };   

}

Client 類

public class MyActivity extends Activity {   

    private static final String TAG = "aidltest";  
    private Button btnOk;   
    private Button btnCancel;  

...

    ITaskBinder mService;   

    private ServiceConnection mConnection = new ServiceConnection() {   

        public void onServiceConnected(ComponentName className, IBinder service) {  
            mService = ITaskBinder.Stub.asInterface(service);   
            try {   
                mService.registerCallback(mCallback);  
            } catch (RemoteException e) {  

            }  
        }  

        public void onServiceDisconnected(ComponentName className) {   
            mService = null;  
        }   
    };   

    private ITaskCallback mCallback = new ITaskCallback.Stub() {  

        public void actionPerformed(int id) {   
            printf("callback id=" + id);  
        }   
    };   

}

RemoteCallbackList 可以實現正常注冊于解注冊的原因在于注冊與解注冊時雖然對應的回調接口不是同一個,但是其對應的 Binder 對象卻是同一個。

Messenger 通信

以下概括了Messenger的使用方法:

  1. 服務實現一個 Handler ,用于客戶端每次調用時接收回調
  2. 此 Handler 用于創建一個 Messenger 對象(它是一個對 Handler 的引用)
  3. 此 Messenger 對象創建一個 IBinder ,服務在 onBind() 中把它返回給客戶端
  4. 客戶端用 IBinder 將 Messenger (引用服務的 Handler )實例化,客戶端用它向服務發送消息對象 Message
  5. 服務接收 Handler 中的每個消息 Message ——確切的說,是在 handleMessage() 方法中接收

Service 類

public class MessengerService extends Service {

/**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_REGISTER_CLIENT:
                    mClients.add(msg.replyTo);
                    break;
                case MSG_UNREGISTER_CLIENT:
                    mClients.remove(msg.replyTo);
                    break;
                case MSG_SET_VALUE:
                    mValue = msg.arg1;
                    for (int i=mClients.size()-1; i>=0; i--) {
                        try {
                            mClients.get(i).send(Message.obtain(null,
                                    MSG_SET_VALUE, mValue, 0));
                        } catch (RemoteException e) {
                            // The client is dead.  Remove it from the list;
                            // we are going through the list from back to front
                            // so this is safe to do inside the loop.
                            mClients.remove(i);
                        }
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    ...
}

Client 類

public static class Binding extends Activity {

    /**
     * Messenger for communicating with service.
     */
    Messenger mService = null;

    /**
     * Handler of incoming messages from service.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MessengerService.MSG_SET_VALUE:
                    mCallbackText.setText("Received from service: " + msg.arg1);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service.  We are communicating with our
            // service through an IDL interface, so get a client-side
            // representation of that from the raw service object.
            mService = new Messenger(service);
            mCallbackText.setText("Attached.");
            // We want to monitor the service for as long as we are
            // connected to it.
            try {
                Message msg = Message.obtain(null,
                        MessengerService.MSG_REGISTER_CLIENT);
                msg.replyTo = mMessenger;
                mService.send(msg);

                // Give it some value as an example.
                msg = Message.obtain(null,
                        MessengerService.MSG_SET_VALUE, this.hashCode(), 0);
                mService.send(msg);
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even
                // do anything with it; we can count on soon being
                // disconnected (and then reconnected if it can be restarted)
                // so there is no need to do anything here.
            }

            // As part of the sample, tell the user what happened.
            Toast.makeText(Binding.this, R.string.remote_service_connected,
                    Toast.LENGTH_SHORT).show();
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null;
            mCallbackText.setText("Disconnected.");
            // As part of the sample, tell the user what happened.
            Toast.makeText(Binding.this, R.string.remote_service_disconnected,
                    Toast.LENGTH_SHORT).show();
        }
    };

    ...
}

在 MessengerService.java (服務)和 MessengerServiceActivities.java (客戶端)例程中,可以看到如何關于 Messenger 的實用例子。

Messenger 和 AIDL 的異同

其實 Messenger 的底層也是用 AIDL 實現的,但用起來還是有些不同的,這里總結了幾點區別:

  1. Messenger 本質也是 AIDL,只是進行了封裝,開發的時候不用再寫 .aidl 文件

    結合自身的使用,因為不用去寫 .aidl 文件,相比起來,Messenger 使用起來十分簡單。但前面也說了,Messenger 本質上也是 AIDL,故在底層進程間通信這一塊,兩者的效率應該是一樣的。

  2. 在 Service 端,Messenger 處理 Client 端的請求是單線程的,而 AIDL 是多線程的

    使用 AIDL 的時候,service 端每收到一個 client 端的請求時,就在 Binder 線程池中取一個線程去執行相應的操作。而 Messenger ,service 收到的請求是放在 Handler 的 MessageQueue 里面,Handler 大家都用過,它需要綁定一個 Thread,然后不斷 poll message 執行相關操作,這個過程是同步執行的。

  3. Client 的方法,使用 AIDL 獲取返回值是同步的,而 Messenger 是異步的

    Messenger 只提供了一個方法進行進程間通信,就是 send(Message msg) 方法,發送的是一個 Message,沒有返回值,要拿到返回值,需要把 Client 的 Messenger 作為 msg.replyTo 參數傳遞過去,Service 端處理完之后,在調用客戶端的 Messenger 的 send(Message msg) 方法把返回值傳遞回 Client,這個過程是異步的,而 AIDL 你可以自己指定方法,指定返回值,它獲取返回值是同步的(如果沒有用 oneway 修飾方法的話)。

 

來自:http://www.jianshu.com/p/d46f20695cb7

 

 本文由用戶 ezra12519 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!