美文网首页
aidl 流程简单分析

aidl 流程简单分析

作者: 曾大稳丶 | 来源:发表于2018-01-26 13:59 被阅读0次

    我们通过写一个从服务端(另外一个进程)获取用户名和密码作为demo来进行源码讲解。

    1. new一个IUserAidl.aidl
    newAidl.jpg
    
    interface IUserAidl {
        //注意 String  S大写 
        String getUserName();
        String getPwd();
    }
    
    
    1. make生成IUserAidl.java文件
    2. 写一个service作为服务端
    
    
    /**
     * Des:
     * Created by zzw on 2018/1/26.
     */
    
    public class MessageService extends Service {
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return new UserBinder();
        }
    
        private final class UserBinder extends IUserAidl.Stub {
    
            @Override
            public String getUserName() throws RemoteException {
                return "zzw";
            }
    
            @Override
            public String getPwd() throws RemoteException {
                return "123456";
            }
        }
    }
    
    
    
    1. AndroidManifest.xml注册service
     <service
        android:process=":test"
        android:name=".MessageService"/>
    
    
    1. Activity绑定
     public class MainActivity extends AppCompatActivity {
    
        //客户端获取的aidl实例 , 通过这个实例就可以进行通讯
        private IUserAidl mIUserAidl;
    
        private ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //连接
                mIUserAidl = IUserAidl.Stub.asInterface(service);
                Log.e("zzz", "连接成功");
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                //断开连接
                Log.e("zzz", "断开连接");
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //启动服务
            Intent intent = new Intent(this, MessageService.class);
            bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        }
    
    
    
        public void getUserName(View view) {
            try {
                if (mIUserAidl != null) {
                    String userName = mIUserAidl.getUserName();
                    Log.e("zzz", userName);
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    
        public void getPwd(View view) {
            try {
                if (mIUserAidl != null) {
                    String pwd = mIUserAidl.getPwd();
                    Log.e("zzz", pwd);
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(mServiceConnection);
        }
    }
    
    

    启动应用,发现是有test进程的 ,说明没问题。在当前客户端的进程(Acticity)可以发现是打印连接成功,然后依次点击获取用户名和密码,都对应打印成功,说明程序正常。我们接下来就开始分析源码,看看是怎么通讯的。

    IUserAidl.java

    public interface IUserAidl extends android.os.IInterface {
        /**
         * Local-side IPC implementation stub class.
         */
        public static abstract class Stub extends android.os.Binder implements com.zzw.testaidl.IUserAidl {
            private static final java.lang.String DESCRIPTOR = "com.zzw.testaidl.IUserAidl";
    
            /**
             * Construct the stub at attach it to the interface.
             */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
            /**
             * Cast an IBinder object into an com.zzw.testaidl.IUserAidl interface,
             * generating a proxy if needed.
             */
            public static com.zzw.testaidl.IUserAidl asInterface(android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                if (((iin != null) && (iin instanceof com.zzw.testaidl.IUserAidl))) {
                    return ((com.zzw.testaidl.IUserAidl) iin);
                }
                return new com.zzw.testaidl.IUserAidl.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_getUserName: {
                        data.enforceInterface(DESCRIPTOR);
                        java.lang.String _result = this.getUserName();
                        reply.writeNoException();
                        reply.writeString(_result);
                        return true;
                    }
                    case TRANSACTION_getPwd: {
                        data.enforceInterface(DESCRIPTOR);
                        java.lang.String _result = this.getPwd();
                        reply.writeNoException();
                        reply.writeString(_result);
                        return true;
                    }
                }
                return super.onTransact(code, data, reply, flags);
            }
    
            private static class Proxy implements com.zzw.testaidl.IUserAidl {
                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 java.lang.String getUserName() 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_getUserName, _data, _reply, 0);
                        _reply.readException();
                        _result = _reply.readString();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
    
                @Override
                public java.lang.String getPwd() 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_getPwd, _data, _reply, 0);
                        _reply.readException();
                        _result = _reply.readString();
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
            }
    
            static final int TRANSACTION_getUserName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
            static final int TRANSACTION_getPwd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        }
    
        public java.lang.String getUserName() throws android.os.RemoteException;
    
        public java.lang.String getPwd() throws android.os.RemoteException;
    }
    
    

    从上面这些代码我们可以知道,当客户端(Activity)和服务端(Service)bind的时候,服务端会把IUserAidl.Stub当做IBinder给传过来,然后通过IUserAidl.Stub.asInterface(service);拿到真正的IUserAidl实现类IUserAidl.Stub.Proxy
    我们进入IUserAidl.Stub.Proxy这个类中,从调用的函数(getUserName getPwd)点进去查看是怎么进行通讯的。

    Proxy关键代码:
    //mRemote是服务端返回的IBinder实例  IUserAidl.Stub
    _data.writeInterfaceToken(DESCRIPTOR);
    mRemote.transact(Stub.TRANSACTION_getUserName, _data, _reply, 0);
    _reply.readException();
    _result = _reply.readString();
    
    

    看到实际是调用了IUserAidl.Stubtransact函数,但是在IUserAidl.Stub中并没有transact函数,只有一个onTransact函数,这里我们可以猜测应该是调用transact的时候调用了onTransact函数

    Stub.onTransact关键代码
    @Override
            public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws
                    android.os.RemoteException {
        switch (code) {
            case TRANSACTION_getUserName: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getUserName();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                  }
         }
     }
    

    首先通过code判断调用的是那个函数,然后在调用服务端的getUserName()函数拿到对应的值,在写入reply里面,然后在Proxy里面通过_reply读取出来,这样就完成了数据传递。

    我们来验证我们的想法是不是在transact调用了onTransact。我们知道mRemoteIUserAidl.Stub,他是继承Binder的,在Binder查看到transact函数

    
    /**
         * Default implementation rewinds the parcels and calls onTransact.  On
         * the remote side, transact calls into the binder to do the IPC.
         */
        public final boolean transact(int code, Parcel data, Parcel reply,
                int flags) throws RemoteException {
            if (false) Log.v("Binder", "Transact: " + code + " to " + this);
    
            if (data != null) {
                data.setDataPosition(0);
            }
            boolean r = onTransact(code, data, reply, flags);
            if (reply != null) {
                reply.setDataPosition(0);
            }
            return r;
        }
    
    

    我们从这段代码就可以看到确实是在transact中调用了onTransact函数。

    总结:

    1. 客户端通过bind拿到服务端IBinder对象xxxx.Stub,然后通过xxxx.Stub.asInterface函数拿到对应的服务端通讯的代理类xxxx.Stub.Proxy
    2. 每个通讯的函数和都会生成一个code,当我们客户端调用函数时都会通过服务端xxxx.Stub对象调用transact函数,并将相应的code Parcel对象传入,然后回调onTransact函数,通过code判断调用服务端的对应的函数,拿到对应的数据将之写入Parcel里面
    3. 服务端调用完毕之后,客户端通过Parcel拿到对应的数据,然后返回即可。

    相关文章

      网友评论

          本文标题:aidl 流程简单分析

          本文链接:https://www.haomeiwen.com/subject/bnaiaxtx.html