感觉安卓的ipc还有有些门槛的,初学时无非是看网上的文章或者看开发艺术那本书上的demo。看书上的demo时作者直接就使用了’‘实现Parcelable接口的数据类型’’,非基本数据类型,初学时老是遇坑。网上的文章点赞多的好多也是仿写作者的demo。其他的文章可能写的不够系统,这就造成自己比着demo写老是出现bug,最终放弃了。。。经历过这种感受后,自己就决定趁着这次温习知识的过程来详细的总结下aidl写法。达到知新的效果,同时帮助大家快速入门这块进阶知识!!!!
一、基础
1、概念
aidl:android interface definition language即安卓接口定义语言,安卓ipc方式之一。
2、为啥设计这种语言
为了跨进程通信啊,这门语言就像一个桥梁吧两个app,确切的说应该是吧两个进程联系起来,使他们可以进行交流通信。
3、aidl语法
语法十分简单,和java几乎一样。
(1)文件后缀
我们平时写的代码文件为.java而aidl文件为.aidl文件和java文件一样studio/idea提供快速创建方式。
(2)支持数据的类型
- java的基本数据类型:byte,short,int,long,float,double,boolean,char
- String 类型。
- CharSequence类型
- List只支持ArrayList,list中的元素类型必须是aidl支持类型。
- Map只支持HashMap,map中的元素类型必须是aidl支持类型,包括key value。
- Parcelable:所有实现了Parcelable接口的类(具体使用参考下栗子)
- aidl接口本身:所有的aidl接口本身也可以在aidl文件中使用。
二、栗子:使用基本数据类型、不同app之间进行aidl
1、基本数据类型是常见的数据类型,使用起来坑少的多,并且service基础文章《Android四大组件—Service》中通过bindService方式开启的栗子也实践过,这里在之前的栗子上改写平滑深入更易使大家接受。
2、由于app的四大组件一般默认运行在主进程,所以两个app就是两个不同的进程啦!两个app之间传递一句信息就是简单的通信啦。
基于这两点分析我们就来个栗子:
1、回顾MusicService
使用aidl进行ipc时,工程的文件夹、很重要,这里就特别回顾下之前bindService方式栗子的包名文件夹。
2、换汤不换药操作:修改之前的接口MusicManager
1、其实安卓的组件和service通信模型就是C/S模型。比如《Android四大组件—Service》中activity中通过bindService方式开启MusicService,然后通过MusicManager接口调用方法和MusicService通信。我们可以理解为activity就是客户端,MusicService就是服务端。
2、使用aidl方式进行ipc时服务端我们还使用这个MusicService,但是MusicManager.java文件要变成MusicManager.aidl文件(具体操作如下图)
3、为啥变MusicManager.aidl???因为如果客户端还是使用MusicManager。这个类假如是MusicManager.java接口的话,我们如何在另一个app中使用它?根本没法使用?所以这里使用MusicManager.aidl,因为安卓系统使用bind对aidl进行了封装。另一个app中移植下aidl文件后两app就可以进行ipc了。
(1)通过编译器创建aidl文件
手动创建也行,只是下图生成的aidl文件夹、子文件夹(包名)、aidl文件都要我们手动创建,然后rebuild project。
如上图神奇的事情发生了,编译器自动帮我们在main文件夹下建立个aidl文件,然后创建同包名的文件夹(com.sunnyday.administrator.servicedemo.services)在这个文件夹下创建了个aidl文件
ps:可能会遇到如下坑,原因分析可能是gradle的buildToolsVersion "29.0.2"版本不兼容,吧这句删除即可。亲测屡试不爽。
com.android.ide.common.workers.WorkerExecutorException:
1 exception was raised by workers:
java.lang.RuntimeException:
java.lang.RuntimeException:
java.io.IOException:
com.android.ide.common.process.ProcessException:
Error while executing process
D:\androidevn\sdk\build-tools\29.0.0\aidl.exe with arguments
{-pD:\androidevn\sdk\platforms\android-29\framework.aidl
-oE:\AsProjects\servicedemo\app\build\generated\aidl_source_output_dir\debug\compileDebugAidl\out
-IE:\AsProjects\servicedemo\app\src\debug\aidl -IE:\AsProjects\MY\app\src\main\aidl
-IC:\Users\zb\.gradle\wrapper\dists\gradle-4.4-all\caches\transforms-2\files-2.1\4b6854fbce58e1d04f4d14eeade6ab12\aidl
-IC:\Users\zb\.gradle\wrapper\dists\gradle-4.4-all\caches\transforms-2\files-2.1\3702cc676a28c3d70a3a170f6be0425e\aidl
-dC:\Users\zb\AppData\Local\Temp\aidl4795291112600340041.d E:\AsProjects\servicedemo\app\src\main\aidl\com\example\mytest\PlayManager.aidl}
(2)简单修改MusicManager.aidl文件
删除无用代码,添加方法。rebuild project。
(3)修改MusicService
之前的接口不用啦,换成了aidl文件,MusicService内的MyBind类实现接口肯定会报错啦!!!这里也要修改啦!!!
可以看到,其实也就是让MyBind类继承了MusicManager.Stub类。其实Stub是系统帮我们生成的MusicManager的内部类。这里我们无需多管直接这样继承即可。
(4)服务端完工#运行app
至此,服务端就完工啦!我们只需运行app,等待其他的进程开启这个服务即可。
3、新建客户端app
新建个工程,我这里取名为ClientDemo
(1)代码
public class MainActivity extends AppCompatActivity {
MusicManager musicManager;
private ServiceConnection conn;
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//隐式启动
intent = new Intent();
intent.setAction("com.sunnyday.administrator.servicedemo.services.MusicService");
intent.setPackage("com.sunnyday.administrator.servicedemo");
conn = new MyServiceConnection();
}
// 绑定按钮
public void bindRemoteService(View view) {
bindService(intent, conn, BIND_AUTO_CREATE);
}
// 播放按钮
public void PlayMusic(View view) {
try {
if (null != musicManager) {
Log.i("233", "test: ");
musicManager.playMusic();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 获得实例
musicManager = MusicManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
如上开启方式很简单,隐式开启。我们先点击绑定按钮,再点击播放按钮log如下。
(2)注意点
1、Service android:exported=“true”,否则绑定失败
2、这里除了设置action之外还要设置下包名否则会报错( Service Intent must be explicit: Intent { act=com.example.mytest.MyService } 必须使用显示intent)
三、Aidl中使用非默认支持数据类型注意点
aidl 文件默认支持了常见的数据类型,如果我们想在aidl文件中使用自定义对象这时我们需要注意如下:
1、自定义对象实现序列化接口
2、自定义对象在aidl中导包栗子:类Book实现了paecelable接口,BookManager.aidl文件要使用Book类。这时我们需要另外创建Book.aidl文件然后在BookManager.aidl文件中paecelable关键字声明Book。这时BookManager.aidl才能使用Book类.
#bookmanager.aidl
如上在红线1出new的aidl文件,系统在main目录下帮我们建立了同包名的目录。然后建立aidl文件。这时我们直接使用Book类,然后build一下报错:
解决#建立Book同包名,同类名的Book.aidl
不是同类名吗?此处我们先写Bookl写Book会报错。我们先写Bookl生成aidl文件我们在refactor即可。
删除 interface 在使用parcelable 定义下Book类。
显式导包就可以使用了,build也不会报错了。