zoukankan      html  css  js  c++  java
  • AIDL 定向tag IPC 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
    MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

    AIDL 定向tag IPC 案例 MD


    目录

    定向tag

    总结

    Android官网上在讲到AIDL的地方关于定向tag是这样介绍的:

    All non-primitive parameters require a directional tag indicating which way the data goes. //所有的非基本数据类型的参数都需要一个定向tag来指出数据的流向
    Either in , out , or inout. //可以是 in , out ,或者 inout
    Primitives are in by default , and connot be otherwise. //基本数据类型参数的定向tag默认是,并且只能是 in

    AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通

    其中,数据流向是针对在客户端中的那个传入方法的对象而言的:

    • in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;
    • out 的话表现为服务端将会接收到那个对象的参数为空的对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动
    • inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。

    测试案例

    客户端
    服务端

    AIDL文件夹

    public class Book implements Parcelable{}
    package com.bqt.aidl;
    parcelable Book;
    package com.bqt.aidl;
    import com.bqt.aidl.Book;
    
    interface BookManager {
        Book addBookIn(in Book book);
        Book addBookOut(out Book book);
        Book addBookInout(inout Book book);
    }
    sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }

    服务端

    服务端的逻辑是,首先接受客户端连接的请求,并把服务端处理好的BookManager.Stub的IBinder接口回传给客户端。在BookManager.Stub实现的方法里面,主要是接收客户端传过来的Book对象,并试图对其进行修改,然后把修改过的对象再传回去。

    public class AIDLService extends Service {
    
        @Override
        public IBinder onBind(Intent intent) {
            Log.i("bqt", "【Service-onBind】");
            return new MyBind();
        }
    
        private class MyBind extends BookManager.Stub {
    
            @Override
            public synchronized Book addBookIn(Book book) throws RemoteException {
                modifyBook(book, 100, "Service-In");
                return book;
            }
    
            @Override
            public synchronized Book addBookOut(Book book) throws RemoteException {
                modifyBook(book, 200, "Service-Out");
                return book;
            }
    
            @Override
            public synchronized Book addBookInout(Book book) throws RemoteException {
                modifyBook(book, 300, "Service-Inout");
                return book;
            }
    
            private void modifyBook(Book book, int i, String s) {
                if (book != null) {
                    Log.i("bqt", "【Service-接收到的Book】" + book);
                    book.setPrice(i);//修改book,观察客户端的反馈
                    book.setName(s);
                    Log.i("bqt", "【Service-返回的Book】" + book);
                }
            }
        }
    }
    <!-- 声明权限 -->
    <permission
        android:name="com.bqt.permission"
        android:protectionLevel="normal"/>
    
    <!-- 隐式服务 -->
    <service
        android:name=".AIDLService"
        android:permission="com.bqt.permission">
        <intent-filter>
            <action android:name="com.bqt.service.aidl"/>
        </intent-filter>
    </service>

    客户端

    public class MainActivity extends ListActivity {
        private BookManager mBookManager;
        private MyServiceConnection mServiceConnection;
    
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            String[] array = {
                "addBookIn",
                "addBookOut",
                "addBookInout"};
            setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));
    
            mServiceConnection = new MyServiceConnection();
            Intent intent = new Intent();
            intent.setAction("com.bqt.service.aidl");
            intent.setPackage("com.bqt.aidl2");
            bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mServiceConnection != null) {
                unbindService(mServiceConnection);
            }
            mBookManager = null;
        }
    
        @Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
            if (mBookManager != null) {
                try {
                    Book book = null, returnBook = null;
                    switch (position) {
                        case 0:
                            book = new Book("客户端-In", 10);
                            Log.i("bqt", "【客户端-传进去的Book-执行前】" + book);
                            returnBook = mBookManager.addBookIn(book);
                            break;
                        case 1:
                            book = new Book("客户端-Out", 20);
                            Log.i("bqt", "【客户端-传进去的Book-执行前】" + book);
                            returnBook = mBookManager.addBookOut(book);
                            break;
                        case 2:
                            book = new Book("客户端-Inout", 30);
                            Log.i("bqt", "【客户端-传进去的Book-执行前】" + book);
                            returnBook = mBookManager.addBookInout(book);
                            break;
                    }
    
                    Log.i("bqt", "【客户端-returnBook】" + returnBook);
                    Log.i("bqt", "【客户端-传进去的Book-执行后】" + book);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private class MyServiceConnection implements ServiceConnection {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Toast.makeText(MainActivity.this, "服务已连接", Toast.LENGTH_SHORT).show();
                mBookManager = BookManager.Stub.asInterface(service);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                Toast.makeText(MainActivity.this, "服务已断开", Toast.LENGTH_SHORT).show();
                mBookManager = null;
            }
        }
    }
    <!-- 声明启动服务所需要的权限 -->
    <uses-permission android:name="com.bqt.permission"/>

    演示过程

    In

    【客户端-传进去的Book-执行前】{name=客户端-In,price=10}
    【Service-接收到的Book】{name=客户端-In,price=10}    //收到了客户端原始对象的值【in】
    【Service-返回的Book】{name=Service-In,price=100}
    【客户端-returnBook】{name=Service-In,price=100}
    【客户端-传进去的Book-执行后】{name=客户端-In,price=10}   //客户端原始对象的值没有改变

    Out

    【客户端-传进去的Book-执行前】{name=客户端-Out,price=20}
    【Service-接收到的Book】{name=null,price=0}             //没有收到客户端原始对象的值
    【Service-返回的Book】{name=Service-Out,price=200}
    【客户端-returnBook】{name=Service-Out,price=200}
    【客户端-传进去的Book-执行后】{name=Service-Out,price=200}   //修改了客户端原始对象的值【out】

    InOut

    【客户端-传进去的Book-执行前】{name=客户端-Inout,price=30}
    【Service-接收到的Book】{name=客户端-Inout,price=30}    //收到了客户端原始对象的值【in】
    【Service-返回的Book】{name=Service-Inout,price=300}
    【客户端-returnBook】{name=Service-Inout,price=300}
    【客户端-传进去的Book-执行后】{name=Service-Inout,price=300}   //修改了客户端原始对象的值【out】

    源码分析

    在 AIDL 文件生成的 java 文件中,在进行远程调用的时候基本的调用顺序是:

    • 先从 Proxy 类中调用对应的方法
    • 然后在这些方法中调用 transact() 方法
    • 然后 Stub 中的 onTransact() 方法就会被调用
    • 然后在这个方法里面再调用具体的业务逻辑的方法
    • 当然,在这几个方法调用的过程中,总是会有一些关于数据的写入读出的操作,因为这些是跨进程操作,必须将数据序列化传输。

    BookManager.Stub.Proxy

    private static class Proxy implements com.bqt.aidl.BookManager {
        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 com.bqt.aidl.Book addBookIn(com.bqt.aidl.Book book) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain(); //代表从客户端流向服务端的数据流
            android.os.Parcel _reply = android.os.Parcel.obtain(); //代表从服务端流向客户端的数据流
            com.bqt.aidl.Book _result; //代表客户端调用服务端方法后的返回值,返回值没什么可研究的
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                if ((book != null)) {
                    _data.writeInt(1); //如果book不为空,则_data写入int值1
                    book.writeToParcel(_data, 0); //将book中的数据写入_data中(客户端的数据被完整保留了起来)
                } else {
                    _data.writeInt(0); //如果book为空,则_data写入int值0
                }
                mRemote.transact(Stub.TRANSACTION_addBookIn, _data, _reply, 0); //间接调用onTransact方法
                _reply.readException();
                if ((0 != _reply.readInt())) {
                    _result = com.bqt.aidl.Book.CREATOR.createFromParcel(_reply);
                } else {
                    _result = null;
                }
                //可以看到,在返回_result之前,没有修改客户端传递过来的book对象,所以客户端原始对象不会被改变
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    
        @Override
        public com.bqt.aidl.Book addBookOut(com.bqt.aidl.Book book) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            com.bqt.aidl.Book _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                //可以看到,这里没有将book对象写入_data流就开始传输了,也即客户端传过来的数据内容被丢弃了
                mRemote.transact(Stub.TRANSACTION_addBookOut, _data, _reply, 0);
                _reply.readException();
                if ((0 != _reply.readInt())) {
                    _result = com.bqt.aidl.Book.CREATOR.createFromParcel(_reply);
                } else {
                    _result = null;
                }
                if ((0 != _reply.readInt())) {
                    book.readFromParcel(_reply); //从_reply读取数据然后写入book中(客户端的数据被改变了)
                    //可以看到,在返回_result之前,会修改客户端传递过来的book对象,所以客户端原始对象会被改变
                }
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    
        @Override
        public com.bqt.aidl.Book addBookInout(com.bqt.aidl.Book book) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            com.bqt.aidl.Book _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                if ((book != null)) {
                    _data.writeInt(1);
                    book.writeToParcel(_data, 0); //将book中的数据写入_data中(客户端的数据被完整保留了起来)
                } else {
                    _data.writeInt(0);
                }
                mRemote.transact(Stub.TRANSACTION_addBookInout, _data, _reply, 0);
                _reply.readException();
                if ((0 != _reply.readInt())) {
                    _result = com.bqt.aidl.Book.CREATOR.createFromParcel(_reply);
                } else {
                    _result = null;
                }
                if ((0 != _reply.readInt())) {
                    book.readFromParcel(_reply);//从_reply读取数据然后写入book中(客户端的数据被改变了)
                }
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    }

    BookManager.Stub.onTransact

    @Override
    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
        //data代表从客户端流向服务端的数据流,reply代表从服务端流向客户端的数据流
        switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_addBookIn: {
                data.enforceInterface(DESCRIPTOR);
                com.bqt.aidl.Book _arg0; //_arg0代表客户端输入的book对象
                if ((0 != data.readInt())) {
                    _arg0 = com.bqt.aidl.Book.CREATOR.createFromParcel(data);//获取客户端输入的book对象
                } else {
                    _arg0 = null;
                }
                com.bqt.aidl.Book _result = this.addBookIn(_arg0);//这里才是真正调用服务端写好的实现的地方【代理】
                //_result代表客户端调用服务端方法后的返回值,返回值没什么可研究的
                reply.writeNoException();
                if ((_result != null)) {
                    reply.writeInt(1);
                    _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); //将返回值写入reply
                } else {
                    reply.writeInt(0);
                }
                //执行完方法之后就结束了,对_arg0的修改没有同步到reply
                return true;
            }
            case TRANSACTION_addBookOut: {
                data.enforceInterface(DESCRIPTOR);
                com.bqt.aidl.Book _arg0;
                _arg0 = new com.bqt.aidl.Book(); //可以看到,此时没有从data里读取book对象的操作,而是直接new了一个book对象,这就是为什么服务端收不到客户端传过来的数据
                com.bqt.aidl.Book _result = this.addBookOut(_arg0);//【代理】
                reply.writeNoException();
                if ((_result != null)) {
                    reply.writeInt(1);
                    _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); //将返回值写入reply
                } else {
                    reply.writeInt(0);
                }
                //将_arg0写入reply中
                if ((_arg0 != null)) {
                    reply.writeInt(1);
                    _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);//同步_arg0
                } else {
                    reply.writeInt(0);
                }
                return true;
            }
            case TRANSACTION_addBookInout: {
                data.enforceInterface(DESCRIPTOR);
                com.bqt.aidl.Book _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.bqt.aidl.Book.CREATOR.createFromParcel(data);//获取客户端输入的book对象
                } else {
                    _arg0 = null;
                }
                com.bqt.aidl.Book _result = this.addBookInout(_arg0);//【代理】
                reply.writeNoException();
                if ((_result != null)) {
                    reply.writeInt(1);
                    _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                } else {
                    reply.writeInt(0);
                }
    
                if ((_arg0 != null)) {
                    reply.writeInt(1);
                    _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);//同步_arg0
                } else {
                    reply.writeInt(0);
                }
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }

    2017-11-2

  • 相关阅读:
    nyoj_216_A problem is easy_201312051117
    nyoj_676_小明的求助_201312042142-2
    C# 堆和栈的区别?
    DataReader和DataSet区别
    SQLSERVER2008R2正确使用索引
    SQL Profiler工具简介
    (转)非常完善的Log4net详细说明
    SQL语句优化技术分析
    HashTable、HashSet和Dictionary的区别
    使用Nuget发布自己的类库包
  • 原文地址:https://www.cnblogs.com/baiqiantao/p/7770829.html
Copyright © 2011-2022 走看看