Android AIDL--进程间通信
一 AIDL 是什么
AIDL(Android 接口定义语言) 是 Android 提供的一种进程间通信 (IPC) 机制。
我们可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。
在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。
编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。
通过这种机制,我们只需要写好 aidl 接口文件,编译时系统会帮我们生成 Binder 接口。
二 AIDL 支持的数据类型
共 4 种:
- Java 的基本数据类型
- List 和 Map
- 元素必须是 AIDL 支持的数据类型
- Server 端具体的类里则必须是 ArrayList 或者 HashMap
- 其他 AIDL 生成的接口
- 实现 Parcelable 的实体
三 AIDL 编写步骤
AIDL 的编写主要为以下三部分:
- 创建 AIDL
- 创建要操作的实体类,实现
Parcelable
接口,以便序列化/反序列化 - 新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射 aidl 文件
- Make project ,生成 Binder 的 Java 文件
- 创建要操作的实体类,实现
- 服务端
- 创建 Service,在其中创建上面生成的 Binder 对象实例,实现接口定义的方法
- 在
onBind()
中返回
- 客户端
- 实现
ServiceConnection
接口,在其中拿到 AIDL 类 bindService()
- 调用 AIDL 类中定义好的操作请求
- 实现
四 案例
项目结构图:
1.创建AIDL
1.1创建要操作的实体类,实现 Parcelable
接口,以便序列化/反序列化
package net.shunzhi.aidldemo1.bean; import android.os.Parcel; import android.os.Parcelable; /** * Created by Administrator on 2018/3/9 0009. */ public class Person implements Parcelable { private String mName; public Person (String name){ this.mName=name; } protected Person(Parcel source){ this.mName=source.readString(); } public static final 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 int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mName); } @Override public String toString() { return "Person{" + "mName='" + mName + '\'' + '}'; } }
1.2新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射 aidl 文件
1.鼠标移到项目上面去,点击右键,然后 new->AIDL->AIDL File,按下鼠标左键就会弹出一个框提示生成AIDL文件(IMyAIdlInterface)了
自动生成如图:
2.编写接口IMyAdilInterface.aidl
// IMyAidlInterface.aidl //第二类AIDL文件 //作用是定义方法接口 package net.shunzhi.aidldemo1; ////导入所需要使用的非默认支持数据类型的包 import net.shunzhi.aidldemo1.bean.Person; interface IMyAidlInterface { //传参时除了Java基本类型以及String,CharSequence之外的类型 //都需要在前面加上定向tag,具体加什么量需而定:in(输入), out(输出), inout(输入输出) void addPerson(in Person person); //所有的返回值前都不需要加任何东西,不管是什么数据类型 List<Person> getAllPersons(); }
在接口 aidl 文件中定义将来要在跨进程进行的操作,上面的接口中定义了两个操作:
- addPerson: 添加 Person
- getPersonList:获取 Person 列表
需要注意的是:
- 非基本类型的数据需要导入,比如上面的 Person,需要导入它的全路径。
- 这里的 Person 我理解的是 Person.aidl,然后通过 Person.aidl 又找到真正的实体 Person 类。
- 方法参数中,除了基本数据类型,其他类型的参数都需要标上方向类型
- in(输入), out(输出), inout(输入输出)
3.创建实体类Person.aidl
//第一类AIDL文件 //这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用 //注意:Person.aidl与Person.java的包名应当是一样的 package net.shunzhi.aidldemo1.bean; //parcelable 为小写 parcelable Person;
2.服务端
2.1创建 Service,在其中创建上面生成的 Binder 对象实例,实现接口定义的方法;然后在 onBind()
中返回
package net.shunzhi.aidldemo1; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.support.annotation.Nullable; import net.shunzhi.aidldemo1.bean.Person; import java.util.ArrayList; import java.util.List; /** * Created by Administrator on 2018/3/9 0009. */ public class MyAidlService extends Service { private String TAG="MyAidlService"; private List<Person> list; /** * 创建生成的本地 Binder 对象,实现 AIDL 制定的方法 */ private IBinder iBinder=new IMyAidlInterface.Stub() { @Override public void addPerson(Person person) throws RemoteException { list.add(person); } @Override public List<Person> getAllPersons() throws RemoteException { return list; } }; /** * 客户端与服务端绑定时的回调,返回 mIBinder 后客户端就可以通过它远程调用服务端的方法,即实现了通讯 * @param intent * @return */ @Nullable @Override public IBinder onBind(Intent intent) { list=new ArrayList<>(); return iBinder; } }
2.2在 Manifest 文件中声明
<service android:name=".MyAidlService" android:enabled="true" android:exported="true" android:process=":aidl" />
3.客户端
3.1实现 ServiceConnection
接口,在其中拿到 AIDL 类
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//连接后拿到 Binder,转换成 AIDL,在不同进程会返回个代理
aidl=IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
aidl=null;
}
};
在 Activity 中创建一个服务连接对象,在其中调用 IMyAidl.Stub.asInterface()
方法将 Binder 转为 AIDL 类
3.2接着绑定服务
Intent intent=new Intent(MainActivity.this,MyAidlService.class); bindService(intent,connection,BIND_AUTO_CREATE);
3.3拿到 AIDL 类后,就可以调用 AIDL 类中定义好的操作,进行跨进程请求
private void addPerson(){ Random random=new Random(); Person p=new Person("fhasdfh"+random.nextInt(10) ); try { aidl.addPerson(p); tv.setText(aidl.getAllPersons().toString()); } catch (RemoteException e) { e.printStackTrace(); } }
整体代码:
package net.shunzhi.aidldemo1; import android.content.ComponentName; 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.view.View; import android.widget.Button; import android.widget.TextView; import net.shunzhi.aidldemo1.bean.Person; import java.util.Random; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private TextView tv; private Button bt1,bt2; private IMyAidlInterface aidl; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { tv=findViewById(R.id.tv); bt1=findViewById(R.id.bt1); bt2=findViewById(R.id.bt2); bt1.setOnClickListener(this); bt2.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.bt1://绑定服务 Intent intent=new Intent(MainActivity.this,MyAidlService.class); bindService(intent,connection,BIND_AUTO_CREATE); break; case R.id.bt2: addPerson(); break; } } //拿到 AIDL 类后,就可以调用 AIDL 类中定义好的操作,进行跨进程请求 private void addPerson(){ Random random=new Random(); Person p=new Person("fhasdfh"+random.nextInt(10) ); try { aidl.addPerson(p); tv.setText(aidl.getAllPersons().toString()); } catch (RemoteException e) { e.printStackTrace(); } } private ServiceConnection connection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //连接后拿到 Binder,转换成 AIDL,在不同进程会返回个代理 aidl=IMyAidlInterface.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { aidl=null; } }; }
效果图: