Android Bound Service(二) ----- Using AIDL

refs: http://developer.android.com/guide/components/aidl.html

AIDL(Android Interface Definition Language) 就像其它接口定义语言一样。它使你可以定义服务端及客户端程序的接口,以达到跨进程沟通( IPC )的目的


注意:

1。在多进程多线程的情况下,我们才使用 AIDL

2。单一进程时,使用实现 Binder 类的方式定义接口

3。如果只有跨进程,但不需処理多线程的情况,请使用 Messenger


一、创造一个 AIDL 档案

// IMyAidlInterface.aidl
package com.example.shanwu.interprocesscomm;

// Declare any non-default types here with import statements

interface IMyAidlInterface {
    int getPid();
    String getServiceName();
    void makeSingingService();
}


AIDL 支持以下数据类型做为接口方法的参数与回传类型:

1. 所有 primitive data type

2. String

3. CharSequence

4. List

5. Map

将 aidl 档存放於 /src 下,在编译过程,其会自动生成一个 .java 档,包括了一个名为 Stub 的子类,其为一个 aidl接口类的 abstract implmentation,并且有著所有 aidl 接口类的所有宣告方法如下,这些我们在後面会一一讲解,使我们具有自己实现,而不依赖 aidl 的能力,如下:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /home/shanwu/GitHub/InterProcessCommPractice/InterProcessComm/app/src/main/aidl/com/example/shanwu/interprocesscomm/IMyAidlInterface.aidl
 */
package com.example.shanwu.interprocesscomm;
// Declare any non-default types here with import statements

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

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

        /**
         * Cast an IBinder object into an com.example.shanwu.interprocesscomm.IMyAidlInterface interface,
         * generating a proxy if needed.
         */
        public static com.example.shanwu.interprocesscomm.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.shanwu.interprocesscomm.IMyAidlInterface))) {
                return ((com.example.shanwu.interprocesscomm.IMyAidlInterface) iin);
            }
            return new com.example.shanwu.interprocesscomm.IMyAidlInterface.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_getPid: {
                    data.enforceInterface(DESCRIPTOR);
                    int _result = this.getPid();
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_getServiceName: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getServiceName();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                case TRANSACTION_makeSingingService: {
                    data.enforceInterface(DESCRIPTOR);
                    this.makeSingingService();
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.example.shanwu.interprocesscomm.IMyAidlInterface {
            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 getPid() 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_getPid, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public java.lang.String getServiceName() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getServiceName, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void makeSingingService() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_makeSingingService, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getServiceName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_makeSingingService = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }

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

    public java.lang.String getServiceName() throws android.os.RemoteException;

    public void makeSingingService() throws android.os.RemoteException;
}

Stub 也定义了一些辅助方法,该特别注意的是 asInterface(),其拿一个 IBinder 作为参数(通常是传进客户端的 onServiceConnected()的回调方法)并返回一个 stub 接口对象。


二、实现接口

       我们需要实现 aidl 产生的接口,范例如下:

    private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
        @Override
        public int getPid() throws RemoteException {
            return android.os.Process.myPid();
        }

        @Override
        public String getServiceName() throws RemoteException {
            return "Worker Service~ ";
        }

        @Override
        public void makeSingingService() {
            Log.d("guang", "singing~~~in log...");
            //Toast.makeText(WorkerService.this, "singing~~~", Toast.LENGTH_LONG).show(); not gonna work for the exception
            mHandler.sendEmptyMessage(MSG_START_SING);
        }
    };

现在 mBinder 是一个 Stub 类的实例,并且实现了接口。下一步,这个实例将暴露给客户端,以使他们能够和 Service 互动。在实现 aidl 接口的时候,我们需要注意以下几点:

1. 因为命令不见得是在主线程上执行,所以必须考量多线程的情况,该 Service 得是线程安全。

2. RPC命令一般缺省的情况是同步的。如果 Service 会花一些时间処理一个 request 的话,便不应从主线程呼叫,否则会产生 ANR。

3. 没有任何的异常会回传给呼叫方 (caller)


三、暴露接口给给客户端使用

当客户端呼叫 bindService() 以建立连结时,onServiceConntected回调会接收到 Service onBind()所返回的 mBinder 实例。如果客端是在不同的应用,则客户端也要有一份 aidl 档案在 src/ 路径里。当客户端在 onServiceConnected()回调接收到 IBinder後,我们必须调用 AIDL接口类.Stub.asInterface(service),并将其返回值强转为我们的 AIDL接口类名,如下:

public class MainActivity extends Activity implements View.OnClickListener {
    private ServiceConnection mServiceConn;
    private IMyAidlInterface mService;
    private boolean mIsBind = false;
    private int mWorkerId = -1;

    private void initData() {
        mServiceConn = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                mService = IMyAidlInterface.Stub.asInterface(iBinder);
                Log.d("guang", "onServiceConnected");
                mIsBind = true;
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                Log.d("guang", "onServiceDisconnected");
                mIsBind = false;
            }
        };
    }
然後就可以从客户端呼叫服务端的 Service 了

            case R.id.btn_sing:
                if (mIsBind) {
                    try {
                        mWorkerId = mService.getPid();
                        Toast.makeText(MainActivity.this, mWorkerId + "", Toast.LENGTH_LONG).show();
                        mService.makeSingingService();
                    } catch (RemoteException e) {
                        Log.e("guang", "Service is dead " + e);
                    }
                }
                break;


完整例子:

https://github.com/shanwu/InterProcessCommPractice/tree/ipc_aidl_example



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。