1 为什么要用AIDL?
Service的两种使用方式比较 startService和bindService
我在这篇博客中提到了 bindService的使用和优点, 结合代码具体看下
class MainActivity : AppCompatActivity(), View.OnClickListener {
// ...
private var mBinder: FirstService.FirstBinder? = null
private val mServiceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
// 这里拿到的是 FirstService.FirstBinder对象, 就可以直接调用其api方法获取对应Service提供的能力了
mBinder = (service as FirstService.FirstBinder)
}
override fun onServiceDisconnected(name: ComponentName) {
mBinder = null
}
}
// ...
private fun bindService() {
// 直接可以访问 FirstService.kt
val intent = Intent(this@MainActivity, FirstService::class.java)
bindService(intent, mServiceConnection, BIND_AUTO_CREATE)
}
}
这里的bindService只能使用同进程内服务提供的功能 (相当于bindService直接绑定FirstService.FirstBinder对象), 问题来了, 一个服务既然被设计成服务肯定不是只想给某个进程提供私有功能而是希望可以做到接口通用性,满足更多进程的调用使用,类似下面的示意图
于是这个痛点问题就转化为了如下:
- FirstService.FirstBinder 这个类可以不可以做成 Client 和 Server 共用的?
- FirstService.FirstBinder 必须要实现IBinder接口 这个实现过程太复杂了可以不可以简化?
为了解决IPC通信中, Client 和 Server 共用接口和简化实现IBinder接口的过程, Android提供了 AIDL
2 如何使用 AIDL?
AS版本: Android Studio Iguana | 2023.2.1 Patch 2
2.1 添加 buildFeatures.aidl = true 依赖 并 Sync Now
Client 和 Server App都需要添加 buildFeatures.aidl = true
2.2 Server 创建aidl文件 IFirstInterface.aidl 并 Make Module Server
IFirstInterface.aidl
interface IFirstInterface {
void sayHi(String message);
}
2.3 拷贝aidl文件
2.3.1 Client端新建一个package 必须要和 aidl文件包名一致!!!
这里就是使用server端的包名
2.3.2 将Server端的 IFirstInterface.aidl 拷贝到 Cient端 aidl文件夹 并 Make Module Server
2.4 Server 编写 Service类并给定权限
<application
...
<service
android:name=".SecondService"
android:enabled="true"
android:exported="true">
</service>
</application>
class SecondService : Service() {
private val mBinder = object : IFirstInterface.Stub() {
override fun sayHi(message: String?) {
Log.d("GerryLiang", "This is ServerApp sayHi!")
}
}
override fun onBind(p0: Intent?): IBinder? {
return mBinder
}
}
2.5 Client端 编写启动代码
class SecondActivity : AppCompatActivity() {
private var myService: IFirstInterface? = null
private var isServiceBound = false
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
myService = IFirstInterface.Stub.asInterface(service)
myService?.sayHi("")
isServiceBound = true
}
override fun onServiceDisconnected(name: ComponentName?) {
myService = null
isServiceBound = false
}
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.btn_play).apply {
setOnClickListener {
val pkg = "com.demo.media.session.service"
val intent = Intent().apply {
component = ComponentName(pkg, "$pkg.SecondService")
}
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
}
}
}
2.6 IPC成功
3 源码分析AIDL
查看AS帮我们生成的 IFirstInterface.java
package com.demo.media.session.service;
// Declare any non-default types here with import statements
public interface IFirstInterface extends android.os.IInterface
{
/** Default implementation for IFirstInterface. */
public static class Default implements com.demo.media.session.service.IFirstInterface
{
@Override public void sayHi(java.lang.String message) throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.demo.media.session.service.IFirstInterface
{
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.demo.media.session.service.IFirstInterface interface,
* generating a proxy if needed.
*/
public static com.demo.media.session.service.IFirstInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.demo.media.session.service.IFirstInterface))) {
return ((com.demo.media.session.service.IFirstInterface)iin);
}
return new com.demo.media.session.service.IFirstInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
data.enforceInterface(descriptor);
}
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
}
switch (code)
{
case TRANSACTION_sayHi:
{
java.lang.String _arg0;
_arg0 = data.readString();
this.sayHi(_arg0);
reply.writeNoException();
break;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
return true;
}
private static class Proxy implements com.demo.media.session.service.IFirstInterface
{
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 void sayHi(java.lang.String message) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(message);
boolean _status = mRemote.transact(Stub.TRANSACTION_sayHi, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_sayHi = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public static final java.lang.String DESCRIPTOR = "com.demo.media.session.service.IFirstInterface";
public void sayHi(java.lang.String message) throws android.os.RemoteException;
如上图所示, Server 的 Binder 和 Client 最终拿到的 Binder并不是同一个对象(因为并不在同一个进程)