Android AIDL注意点

Android AIDL注意点

如果AIDL Server 出现了崩溃 (不是AIDL调用的方法) 的情况下,Client可通过死亡代理(IBinder.DeathRecipient)注册监听,进行重新远程绑定Service

private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {

    @Override
    public void binderDied() {
        if (bookManager == null) {
            return;
        }
        bookManager.asBinder().unlinkToDeath(deathRecipient, 0);
        bookManager = null;
        //TODO:这里重写绑定远程Service
        bindMyBookService();
    }
};  

在ServiceConnection

private ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        bookManager = IBookManager.Stub.asInterface(iBinder);
        try {
            iBinder.linkToDeath(deathRecipient, 0);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        bookManager = null;
    }
};  

deathRecipient和在ServiceConnection的onServiceDisconnected中处理的效果相同,但deathRecipient运行在Binder连接池中,onServiceDisconnected运行在Client UI线程中

为空判断

如果AIDL Server没有安装,Client进行远程绑定的时候并不会报错。但是此时binder的实例是为null的,所以对于这个,要进行为空判断,如果为空,需进行重新绑定。

if (bookManager == null) {
    Toast.makeText(MainActivity.this, "aidl已断开", Toast.LENGTH_SHORT).show();
    return;
}  

可通过Binder的isBinderAlive判断Binder是否死亡。

手动导包

尽管Book类已经和IBookManager位于相同的包中,但是在IBookManager中仍然要导入Book类,这就是AIDL的特殊之处。

AIDL Client请求后会挂起

当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以如果一个远程方法是很耗时的,那么不能在UI线程中发起此远程请求。

AIDL Binder方法应采用同步的方式

由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现

AIDL文件不是实现Binder的必须品

我们完全可以不提供AIDL文件即可实现Binder,之所以提供AIDL文件,是为了方便系统为我们生成代码

AIDL支持的数据类型

  1. 基本数据类型(int、long、char、boolean、double等)
  2. String和CharSequence
  3. List:只支持ArrayList,里面的每个元素都必须能被AIDL支持
  4. Map:只支持HashMap,里面的每个元素都必须被AIDL支持,包括key和value
  5. Parcelable:所有实现了Parcelable接口的对象
  6. AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
自定义的Parcelable对象和AIDL对象必须要显式import进来  

如果AIDL文件中用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型。

Book.java

package com.ryg.chapter_2.aidl;
parcelable Book;  

除了基本数据类型,其他类型的参数必须标上方法:in、out或者input

in:输入型参数
out:输出型参数
inout:输入输出型参数

AIDL接口中只支持方法,不支持声明静态常量

为方便AIDL的开发,建议把所有和AIDL相关的类和文件放入一个lib中

如要使用,导入该lib即即可,同时,维护也方便。

AIDL的包结构在服务端和客户端要保持一致

否则运行会出错,这是因为客户端需要反序列化服务端中和AIDL接口相关的所有类,如果类的完整路径不一样的话,就无法成功反序列化,程序也就无法正常运行。

CopyOnWriteArrayList

支持并发读/写,AIDL方法是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问的情形

所以我们要在AIDL方法中处理线程同步,而这里我们直接使用CopeOnWriteArrayList来进行自动的线程同步  

类似的还有ConcurrentHashMap

如果某个远程方法是耗时的,那么就要避免客户端的UI线程中去访问远程方法

客户端调用远程服务的方法,被调用的方法运行在服务端的Binder线程池中,同时客户端线程被挂起,这个时候服务端方法若比较耗时,在UI线程执行该方法就可能导致ANR

异常处理

在Binder调用过程中,即使服务端抛出异常,服务端也不会挂 (服务端中系统默认是对异常进行了try异常捕获的处理,所以挂不了)。

IBookManager.aidl的实现类IBookManager.java

@Override
public void addBook(com.ethanco.aidlservice.bean.Book book) throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
       //.....
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}
  1. 抛出的异常有些能够传到客户端,比如NullPointException,这时客户端就会挂
  2. 但有些异常不能传到客户端,比如RuntimeException,这时,客户端也不会挂

如果Binder调用过程中,服务端的代码是创建了新的线程中抛出异常(在这个线程中没有进行异常捕获处理),服务端会挂,客户端不会挂。

权限检查

权限检查不能在服务端 Service的onBind方法内去检查

@Override
public IBinder onBind(Intent intent) {
    //check authority 在此处无效
}

因为在这里检查权限,是运行在服务端上的UI线程,不是Binder调用,检查的是服务端的权限。。。

如需进行权限检查,应在

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    //此处可进行权限检查
    String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
    String packageName = null;
    if (packages != null && packages.length > 0) {
        packageName = packages[0];
    }
    Log.i(TAG, "packageName:" + packageName);
    return super.onTransact(code, data, reply, flags);
}

onTrasact方法是运行在服务端的Binder线程池里,是Bind调用。如上面的代码,打印出的包名会是客户端的包名,即调用者的包名是客户端。

Binder连接池

将每个业务模块的Binder请求统一转发到远程Service中去执行,从而避免重复创建Service的过程

将所有的AIDL放在同一个Service中去管理。  

BinderPool能够极大提高AIDL的开发效率,并且可以避免大量的Service创建。因此,建议在AIDL开发时使用BinderPool机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Android AIDLAndroid Interface Definition Language)是一种用于定义客户端和服务之间通信接口的语言。AIDL 是一个 Android 特有的 RPC(远程过程调用)机制。 下面是使用 AIDL 的基本步骤: 1. 定义 AIDL 接口 在服务端创建一个 AIDL 文件,定义服务的接口方法。例如,创建一个名为 IMyService.aidl 的文件,其中包含以下内容: ``` interface IMyService { void sayHello(); } ``` 2. 实现 AIDL 接口 在服务端实现 AIDL 接口,例如: ``` public class MyService extends Service { private final IMyService.Stub binder = new IMyService.Stub() { @Override public void sayHello() throws RemoteException { Log.i("MyService", "Hello World"); } }; @Nullable @Override public IBinder onBind(Intent intent) { return binder; } } ``` 3. 绑定服务 在客户端绑定服务,例如: ``` private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IMyService myService = IMyService.Stub.asInterface(service); try { myService.sayHello(); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; private void bindService() { Intent intent = new Intent(this, MyService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE); } ``` 通过上述步骤,就可以实现客户端与服务端之间的通信。需要注意的是,AIDL 接口中定义的方法必须是可序列化的。如果方法参数或返回值类型不支持序列化,可以通过 Parcelable 接口实现序列化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

氦客

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值