Android IPC之AIDL使用解析

来源:互联网 发布:seo推广渠道 编辑:程序博客网 时间:2024/05/17 07:13

在上一篇中介绍了通过Messenger实现IPC的具体方式,但是Messenger在实现IPC存在着一些局限性:

  1. Messenger只支持单线程通信,无法处理并发请求。
  2. Messenger只能用于进程间消息传递,在客户端无法调用服务端的方法。

正是由于Messenger的这两点局限性,我们需要使用AIDL来实现IPC。这里特别指出Messenger底层也是通过AIDL实现的。

AIDL简介

AIDL,Android接口定义语言,用于IPC。

  • 只有允许不同应用的客户端用IPC方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。
  • 如果您不需要执行跨越不同应用的并发IPC,就应该通过实现一个 Binder 创建接口;
  • 如果您想执行 IPC,但根本不需要处理多线程,则使用Messenger 类来实现接口。

AIDL实现IPC流程

AIDL实现流程

  1. 在服务端创建一个aidl接口,并定义方法。
  2. 在服务端创建一个Service,并创建AIDL对应的binder对象,将该对象作为service的onBind方法的返回值。
  3. 在客户端通过绑定服务获取binder对象,通过binder对象生成指定的接口对象。

这里需要注意:

  1. 在客户端绑定服务前,需要将服务端的AIDL相关的文件都copy到客户端中,并保持文件目录一致。因为在客户端需要反序列化服务端中和AIDL相关的类,如果路径不一致,则无法反序列化成功。所以这里建议大家把与AIDL相关的类文件都放在同一个包下。
  2. 在aidl接口中仅支持方法,而不能定义静态字段。
  3. 当我们使用自定义Parcelable对象时,那么必须创建一个和该对象的同名aidl文件。并且需要在aidl文件中通过import导入,即使自定义对象和aidl文件在同一个包下也需要显式导入。

AIDL支持的数据类型

  • Java 编程语言中的所有原语类型(如int、long、char、boolean 等等)
  • String和CharSequence
  • List,List中所有保存的类型必须是AIDL支持的类型,其中在服务端支持使用List作为通用类,但是在客户端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。
  • Map,Map中所有保存的类型必须是AIDL支持的类型,其中在服务端不支持Map作为通用类,但是在客户端实际接收的具体类始终是 HashMap,但生成的方法使用的是 Map 接口。

在aidl接口中,除了基本类型的参数,其他自定义类型的参数都要指定数据走向,有in、out 或 inout这三种类型,默认是in。

AIDL实例演示

服务端程序

创建一个aidl文件,名称为IBookManager并实现两个方法,代码如下:

package com.zhangke.aidlservicedemo;import com.zhangke.aidlservicedemo.Book;interface IBookManager {    List<Book> getBookList();    void addBook(in Book book);}

这里需要注意因为我们使用了自定义类型Book,所以需要创建一个名称同Book相同的aidl文件,只需要通过parcelable定义一下即可,代码如下:

package com.zhangke.aidlservicedemo;parcelable Book;

然后,创建一个Service用于将Aidl对应的binder对象返回到客户端,代码如下:

package com.zhangke.aidlservicedemo;public class BookManagerService extends Service {    public static final String TAG = "zhangke";    //支持并发读写的List    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }    private Binder mBinder = new IBookManager.Stub() {        @Override        public List<Book> getBookList() throws RemoteException {            Log.e(TAG, "query book list...");            return mBookList;        }        @Override        public void addBook(Book book) throws RemoteException {            Log.e(TAG, "add book ...");            mBookList.add(book);        }    };    @Override    public void onCreate() {        super.onCreate();        Log.e(TAG, "service onCreate");    }}

客户端程序

在客户端我们只需要通过绑定服务的方式获取到指定的aidl接口就能调用服务端的方法,代码如下:

package com.zhangke.aidlclientdemo;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import com.zhangke.aidlservicedemo.Book;import com.zhangke.aidlservicedemo.IBookManager;import com.zhangke.aidlservicedemo.IOnBookUpdateListener;import java.util.List;public class MainActivity extends AppCompatActivity {    public static final String TAG = "zhangke";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Intent intent = new Intent();        intent.setComponent(new ComponentName("com.zhangke.aidlservicedemo", "com.zhangke.aidlservicedemo.BookManagerService"));        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);    }    IBookManager mIBookManager = null;    ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            mIBookManager = IBookManager.Stub.asInterface(service);        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.e(TAG, "onServiceDisconnected");        }    };    @Override    protected void onDestroy() {        super.onDestroy();        unbindService(mConnection);    }    int bookId = 1;    public void add(View view) throws RemoteException {        Log.e(TAG, Thread.currentThread().toString());        mIBookManager.addBook(new Book(++bookId, "android#" + bookId));    }    public void query(View view) throws RemoteException {        List<Book> bookList = mIBookManager.getBookList();        for (Book book : bookList) {            Log.e(TAG, book.toString());        }    }}

效果如下:
这里写图片描述

我们通过点击添加Book和查询Book就能调用的方法,通过日志我们可以看到服务端方法调用成功。

02-13 03:22:30.079 6511-6522/com.zhangke.aidlservicedemo E/zhangke: add book ...02-13 03:22:31.280 6511-6523/com.zhangke.aidlservicedemo E/zhangke: query book list...02-13 03:22:31.280 6870-6870/com.zhangke.aidlclientdemo E/zhangke: [bookId: 2, bookName:book#2]02-13 03:22:31.280 6870-6870/com.zhangke.aidlclientdemo E/zhangke: [bookId: 3, bookName:book#3]02-13 03:22:31.280 6870-6870/com.zhangke.aidlclientdemo E/zhangke: [bookId: 4, bookName:book#4]

这样,整个通过aidl实现IPC的基本流程就结束了。

AIDL常见问题解析

  1. 客户端在远程调用服务端方法时,被调用的方法是运行在服务端的binder线程池中的,此时客户端会处于挂起状态,如果服务端方法为耗时方法,如果客户端是在UI线程中调用该远程方法,可能会导致ANR。

Binder异常终止

在通过binder调用远程服务的方法时,有可能会出现服务端程序异常终止的情况,这样客户端的方法就不能正常被调用,这个时候我们需要通过重新连接服务来保证程序的健壮性。

重新连接服务有两种方式:

  • 通过给binder设置DeathRecipient,在binder死亡时,DeathRecipient的binderDied方法会被调用;
  • 通过ServiceConnection的onServiceDisconnected方法监听服务的异常终端,然后在该方法中设置重连。

代码如下:

ServiceConnection mConnection = new ServiceConnection() {    @Override    public void onServiceConnected(ComponentName name, IBinder service) {        mIBookManager = IBookManager.Stub.asInterface(service);        try {            //1、通过deathRecipient方式监听异常中断            service.linkToDeath(deathRecipient, 0);        } catch (RemoteException e) {            e.printStackTrace();        }    }    @Override    public void onServiceDisconnected(ComponentName name) {        Log.e(TAG, "onServiceDisconnected");        mIBookManager = null;        //2、设置重连        linkServer();    }};final IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {    @Override    public void binderDied() {        Log.e(TAG, "binder death");        //1、设置重连        linkServer();    }};public void linkServer(){    Intent intent = new Intent();    intent.setComponent(new ComponentName("com.zhangke.aidlservicedemo", "com.zhangke.aidlservicedemo.BookManagerService"));    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}

项目代码

0 0
原创粉丝点击