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
AIDL 定向tag IPC 案例 MD的更多相关文章
- 服务 AIDL 定向tag IPC Parcelable 案例 MD
		
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
 - RxJava RxPermissions 动态权限 简介 原理 案例 MD
		
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
 - LruCache DiskLruCache 缓存 简介 案例 MD
		
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
 - - 反编译 AndroidKiller 逆向 实践案例 MD
		
目录 目录 反编译 AndroidKiller 逆向 实践案例 MD AndroidKiller 简介 插件升级 基本使用 实践案例 修改清单文件 打印 debug 级别的日志 方式一:直接代理 Lo ...
 - AS 自定义 Gradle plugin 插件 案例 MD
		
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
 - AIDL实现Android IPC
		
1.AIDL文本解释 在软件工程中,接口定义语言(IDL)已经成为通用术语,是用来描述软件组件接口的特定语言.在Android中,该IDL被称为Android接口定义语言(AIDL),它是纯文本文件, ...
 - Xposed 框架 hook 简介 原理 案例 MD
		
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
 - IO流 简介 总结 API 案例 MD
		
目录 IO 流 简介 关闭流的正确方式 关闭流的封装方法 InputStream 转 String 的方式 转换流 InputStreamReader OutputStreamWriter 测试代码 ...
 - protobuf Protocol Buffers 简介 案例 MD
		
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
 
随机推荐
- CSUOJ 2031 Barareh on Fire
			
Description The Barareh village is on fire due to the attack of the virtual enemy. Several places ar ...
 - awk 基本函数用法
			
gsub函数有点类似于sed查找和替换.它允许替换一个字符串或字符为另一个字符串或字符,并以正则表达式的形式执行.第一个函数作用于记录$0,第二个gsub函数允许指定目标,然而,如果未指定目标,缺省为 ...
 - win10怎么修改svn的用户和密码
			
win10怎么修改svn的用户和密码(一般为默认),其他的系统也差不多 方法/步骤 1.方法一: 1.双击我的电脑在c盘找到auth文件夹 C:\Users\系统帐户名\AppData\Roaming ...
 - 1024 Palindromic Number (25)(25 point(s))
			
problem A number that will be the same when it is written forwards or backwards is known as a Palind ...
 - BZOJ.3105.[CQOI2013]新Nim游戏(线性基 贪心 博弈论)
			
题目链接 如果后手想要胜利,那么在后手第一次取完石子后 可以使石子数异或和为0.那所有数异或和为0的线性基长啥样呢,不知道.. 往前想,后手可以取走某些石子使得剩下石子异或和为0,那不就是存在异或和为 ...
 - 【推导】【数学期望】【冒泡排序】Petrozavodsk Winter Training Camp 2018 Day 5: Grand Prix of Korea, Sunday, February 4, 2018 Problem C. Earthquake
			
题意:两地之间有n条不相交路径,第i条路径由a[i]座桥组成,每座桥有一个损坏概率,让你确定一个对所有桥的检测顺序,使得检测所需的总期望次数最小. 首先,显然检测的时候,是一条路径一条路径地检测,跳跃 ...
 - Codeforces Round #279 (Div. 2) B - Queue 水题
			
#include<iostream> #include<mem.h> using namespace std; ],q[]; int main() { int n,x,y; m ...
 - hibernate核心配置
			
# hibernate核心配置 注意: - hibernate.cfg.xml默认放在src目录下(这样可以自动加载该文件) - 必须配置的参数: * 数据库的四大参数和方言 - 可选配置的参 ...
 - 【转】Spring中事务与aop的先后顺序问题
			
[原文链接] http://my.oschina.net/HuifengWang/blog/304188 [正文] Spring中的事务是通过aop来实现的,当我们自己写aop拦截的时候,会遇到跟sp ...
 - Tomcat启动异常 java.net.BindException: Cannot assign requested address: JVM_Bind
			
从Apache官网下载的tomcat7,在MyEclipse中启动时抛出如下异常: 严重: StandardServer.await: create[localhost:8005]: java.net ...