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 ...
随机推荐
- 洛谷P4645 [COCI2006-2007 Contest#7] BICIKLI [Tarjan,拓扑排序]
题目传送门 BICIKLI 题意翻译 给定一个有向图,n个点,m条边.请问,1号点到2号点有多少条路径?如果有无限多条,输出inf,如果有限,输出答案模10^9的余数. 两点之间可能有重边,需要看成是 ...
- CSS 显示或隐藏子元素
很多时候我们仅仅只是想让鼠标移动入某个元素,然后显示出某个元素. 大多数博客的标题或内容都是:使用CSS实现鼠标悬停在一行上,显示某些元素 很遗憾,这是错误的,鼠标悬停后,尽管CSS标准中有定义此种方 ...
- IDEA导入eclipse项目并部署运行完整步骤(转发)
首先说明一下:idea里的project相当于eclipse里的workspace,而idea里的modules相当于eclipse里的project 1.File-->Import Proje ...
- CentOS7启动流程
同样我也找了一张CentOS7的启动流程图. 第一步.硬件启动阶段 这一步和CentOS6差不多,详细请看CentOS6启动流程(含详细流程图) 第二步.GRUB2引导阶段 从这一步开始,CentOS ...
- 20162327WJH实验四——图的实现与应用
20162327WJH实验四--图的实现与应用 实 验 报 告 课程:程序设计与数据结构 班级: 1623 姓名: 王旌含 学号:20162327 成绩: 指导教师:娄嘉鹏 王志强 实验日期:11月2 ...
- hdu 2732 最大流 **
题意:题目是说一个n*m的迷宫中,有每个格子有柱子.柱子高度为0~3,高度为0的柱子是不能站的(高度为0就是没有柱子)在一些有柱子的格子上有一些蜥蜴,一次最多跳距离d,相邻格子的距离是1,只要跳出迷宫 ...
- 持续集成之Jenkins插件使用(一)- 多个job之间的串并联
转载自:http://qa.blog.163.com/blog/static/190147002201391661510655/ Jenkins除了开源和免费,还有一个最吸引人的功能之一就是支持插件. ...
- 【原】使用Spring自带的JdbcTemplate。
使用Spring自带的JdbcTemplate,可以简化对数据库的操作,用起来十分方便.通过一下几个步骤的配置,即可以使用JdbcTemplate. (1)配置好Spring的数据源,加入mysql驱 ...
- js的栈与堆
JavaScript中基本数据类型和引用数据类型的区别 这是我引用别人的 觉得很好 1.基本数据类型和引用数据类型 ECMAScript包括两个不同类型的值:基本数据类型和引用数据类型. 基本 ...
- MySQL的mysql.sock文件作用(转)
mysql.sock是可用于本地服务器的套接字文件.它只是另一种连接机制. 不包含任何数据,但仅用于从客户端到本地服务器来进行交换数据.