轻松搞定AIDL
来源:互联网 发布:万网域名增加二级 编辑:程序博客网 时间:2024/06/01 09:27
前言
为了防止遗忘这些知识点,写一篇博客加深自己的理解,方便忘记后再重新学习。
概述
AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言。
AIDL是用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。线程间通讯有多种方式,下面简单介绍下不同之间区别。
多种线程间通讯方式的不同:详情参考此博客
- Bundle:四大组件间的进程间通信方式,简单易用,但传输的数据类型受限。
- 文件共享: 不适合高并发场景,并且无法做到进程间的及时通信。
- Messenger: 数据通过Message传输,只能传输Bundle支持的类型
- ContentProvider:android 系统提供的。简单易用。但使用受限,只能根据特定规则访问数据。
- AIDL:功能强大,支持实时通信,但使用稍微复杂。
- Socket:网络数据交换的常用方式。不推荐使用。
关于AIDL语法
aidl的语法基本和java一样,仅有几点不同之处:
- 文件类型:AIDL文件的后缀是 .aidl,而不是 .java。
- 数据类型:AIDL默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包,就算目标文件与当前正在编写的 .aidl 文件在同一个包下。(列:编写了两个文件,一个叫做 person.java ,另一个叫做PersonManager.aidl,它们都在 com.mumu.aidl包下 ,在 .aidl 文件里使用person对象我们就必须在 .aidl 文件里面写上 import com.mumu.aidl.person; )
- 默认支持的数据类型包括:
- Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。(实测short不支持)
- String 类型。
- CharSequence类型。
- List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。
- Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。
- parcelable序列化的数据类型:parcelable所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口;
- 定向tag:AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。Java 中的基本类型和 String ,CharSequence 的定向 tag 默认且只能是 in 。注意,不要全都用 inout ,工程大了系统的开销就会大很多,排列整理参数的开销很大。详细Tag使用方式参考此博客
dome实例(使用的studio工具)
简单实现基本数据类型(如图一个远程相加运算)
服务端
- 创建一个新的项目作为服务端
- 创建服务器端的aidl包
- 创建的aidl包下aidl文件
- 实现aidl文件接口
- 创建服务器端的aidl包
package ready.mumu.service;interface MyAidl { int addnum(int num1 , int num2);}
- 在java包下创建一个service并实现aidl接口
public class Myservice extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return iBinder; } private final MyAidl.Stub iBinder = new MyAidl.Stub(){ @Override public int addnum(int num1, int num2) throws RemoteException { Log.v("MUMU","收到输入的远程请求,收到的值是num1:"+num1+" num2:"+num2); return num1 + num2; }}
- 注册表注册service
<service android:name=".Myservice" android:process=":remote"> <intent-filter> <action android:name="ready.mumu.service.MyAidl"/> </intent-filter> </service>
这里说一下Android声明文件中的android:process属性可以为任意组件包括应用指定进程,如果我们需要让一个服务在一个远端进程中运行(而不是标准的它所在的apk的进程中运行),我们可以在声明文件中这个服务的标签中通过android:process属性为其指定一个进程。
“:remote”又是什么意思呢?“remote”不是关键,这个完全可以自己随意取名字,“:”冒号才是关键。
进程名以“:”开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。而进程名不以“:”开头的进程属于全局进程,其他应用可以通过某些方式和它跑在同一个进程中。
客户端
- 将服务端对应的aidl拷贝到客户端,要求aidl完全一致,所在的包名也完全一致
- 创建客户端的activity运行界面,并实现按钮点击事件(xml布局文件就不写了,简单的三个输入框一个按钮)
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initUI(); //软件启动就绑定服务 bindService(); } private void initUI() { et_num1 = (EditText) findViewById(R.id.et_num1); et_num2 = (EditText) findViewById(R.id.et_num2); et_res = (EditText) findViewById(R.id.et_res); bt_add = (Button) findViewById(R.id.bt_add); bt_add.setOnClickListener(this); }
- 实现绑定服务方法
private void bindService() { //获取到服务端 Intent intent = new Intent(); //5.0之后必须显示intent启动 绑定服务 , ComponentName两个参数对应是服务包名和服务文件名(文件名必须是包名+文件名) intent.setComponent(new ComponentName("ready.mumu.service","ready.mumu.service.Myservice")); bindService(intent,conn, Context.BIND_AUTO_CREATE); }
- 实现ServiceConnection(conn)绑定回调
MyAidl myaidl;private ServiceConnection conn = new ServiceConnection() { //绑定上服务的时候执行 @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { //拿到远程的服务 myaidl = MyAidl.Stub.asInterface(iBinder); } //当服务断开的时候执行 @Override public void onServiceDisconnected(ComponentName componentName) { //回收资源 myaidl = null; } };
- 实现“远程计算”按钮onclick方法
@Override public void onClick(View view) { if(view == bt_add){ int num1 = Integer.parseInt(et_num1.getText().toString()); int num2 = Integer.parseInt(et_num2.getText().toString()); try { //调用远程服务 int res = myaidl.addnum(num1 , num2); et_res.setText(res+""); } catch (RemoteException e) { e.printStackTrace(); et_res.setText("报错了"); } }
- 在activity销毁的onDestroy方法中解绑服务
@Override protected void onDestroy() { super.onDestroy(); unbindService(conn); }
如此简单的小dome就完成了。
相对复杂的序列化实现dome(如图)
这里在上边dome的基础上又添加了一个“序列化调用”按钮,点击之后输入传入的自定义的序列化数据,下面的步骤是在上个dome基础之上添加的。
服务端
方便看结构,先来一张服务端的代码结构图
- 创建一个java类myParcelable,定义数据类型、构造方法、get/set方法,实现Parcelable序列化(这个类的包名和aidl的包名要一致)
public class myParcelable implements Parcelable{ String name; int age; String sex; //参数是一个Parcel,用它来存储与传输数据 protected myParcelable(Parcel in) { //注意,此处的读值顺序应当是和writeToParcel()方法中一致的 name = in.readString(); age = in.readInt(); sex = in.readString(); } public static final Creator<myParcelable> CREATOR = new Creator<myParcelable>() { @Override public myParcelable createFromParcel(Parcel in) { return new myParcelable(in); } @Override public myParcelable[] newArray(int size) { return new myParcelable[size]; } }; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { //数据存储至Parcel dest.writeString(name); dest.writeInt(age); dest.writeString(sex); } //方便数据清晰 @Override public String toString() { return "myParcelable{" + "name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + '}'; }}
- 在aidl包下创建一个aidl文件(myParcelable.aidl)用于定义parcelable对象(非默认支持数据类型必须通过AIDL文件定义才能被使用)。这个myParcelable.aidl和myParcelable.java的包名要一致,所以上个类创建时才说要与aidl包名一致。
// myParcelable.aidlpackage ready.mumu.service; parcelable myParcelable;
- 在原来的MyAidl.aidl中添加一个readText方法
// MyAidl.aidlpackage ready.mumu.service;//注意需要导入包import ready.mumu.service.myParcelable;interface MyAidl { int addnum(int num1 , int num2); //传参时除了Java基本类型以及String,CharSequence之外的类型 //都需要在前面加上定向tag,具体加什么量需而定 String readText(in myParcelable par);}
客户端
结构附图
- 将服务端的myParcelable.java和myParcelable.aidl拷贝过来,注意包名一致
- 将服务端的MyAidl.ail复制到客户端,此文件要保持客户端和服务端一致。
- 实现activity中“序列化调用”按钮onclick方法
if(view == bt_par){ try { String msg = myaidl.readText(new myParcelable("张三",18,"男")); et_res.setText(msg); } catch (RemoteException e) { e.printStackTrace(); et_res.setText("序列化出错了"); } }
如此,序列化数据的远程调用也就结束了。
结语
可能看起来会稍微混乱一点,实现一下就会发现其实aidl使用还是很简单的,最后留下dome地址,看dome可能会相对更容易理解一些。dome下载地址
- 轻松搞定AIDL
- 轻松搞定vmware + win2003Cluster
- 轻松搞定Eclipse配置
- 轻松搞定eclipse配置
- FedoraCore 11 轻松搞定
- IIS 500, 轻松搞定!
- 轻松搞定dataguard
- 轻松搞定dataguard
- 《轻松搞定XML》
- 轻松搞定java高薪
- 轻松搞定java高薪
- 轻松搞定KMP算法
- 轻松搞定二叉树
- 轻松搞定素数
- 轻松搞定Linux常用命令
- 轻松搞定EasyUI
- 轻松搞定Linux环境变量
- 轻松搞定GitHub Pages
- 设计原则之开放-封闭原则 javascript
- 调试技巧之调用堆栈 - Call stack
- 获得应用需要的所有权限并查看是否授权
- Jquery 获取复选框的值
- Android AndroidUtilCode-进程相关工具类
- 轻松搞定AIDL
- MySQL数据库(入门资料文档下载分享)
- javascript的正则表达式
- codevs 1332 上白泽慧音(Tarjan)
- C++里的花括号{},块,作用域
- 归并排序求逆序对
- Boost学习之可移植路径操作--filesystem
- i.MX6ULL -- 架构图
- LeetCode 436. Find Right Interval 题解(C++)