Android IPC机制(二):AIDL的基本使用方法

一、前言

上一篇博客,讲述了实现序列化和反序列化的基本方式,是实现进程间通讯的必要条件,而这篇博客主要来讲一讲AIDL,通过展示AIDL的基本使用方法来引出IPC的核心:Binder。


二、什么是AIDL?

AIDL全称:Android Interface Definition Language,即Android接口定义语言。由于不同的进程不能共享内存,所以为了解决进程间通讯的问题,Android使用一种接口定义语言来公开服务的接口,本质上,AIDL非常像一个接口,通过公开接口,让别的进程调用该接口,从而实现进程间的通讯。


三、使用AIDL

以下结合一个具体实例来示范AIDL的使用方法。


1、建立.aidl文件

为了方便AIDL的开发,建议把所有和AIDL相关的类和文件放入同一个包中,这样方便把整个包复制,以便其他模块或者应用需要用到同一个AIDL。在Android Studio下,专门为AIDL文件创建了一个文件夹,方便我们的管理:

                                           

可以看到,笔者新建了一个service模块,该模块在manifests的声明如下:

<service android:name=".MyAidlService"
            android:enabled="true"
            android:exported="true"></service>
这个模块为app模块提供服务,与app模块处于不同进程,所以模拟了进程间通讯的场景。在service模块,新建一个AIDL文件夹,然后新建一个包,这里包名为com.chenyu.service,然后新建AIDL文件:IMyAidl.aidl:

// IMyAidl.aidl
package com.chenyu.service;

// Declare any non-default types here with import statements
import com.chenyu.service.Person;
interface IMyAidl {
    void addPerson(in Person person);
    List<Person> getPersonList();
}
这与定义一个接口的语法基本相同,都是以Interface为关键字定义。里面声明了两个方法,分别是addPerson(in Person person)与getPersonList()。AIDL中除了基本数据类型,其他类型的参数必须标上方向,in、out、或者inout,in表示输入型参数,out表示输出型参数,inout表示输入输出型参数,我们要根据需要实际指定参数类型,因为底层的数据处理开销非常大,如果不指定类型,编译将会无法通过。

AIDL支持的数据类型:①基本数据类型(int,long,char,boolean,double)②string和CharSequence,③List:只支持ArrayList,以及里面所有的元素必须能够被AIDL支持 ④Map:只支持HashMap,以及里面所有的元素必须能够被AIDL支持  ⑤Parcelable:所有实现了Parcelable接口的对象 ⑥AIDL:所有AIDL接口本身也可以在AIDL文件中使用。

注意一下:这里使用了自定义的Parcelable对象:Person类,但是AIDL不认识这个类,所以我们要创建一个与Person类同名的AIDL文件:Person.aidl

// IMyAidl.aidl
package com.chenyu.service;
parcelable Person;
只有这样,IMyAidl.aidl才能知道其中的Person是使用了Parcelable接口的类,注意,Person类的包名与Person.aidl的包名一定要相同,即无论其他应用或者其他模块,只要有AIDL,都应该保证AIDL的所有包结构一致,才能保证顺利进行IPC通讯,减少不必要的麻烦。


2、Person类,实现Parcelable接口

在java文件夹中,创建com.chenyu.service包,这样就与上面的是相同包名了。

package com.chenyu.service;

import android.os.Parcel;
import android.os.Parcelable;


public class Person implements Parcelable {
    private String name;
    private int age;
    private int number;

    public Person(Parcel source) {
        this.name=source.readString();
        this.age=source.readInt();
        this.number=source.readInt();
    }

    //getter、setter method
    //...
    public Person(int age, String name, int number) {
        this.age = age;
        this.name = name;
        this.number = number;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
        dest.writeInt(number);
    }
    public static final Parcelable.Creator<Person> CREATOR=new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel source) {
            return new Person(source);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", number=" + number +
                '}';
    }
}
对于Parcelable接口的详细解析,可参考上一篇博客,这里不再赘述。

3、实现服务端

上面我们定义了一个AIDL接口,接下来要做的是实现这个AIDL接口,在java/com.chenyu.service中,创建MyAidlService.java文件:

package com.chenyu.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class MyAidlService extends Service {
    private ArrayList<Person> persons;
    @Override
    public IBinder onBind(Intent intent) {
        persons=new ArrayList<Person>();
        Log.d("cy", "success bind");
        return iBinder;
    }
    private IBinder iBinder= new IMyAidl.Stub() {
        @Override
        public void addPerson(Person person) throws RemoteException {
            persons.add(person);
        }

        @Override
        public List<Person> getPersonList() throws RemoteException {
            return persons;
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("cy", "onCreate ");
    }
}
我们来看一下服务端是如何实现接口的:

(1)为了实现来自.aidl文件生成的接口,需要继承Binder接口(例如Ibinder接口),并且实现从.aidl文件中继承的方法,在上面代码中,使用匿名实例实现一个叫IMyAidl(定义在IMyAidl.aidl中)的接口,实现了两个方法,addPerson和getPersonList.

(2)onBind方法:该方法在客户端与服务端连接的时候回调,实现客户端和服务端的绑定,并返回一个Binder实例,这里返回的是iBinder,而IBinder是(1)中实现了接口的匿名实例,即客户端拿到的实际上实现了接口的一个实例,这样,客户端通过Binder就与服务端建立了连接,客户端通过Binder远程调用服务端的实例方法,这样也即实现了进程间通讯。

4、实现客户端

在实现客户端之前,先确保把aidl的包复制过来,就相上面笔者所给出的结构图一样,包括Person类也应该复制过来。显示界面比较简单,就不贴出来了,主要看Activity的代码:

package com.chenyu.myaidl;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.chenyu.service.IMyAidl;
import com.chenyu.service.Person;

import java.util.List;

public class MainActivity extends Activity implements View.OnClickListener {

    private Button btn;
    IMyAidl iMyAidl;
    private ServiceConnection conn=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d("cylog", "onServiceConnected success");
            iMyAidl=IMyAidl.Stub.asInterface(service);<span style="white-space:pre">	</span>//①

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d("cylog", "onServicedisConnected ");
            iMyAidl=null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        bindService();
    }

    private void initView() {
        btn= (Button) findViewById(R.id.cal);
        btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        try {
            iMyAidl.addPerson(new Person(21, "陈育", 22255));
            List<Person> persons = iMyAidl.getPersonList();  //②
            Log.d("cylog",persons.toString());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private void bindService() {
        Intent intent=new Intent();
        intent.setComponent(new ComponentName("com.chenyu.service","com.chenyu.service.MyAidlService"));
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}
与绑定一般Service的语法差不多,在安卓5.0之后,必须显式指定Service的包名和类名即:

private void bindService() {
        Intent intent=new Intent();
        intent.setComponent(new ComponentName("com.chenyu.service","com.chenyu.service.MyAidlService"));
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }
在bindService(intent,conn,Context.BIND_AUTO_CREATE)方法中有几个参数需要说明一下,

①conn:该参数代表了与服务端的连接,即ServiceConnection.

②Context.BIND_AUTO_CREATE:该参数表示绑定的同时创建一个Service。
在发出绑定成功之后,会回调①处的代码,此时,可在回调方法onServiceConnected()方法中,获取服务器返回的IMyAidl实例,在客户端拿到该实例之后,就可以通过调用相应的方法进行远程通讯了,比如上述的②处代码。

最后,看一下运行结果,先运行service,然后运行app:



可以看出,的确是构成了进程间通讯,并且完成了进程间通讯。

以上是利用AIDL实现进程通讯的基本方法,希望对大家有所帮助。关于AIDL的核心原理以及Binder,AIDL注意事项,会在下一篇博客更新。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值