二、Android中的IPC方式:AIDL

AIDL相关的重要类和接口

1.IInterface
2.IBinder
3.Binder
4.BinderProxy

IInterface

public interface IInterface
{
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    public IBinder asBinder();
}

IBinder

/**
     * Get the canonical name of the interface supported by this binder.
     */
    public @Nullable String getInterfaceDescriptor() throws RemoteException;

    /**
     * Check to see if the object still exists.
     * 
     * @return Returns false if the
     * hosting process is gone, otherwise the result (always by default
     * true) returned by the pingBinder() implementation on the other
     * side.
     */
    public boolean pingBinder();

    /**
     * Check to see if the process that the binder is in is still alive.
     *
     * @return false if the process is not alive.  Note that if it returns
     * true, the process may have died while the call is returning.
     */
    public boolean isBinderAlive();
    
    /**
     * Attempt to retrieve a local implementation of an interface
     * for this Binder object.  If null is returned, you will need
     * to instantiate a proxy class to marshall calls through
     * the transact() method.
     */
    public @Nullable IInterface queryLocalInterface(@NonNull String descriptor);

    /**
     * Print the object's state into the given stream.
     * 
     * @param fd The raw file descriptor that the dump is being sent to.
     * @param args additional arguments to the dump request.
     */
    public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException;

    /**
     * Like {@link #dump(FileDescriptor, String[])} but always executes
     * asynchronously.  If the object is local, a new thread is created
     * to perform the dump.
     *
     * @param fd The raw file descriptor that the dump is being sent to.
     * @param args additional arguments to the dump request.
     */
    public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args)
            throws RemoteException;

    /**
     * Execute a shell command on this object.  This may be performed asynchrously from the caller;
     * the implementation must always call resultReceiver when finished.
     *
     * @param in The raw file descriptor that an input data stream can be read from.
     * @param out The raw file descriptor that normal command messages should be written to.
     * @param err The raw file descriptor that command error messages should be written to.
     * @param args Command-line arguments.
     * @param shellCallback Optional callback to the caller's shell to perform operations in it.
     * @param resultReceiver Called when the command has finished executing, with the result code.
     * @hide
     */
    public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
            @Nullable FileDescriptor err,
            @NonNull String[] args, @Nullable ShellCallback shellCallback,
            @NonNull ResultReceiver resultReceiver) throws RemoteException;

    /**
     * Perform a generic operation with the object.
     * 
     * @param code The action to perform.  This should
     * be a number between {@link #FIRST_CALL_TRANSACTION} and
     * {@link #LAST_CALL_TRANSACTION}.
     * @param data Marshalled data to send to the target.  Must not be null.
     * If you are not sending any data, you must create an empty Parcel
     * that is given here.
     * @param reply Marshalled data to be received from the target.  May be
     * null if you are not interested in the return value.
     * @param flags Additional operation flags.  Either 0 for a normal
     * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
     *
     * @return Returns the result from {@link Binder#onTransact}.  A successful call
     * generally returns true; false generally means the transaction code was not
     * understood.
     */
    public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
        throws RemoteException;

    /**
     * Interface for receiving a callback when the process hosting an IBinder
     * has gone away.
     * 
     * @see #linkToDeath
     */
    public interface DeathRecipient {
        public void binderDied();
    }

    /**
     * Register the recipient for a notification if this binder
     * goes away.  If this binder object unexpectedly goes away
     * (typically because its hosting process has been killed),
     * then the given {@link DeathRecipient}'s
     * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
     * will be called.
     * 
     * 

You will only receive death notifications for remote binders, * as local binders by definition can't die without you dying as well. * * @throws RemoteException if the target IBinder's * process has already died. * * @see #unlinkToDeath */ public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException; /** * Remove a previously registered death notification. * The recipient will no longer be called if this object * dies. * * @return {@code true} if the recipient is successfully * unlinked, assuring you that its * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method * will not be called; {@code false} if the target IBinder has already * died, meaning the method has been (or soon will be) called. * * @throws java.util.NoSuchElementException if the given * recipient has not been registered with the IBinder, and * the IBinder is still alive. Note that if the recipient * was never registered, but the IBinder has already died, then this * exception will not be thrown, and you will receive a false * return value instead. */ public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);

Binder

public class Binder implements IBinder {
   ...
   public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

   public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }
   ...
}

IMusicService.aidl 的定义

interface IMusicService {

    void updateMusicList(in List musicFileList);

    void getPlayList(out List musicFileList);

    boolean play(int position);

    boolean pause();

    boolean stop();

}

void updateMusicList(in List musicFileList);
void getPlayList(out List musicFileList);中的in和out表示数据走向的方向标记。in表示走向Remote服务,out表示走向客户端。它们之间的细节差别表现:

二、Android中的IPC方式:AIDL_第1张图片
in_out数据走向01.png
二、Android中的IPC方式:AIDL_第2张图片
in_out数据走向02.png

IMusicService 的类结构

IMusicService 有一个抽象的子类Stub, Stub继承自Binder,实现了IMusicService接口。Stub里面定义了一个Proxy类,Stub.Proxy直接实现了IMusicService接口。

public interface IMusicService extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements com.chiang.chnplayer.aidl.IMusicService {
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        private static class Proxy implements com.chiang.chnplayer.aidl.IMusicService {
            
        }

        public static com.chiang.chnplayer.aidl.IMusicService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.chiang.chnplayer.aidl.IMusicService))) {
                return ((com.chiang.chnplayer.aidl.IMusicService) iin);
            }
            return new com.chiang.chnplayer.aidl.IMusicService.Stub.Proxy(obj);
        }

    }

    public void updateMusicList(java.util.List musicFileList) throws android.os.RemoteException;

    public void getPlayList(java.util.List musicFileList) throws android.os.RemoteException;

    public boolean play(int position) throws android.os.RemoteException;

    public boolean pause() throws android.os.RemoteException;

    public boolean stop() throws android.os.RemoteException;
}
二、Android中的IPC方式:AIDL_第3张图片
AIDL_IMusicService02_类结构.png

IMusicService 的具体实现

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

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

        /**
         * Cast an IBinder object into an com.chiang.chnplayer.aidl.IMusicService interface,
         * generating a proxy if needed.
         */
        public static com.chiang.chnplayer.aidl.IMusicService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.chiang.chnplayer.aidl.IMusicService))) {
                return ((com.chiang.chnplayer.aidl.IMusicService) iin);
            }
            return new com.chiang.chnplayer.aidl.IMusicService.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_updateMusicList: {
                    //验证Binder标识
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List _arg0;
                    _arg0 = data.createTypedArrayList(com.chiang.chnplayer.aidl.SongInfo.CREATOR);
                    this.updateMusicList(_arg0);//这调用的是继承IMusicService.Stub后实现的方法
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getPlayList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List _arg0;
                    _arg0 = new java.util.ArrayList();
                    this.getPlayList(_arg0);
                    reply.writeNoException();
                    reply.writeTypedList(_arg0);
                    return true;
                }
                case TRANSACTION_play: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    boolean _result = this.play(_arg0);
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }
                case TRANSACTION_pause: {
                    data.enforceInterface(DESCRIPTOR);
                    boolean _result = this.pause();
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }
                case TRANSACTION_stop: {
                    data.enforceInterface(DESCRIPTOR);
                    boolean _result = this.stop();
                    reply.writeNoException();
                    reply.writeInt(((_result) ? (1) : (0)));
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.chiang.chnplayer.aidl.IMusicService {
            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 void updateMusicList(java.util.List musicFileList) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeTypedList(musicFileList);
                    mRemote.transact(Stub.TRANSACTION_updateMusicList, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void getPlayList(java.util.List musicFileList) 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_getPlayList, _data, _reply, 0);
                    _reply.readException();
                    _reply.readTypedList(musicFileList, com.chiang.chnplayer.aidl.SongInfo.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

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

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

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

        static final int TRANSACTION_updateMusicList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getPlayList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_play = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_pause = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        static final int TRANSACTION_stop = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
    }

    public void updateMusicList(java.util.List musicFileList) throws android.os.RemoteException;

    public void getPlayList(java.util.List musicFileList) throws android.os.RemoteException;

    public boolean play(int position) throws android.os.RemoteException;

    public boolean pause() throws android.os.RemoteException;

    public boolean stop() throws android.os.RemoteException;
}

data.enforceInterface(DESCRIPTOR);//验证Binder标识

IMusicService.Stub的实现类(这里是匿名实现类)

private IMusicService.Stub mBinder=new IMusicService.Stub() {
        
        @Override
        public boolean stop() throws RemoteException {
            
            return mMusicPlayer.stop();
        }

        @Override
        public void updateMusicList(List musicFileList)
                throws RemoteException {
            mMusicPlayer.updateMusicList(musicFileList);
        }
        
        @Override
        public boolean play(int position) throws RemoteException {
            
            return mMusicPlayer.play(position);
        }
        
        @Override
        public boolean pause() throws RemoteException {
            
            return mMusicPlayer.pause();
        }

        @Override
        public void getPlayList(List musicFileList)
                throws RemoteException {
            List tmp = mMusicPlayer.getFileList();
            int count = tmp.size();
            for(int i = 0; i < count; i++)
            {
                musicFileList.add(tmp.get(i));
            }
        }
    
    };

ServiceConnection中onServiceConnected参数 IBinder service的来源?

class MServiceConnetion implements ServiceConnection{
        @Override
        public void onServiceConnected(ComponentName name, IBinder service){ 
            mIMusicService=IMusicService.Stub.asInterface(service);
            
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            
        }
        
    }

关于在同一进程绑定Service和跨进程绑定Service后,ServiceConnection返回的IBinder对象和X.Stub.asInterface返回的对象。

    private IPayService mIPayService;

    class PayServiceConn implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIPayService = IPayService.Stub.asInterface(service);
            mIPayService.toString();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }
    public static com.fmtech.aidl_payservice.IPayService asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.fmtech.aidl_payservice.IPayService))) {
            return ((com.fmtech.aidl_payservice.IPayService) iin);
        }
        return new com.fmtech.aidl_payservice.IPayService.Stub.Proxy(obj);
    }

public class PayService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new PayServiceStub();
    }

    private void pay(){
        ...
    }

    class PayServiceStub extends IPayService.Stub{
        @Override
        public void callMethodInService() throws RemoteException {
            pay();
        }
    }
}

1.同一进程绑定Service
ServiceConnection#onServiceConnected返回的对象是PayService在onBind中直接返回的PayServiceStub实例;IPayService.Stub.asInterface方法返回的也是PayServiceStub实例。所以,当MainActivity中mIPayService调用callMethodInService时,直接调用的是Payservice中的callMethodInService方法。不需要走其他流程。


二、Android中的IPC方式:AIDL_第4张图片
onServiceConnected返回的IBinder对象01.png

2.跨进程绑定Service
ServiceConnection#onServiceConnected返回的对象是BinderProxy实例;IPayService.Stub.asInterface方法返回的是IPayService.Stub.Proxy实例。所以,当MainActivity中mIPayService调用callMethodInService时流程如下:
-->IPayService.Stub.Proxy#callMethodInService()-->BinderProxy#transact()

BinderProxy#queryLocalInterface()

    public IInterface queryLocalInterface(String descriptor) {
        return null;
    }

当onServiceConnected返回的对象是BinderProxy时,asInterface()方法中queryLocalInterface()返回的是null,所以asInterface()方法会返回*.Stub.Proxy

        public static com.chiang.chnplayer.aidl.IMusicService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.chiang.chnplayer.aidl.IMusicService))) {
                return ((com.chiang.chnplayer.aidl.IMusicService) iin);
            }
            return new com.chiang.chnplayer.aidl.IMusicService.Stub.Proxy(obj);
        }
    @Override
    public void callMethodInService() 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(IPayService.Stub.TRANSACTION_callMethodInService, _data, _reply, 0);//mRemote即是BinderProxy实例
            _reply.readException();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }
    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        ...
       return transactNative(code, data, reply, flags);
    }

public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;

BinderProxy.transact后会调用native方法,由native层进行处理;最后,再由native层回调IPayService.Stub.onTransact()方法,进而调用callMethodInService()方法。

二、Android中的IPC方式:AIDL_第5张图片
onServiceConnected返回的IBinder对象02.png

为什么跨进程绑定Service时,ServiceConnection#onServiceConnected返回的对象是BinderProxy实例?

........

【相关源码】MusicService

你可能感兴趣的:(二、Android中的IPC方式:AIDL)