aidl-Android
aidl-Android
https://blog.csdn.net/u010775335/article/details/105541176
https://www.jb51.net/article/183013.htm
https://www.imooc.com/video/11195
https://blog.csdn.net/jone_net/article/details/102702544
定义:Android接口定义语言
功能:通过AIDL接口在进程间传递数据(进程/APP之间通信)
Android中每个应用程序独立拥有一个虚拟机,所以应用程序之间不能直接通信。
跨进程的数据通信是通过Android系统底层实现的
注意:
- aidl毕竟是跨进程的,比较耗资源.
AIDL支持数据类型:
-
Java 的原生类型(
int
,long
,boolen
,float
,double
,String
,byte
,char
)short 不支持
-
String 和CharSequence
-
List 和 Map
- list的返回必须专为
ArrayList
才能接收
List和Map 对象的元素必须是AIDL支持的数据类型; 以上三种类型都不需要导入(import)
- list的返回必须专为
-
Parcelable (序列化)
AIDL 自动生成的接口 需要导入(import)
实现android.os.Parcelable 接口的类. 需要导入(import)。
传入的参数若是list等大的数据,需要标注类型
- 常见的是
in
、Parcelable
,少用的out
、inout
。 in
:客户端的参数输入;out
:服务端的参数输入;inout
:这个可以叫输入输出参数,客户端可输入、服务端也可输入。客户端输入了参数到服务端后,服务端也可对该参数进行修改等,最后在客户端上得到的是服务端输出的参数。
void getDatas(in byte[] bs);
void DataWhole(in PackageData data);
AIDL支持自定义对象
AIDL、Binder、Messenger的在什么时候使用?
1、有IPC、有多个应用程序、有多线程时,使用AIDL
2、只有IPC、有多个应用程序、但没有多线程时,使用Binder
3、只有IPC、只有一个应用程序、没有多线程时,使用Messenger
aidl语法
编写语法
- 后缀为
aidl
aidl
文件里面的目录结构和代码的package结构一致- 接口类型是
interface
,对象类型是parcelable
- 文件名首字母大写
- 参数默认只能使用基础类型,使用复杂类型需要import,若是自定义对象需要手动创建对应的对象的aidl
- 文件名和接口名必须一致
- AIDL只支持方法,不能定义静态成员
- AIDL运行方法有任何类型的参数和返回值,除默认的类型外,均需要导包
package com.jsy.demo1; //包名,文件夹目录必须对应上
//interface 关键字,文件名首字母大写
interface DemoOne {//文件是接口文件
void init(); //定义接口
}
编译
命令行:在Android-sdk/build-tools/其中一个版本的/aidl.exe 把这个文件夹加入环境变量
aidl DemoOne.aidl
## 会在当前文件夹下生成对应的Java文件
Android Studio和eclipse:会自动生成
as中可以直接创建aidl,会自动创建包结构,位置在
build/source/aidl
中eclipse中会自动编译,as需要手动点击小象编译
使用步骤
移动端:
- 创建aidl,若是有自定义的对象需要创建对应的aidl和Java对象类并编写
Parcelable.Creator
等内容 - 创建
service
,监听客户端的连接,并实现aidl的方法
客户端:
- 复制aidl和需要的自定义的对象到对应的包中(包结构和文件需要和服务端一致)
- 编写
Intent
连接服务端 - 连接之后就可以调用服务端方法了
注意
参数大小的限制
如上在传递byte[] 长度大于1024*1024时会抛出 TransactionTooLargeException 异常
实现与服务之间互相调用
-
在绑定服务时会返回一个实现了AIDL的对象,这样可以通过对象调用服务中对应实现,
-
可以在应用层实现一个AIDL接口的对象,通过绑定服务返回的AIDL对象回传给服务,这样可以在服务中主动调用应用层的方法实现数据回传通知,
//接收回调
INotification notification = new INotification.Stub() {
@Override
public void Datas(byte[] bs) throws RemoteException {
Log.d(TAG, "Datas: 收到数据=" + Arrays.toString(bs));//已测试 最大数据1024*1024
}
}
//传递回调对象
void setNotification(in INotification Notification);
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iAidlInterface = IAidlInterface.Stub.asInterface(service);
try {
iAidlInterface.setNotification(notification);
} catch (RemoteException e) {
e.printStackTrace();
}
}
原理
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.jsy.aidl1;
public interface IMyAidl2 extends android.os.IInterface {
/**
* Default implementation for IMyAidl2.
*/
public static class Default implements com.jsy.aidl1.IMyAidl2 {
//璁剧疆 in
@Override
public java.util.List<com.jsy.aidl1.Persion> add(com.jsy.aidl1.Persion persion) throws android.os.RemoteException {
return null;
}
@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.jsy.aidl1.IMyAidl2 {
//
private static final java.lang.String DESCRIPTOR = "com.jsy.aidl1.IMyAidl2";
/** 构造方法
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**将IBinder对象转换为com.jsy.aidl1.IMyAidl2接口,如果需要,则生成代理。
* 拿到的是远程服务的代理
* Cast an IBinder object into an com.jsy.aidl1.IMyAidl2 interface,
* generating a proxy if needed.
*/
public static com.jsy.aidl1.IMyAidl2 asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.jsy.aidl1.IMyAidl2))) {
return ((com.jsy.aidl1.IMyAidl2) iin);
}
return new com.jsy.aidl1.IMyAidl2.Stub.Proxy(obj);
}
//返回这个 Stub
@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;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
//2. 收到 TRANSACTION_add 的消息
case TRANSACTION_add: {
data.enforceInterface(descriptor);
//2.2封装Persion对象
com.jsy.aidl1.Persion _arg0;
if ((0 != data.readInt())) {
_arg0 = com.jsy.aidl1.Persion.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
//2.3调用代理中的add方法
java.util.List<com.jsy.aidl1.Persion> _result = this.add(_arg0);
reply.writeNoException();
//2.4写回
reply.writeTypedList(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
//代理
private static class Proxy implements com.jsy.aidl1.IMyAidl2 {
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;
}
//璁剧疆 in
//真正实现add方法
//实现类调用add,传入persion
@Override
public java.util.List<com.jsy.aidl1.Persion> add(com.jsy.aidl1.Persion persion) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.jsy.aidl1.Persion> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((persion != null)) {
_data.writeInt(1);
persion.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
//1. transact会进入操作系统底层,发到Stub.TRANSACTION_add,进入存根onTransact
boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().add(persion);
}
_reply.readException();
_result = _reply.createTypedArrayList(com.jsy.aidl1.Persion.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.jsy.aidl1.IMyAidl2 sDefaultImpl;
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.jsy.aidl1.IMyAidl2 impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.jsy.aidl1.IMyAidl2 getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
//璁剧疆 in
public java.util.List<com.jsy.aidl1.Persion> add(com.jsy.aidl1.Persion persion) throws android.os.RemoteException;
}
案例
案例1:基础
服务端:提供接口,计算和
客户端:传入数据,展示计算的和
服务端
aidl
package com.jsy.aidl1;
interface IMyAidlInterface {
//计算2个数的和
int add(int num1,int num2);
}
service:监听客户端连接到服务端,并实现aidl的接口
创建的时候应该会加入xml中
package com.jsy.aidl1;//包结构不限制
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class MyAidlService extends Service {
private static final String TAG = "MyAidlService";
/**
* 当客户端绑定到这个服务的时候执行
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
private IBinder iBinder = new IMyAidlInterface.Stub() {
@Override
public int add(int num1, int num2) throws RemoteException {
Log.d(TAG, "add: 收到客户端求和参数");
return num1 + num2;
}
};
}
客户端
页面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<EditText
android:id="@+id/num1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="+"
/>
<EditText
android:id="@+id/num2"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="="
/>
<TextView
android:id="@+id/sum"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
/>
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="AIDL计算"/>
</LinearLayout>
aidl:需要和服务端一直(直接粘贴过来)
package com.jsy.aidl1;
interface IMyAidlInterface {
//计算2个数的和
int add(int num1,int num2);
}
页面使用
页面初始化的时候,连接服务端,点击按钮直接调用
package com.jsy.aidl2;
import androidx.appcompat.app.AppCompatActivity;
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.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.jsy.aidl1.IMyAidlInterface;
//客户端
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText num1Edit;
private EditText num2Edit;
private TextView sumText;
private Button button;
private IMyAidlInterface iMyAidlInterface;
private ServiceConnection conn = new ServiceConnection() {
//当绑定上服务端,IBinder就是服务端返回的IBinder
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//拿到了远程服务
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
//断开服务的时候
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface=null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
//软件启动就绑定服务
bindService();
}
private void initView() {
num1Edit = (EditText) findViewById(R.id.num1);
num2Edit = (EditText) findViewById(R.id.num2);
sumText = (TextView) findViewById(R.id.sum);
button = (Button) findViewById(R.id.btn);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// iMyAidlInterface.add(num1.get)
int num1 = Integer.parseInt(num1Edit.getText().toString());
int num2 = Integer.parseInt(num2Edit.getText().toString());
try {
//调用远程
int sum=iMyAidlInterface.add(num1,num2);
sumText.setText(sum+"");
} catch (RemoteException e) {
e.printStackTrace();
sumText.setText("错误了");
}
}
private void bindService() {
//获取到服务端
Intent intent = new Intent();
//aidl的包,aidl的全名
intent.setComponent(new ComponentName("com.jsy.aidl1", "com.jsy.aidl1.IMyAidlInterface"));
//绑定服务
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
案例2:使用自定义对象
服务器
区别
- 对象加上 in,文件引入对象包结构
- 对象创建entity,继承Parcelable,编写对应的读写方法
- 对象创建对应包结构的aidl
IMyAidl2.aidl
package com.jsy.aidl1;
//需要将自定义的对象导入
import com.jsy.aidl1.Persion;
interface IMyAidl2 {
//设置 in
List<Persion> add(in Persion persion);
}
Persion对象
package com.jsy.aidl1;
//服务端和客户端必须一致
import android.os.Parcel;
import android.os.Parcelable;
public class Persion implements Parcelable {
private String name;
private int age;
public Persion() {
}
public Persion(String name, int age) {
//注入的顺序和读取的顺序必须一致!!!!
this.name = name;
this.age = age;
}
public Persion(Parcel source) {
this.name=source.readString();
this.age=source.readInt();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int describeContents() {
return 0;
}
//写入
@Override
public void writeToParcel(Parcel dest, int flags) {
//需要注意顺序!!!!!
dest.writeString(name);
dest.writeInt(age);
}
public static final Parcelable.Creator<Persion> CREATOR=new Creator<Persion>() {
//读取
@Override
public Persion createFromParcel(Parcel source) {
return new Persion(source);
}
@Override
public Persion[] newArray(int size) {
return new Persion[0];
}
};
@Override
public String toString() {
return "Persion{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Persion.aidl
package com.jsy.aidl1;
//类型是Parcelable
parcelable Persion ;
service
package com.jsy.aidl1;
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 MyAidl2Service extends Service {
private ArrayList<Persion> persions;
/**
* 当客户端绑定到这个服务的时候执行
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
persions=new ArrayList<>();
return iBinder;
}
private IBinder iBinder = new IMyAidl2.Stub() {
@Override
public List<Persion> add(Persion persion) throws RemoteException {
persions.add(persion);
return persions;
}
};
}
客户端
2个aidl和persion需要复制过来(包结构一致)
Main2Activity调用
使用的时候没有区别
package com.jsy.aidl2;
import androidx.appcompat.app.AppCompatActivity;
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.jsy.aidl1.IMyAidl2;
import com.jsy.aidl1.IMyAidlInterface;
import com.jsy.aidl1.Persion;
import java.util.ArrayList;
import java.util.List;
public class Main2Activity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "Main2Activity";
private IMyAidl2 iMyAidl2;
private ServiceConnection conn = new ServiceConnection() {
//当绑定上服务端,IBinder就是服务端返回的IBinder
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//拿到了远程服务
iMyAidl2 = IMyAidl2.Stub.asInterface(service);
}
//断开服务的时候
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidl2 = null;
}
};
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
btn = (Button) findViewById(R.id.btn);
//软件启动就绑定服务
bindService();
btn.setOnClickListener(this);
}
private void bindService() {
//获取到服务端
Intent intent = new Intent();
//aidl的包,aidl的全名
intent.setComponent(new ComponentName("com.jsy.aidl1", "com.jsy.aidl1.iMyAidl2"));
//绑定服务
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
@Override
public void onClick(View v) {
try {
List<Persion> list = iMyAidl2.add(new Persion("aaa", 12));
Log.d(TAG, "onClick: " + list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}