最近学习Android跨进程通信,使用到AIDL,参考着开发艺术探索,但是实践过程中也遇到一些问题,特记下流程和常见问题,使用工具Android Studio
1.服务端编写AIDL文件
点击new,创建AIDL接口文件
生成了IMyAidlInterface.aidl文件,文件名是可以自己修改的
package com.example.android_7_test;
// Declare any non-default types here with import statements
import com.example.android_7_test.Book;
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
* 在接口文件中定义 服务端提供的方法 供客户端调用 ,下面的setBook是我测试的方法
*
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
/**
1.定义的方法不能有修饰符
2.不能有方法体
3.除了基本数据类型,要加上方向参数 in 输入性(作为参数传入) out 输出型(返回值) inout
*/
boolean setBook(in Book book);
}
这里面有一个测试用的实体类Book(实体类要实现parcelable接口),同时在相同包下创建Book.aidl,自定义实体类都需要创建同名的aidl文件,可以点击new -》file -》 相应的文件名,后缀为.aidl,参考上方的图
package com.example.android_7_test;
/**
注明 parcelable + 自定义类名
*/
parcelable Book;
Note: 这三个类的包名必须一致,有可能自动生成的包名带有.aidl.xxx,有可能出错,可以都去掉,而且在IMyAidlInterface接口中要明确引入 相关实体类,参考上方的代码
实体类不一定要和这两个aidl文件放在同一个包下,但是为了客户端方便移植,建议放同一个包下,但是这样会造成java目录下的类无法访问这个实体类,会出现无法找到类引用,这时候需要在build.gradle添加配置
android{
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}
}
2.同步项目,生成AIDL的java文件
点击同步项目,完成后在Project目录结构下的build-》source-》aidl下可以看到生成的java文件
3.服务端实现接口
1.实现接口
2.重写onBind,返回binder对象
/**
这个是kotlin代码,可以自己改成java代码
*/
class MyService : Service() {
//实现接口,这里实现的是接口的内部stub类
val interfa = object : IMyAidlInterface.Stub() {
override fun setBook(book: Book?): Boolean {
//实现定义的方法,这里是发送一个toast
Handler(Looper.getMainLooper()).post({ Toast.makeText(applicationContext, "收到设置", Toast.LENGTH_SHORT).show() })
return true
}
override fun basicTypes(anInt: Int, aLong: Long, aBoolean: Boolean, aFloat: Float, aDouble: Double, aString: String?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
override fun onBind(intent: Intent): IBinder {
//返回binder对象,不返回的话绑定服务无法成功
return interfa.asBinder()
}
}
4.在Manifest文件注册Service
<service
android:name=".MyService"
android:process=":remote"
android:exported="true"
>service>
5.客户端复制创建的文件到相同包下
可以和服务端一样,创建aidl文件,生成aidl目录,但是这样生成的包名会不一致,可以在该目录下新建包,和服务端文件包名一致
①同样,这样java目录下的类文件也无法访问实体类Book,还是要在build.gradle中进行上述的配置!!!
②包名必须和服务端的包名一致!!!,否则会出现Binder invocation to an incorrect interface
6.客户端绑定启动service,调用相关方法
/**
获取返回的binder对象
*/
IMyAidlInterface iMyAidlInterface = null;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Intent intent = new Intent();
/**
跨进程,无法使用Intent(this,xx.class)方法,可以使用下面的,通过包名和完整路径类名创建Intent
*/
intent.setClassName("com.example.android_7_test","com.example.android_7_test.MyService");
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
后面就可以用IMyAidlInterface对象调用服务端的定义的方法实现通信,需要注意上面提到的容易出错的点。
感谢阅读,如有错误,欢迎指正。