Android Aidl使用 In Out InOut分析
1.Aidl作用
AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言
目的
实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信
Android跨进程通信的方式
Activity
Activity既可以在进程内(同一个应用程序)访问,也可以跨进程访问
跨进程访问需要指定的是要访问的Activity所对应的Action(一个字符串)或者指定一个Uri
Content Provider
Content Provider提供了一种在多个应用程序之间数据共享的方式(跨进程共享数据)
应用程序可以利用Content Provider完成数据的增、删、查、改
BroadcastReceiver
广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。这就象电台进行广播一样,听众只能被动地收听,而不能主动与电台进行沟通。
在应用程序中发送广播比较简单。只需要调用sendBroadcast方法即可。该方法需要一个Intent对象。通过Intent对象可以发送需要广播的数据。
AIDL服务
服务(Service)是android系统中非常重要的组件。Service可以脱离应用程序运行。也就是说,应用程序只起到一个启动Service的作用。一但Service被启动,就算应用程序关闭,Service仍然会在后台运行。
Service主要有两个作用:后台运行和跨进程通讯。后台运行就不用说了,当Service启动后,就可以在Service对象中 运行相应的业务代码,而这一切用户并不会察觉。而跨进程通讯是这一节的主题。如果想让应用程序可以跨进程通讯,就要使用我们这节讲的AIDL服务
2.TAG简介
Aidl在定义参数传递时,所有的非基本参数都需要一个定向tag来指出数据流通的方式,tag是AIDL语法的一部分,包括 in , out , inout 是三个定向tag
in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。
下面具体分析每一一种TAG的效果
3.Aidl使用
本次示例中,app模块作为客户端,aidlserver模块作为服务端,app依赖aidlserver模块
第一步:
在aidlserver model中创建Aidl需要传递的实体类,并实现Parcelable接口
public class User implements Parcelable {
private String name;
private int age;
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
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;
}
public User() {
}
public User(String name, int age, String id) {
this.name = name;
this.age = age;
this.id = id;
}
protected User(Parcel in) {
name = in.readString();
age = in.readInt();
id = in.readString();
}
public static final Creator CREATOR = new Creator() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
dest.writeString(id);
}
public void readFromParcel(Parcel dest) {
//注意,此处的读值顺序应当是和writeToParcel()方法中一致的
name = dest.readString();
age = dest.readInt();
id = dest.readString();
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", id='" + id + '\'' +
'}';
}
}
第二步:创建aidl文件,定义通信的规则
在项目的app或者model的目录上,点击右键,然后 new->AIDL->AIDL File,按下鼠标左键就会弹出一个框提示生成AIDL文件了。生成AIDL文件之后,项目的目录会变成这样的:
我们的示例在在aidlserver model中
package com.heezier.aidlserver;
//导入所需要使用的非默认支持数据类型的包
import com.heezier.aidlserver.User;
// Declare any non-default types here with import statements
interface IUserTransfer {
void sendInUser(in User user);
void sendOutUser(out User user);
void sendInOutUser(inout User user);
User getUser();
User getTypeUser(int type);
String update();
}
注意:
需要导入所需要使用的非默认支持数据类型的包
传参时除了Java基本类型以及String,CharSequence之外的类型,都需要在前面加上定向tag,具体加什么量需而定
第三步:在aidlserver model中创建需要传递的实体类对应的Aidl文件
// User.aidl
package com.heezier.aidlserver;
//注意parcelable是小写
parcelable User;
此时需要build一下就可以在generated/aidlXXXX中看到as自动生成的IUserTransfer接口,其中包含aidl的内部实现
第三步:在aidlserver model中创建服务端UserServer
标识aidlserver model为其他进程提供数据服务
具体代码如下:
public class UserServer extends Service {
private User mUser = new User("zhangsan", 20, "123456");
private Map userMap = new HashMap<>();
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("yhp", "================onBind");
return iUserTransfer;
}
private final IUserTransfer.Stub iUserTransfer = new IUserTransfer.Stub() {
@Override
public void sendInUser(User user) throws RemoteException {
user.setId("server_fix_In_000000");
userMap.put(0, user);
}
@Override
public void sendOutUser(User user) throws RemoteException {
user.setId("server_fix_Out_000000");
Log.e("yhp", "=======user=======" + user.toString());
userMap.put(1, user);
}
@Override
public void sendInOutUser(User user) throws RemoteException {
user.setId("server_fix_InOut_000000");
userMap.put(2, user);
}
@Override
public User getUser() throws RemoteException {
return mUser;
}
@Override
public User getTypeUser(int type) throws RemoteException {
return userMap.get(type);
}
@Override
public String update() throws RemoteException {
if(userMap == null) return null;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("修改后服务端的数据\n");
Log.e("yhp", "=======服务端修改后的数据=======");
for(int i = 0; i < userMap.size(); i++){
stringBuilder.append(userMap.get(i));
stringBuilder.append("\n");
}
Log.e("yhp", stringBuilder.toString());
return stringBuilder.toString();
}
};
@Override
public void onCreate() {
Log.e("yhp", "================onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("yhp", "================onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.e("yhp", "================onDestroy");
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
Log.e("yhp", "================onUnbind");
return super.onUnbind(intent);
}
}
继承Server,然后在创建Aidl服务的示例:IUserTransfer.Stub,在onBind返回实例对象;
IUserTransfer.Stub
实例分别实现了Aidl中定义的方法。
我们为了验证定向TAG的作用,所以在每次接受到数据之后,服务端做一次数据修改,主要修改User中的Id字段,然后再保存到一个map中。
另外在aidlserver model的AndroidManifest.xml注册服务,注意将服务放到另外一个进程中。
第四步:在app中以客户端的角色开启服务,使用Aidl服务
public class AdilActivity extends AppCompatActivity {
private IUserTransfer iUserTransfer;
private TextView textView;
private TextView textOriginView;
private TextView textServerView;
private TextView textResult;
private List listUser = new ArrayList();
StringBuilder stringOrginBuilder = new StringBuilder();
private Handler uiHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
tryBindServer();
textView = findViewById(R.id.tv_user);
findViewById(R.id.btn_getUser).setOnClickListener(v -> {
if(iUserTransfer != null){
try {
textView.setText("原始数据" + iUserTransfer.getUser().toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
textServerView = findViewById(R.id.tv_serverrsult);
textOriginView = findViewById(R.id.tv_originuser);
stringOrginBuilder.append("客户端原始数据======\n");
User inUser = new User("inUser", 1, "111111111");
stringOrginBuilder.append(inUser.toString()+ "\n");
listUser.add(inUser);
findViewById(R.id.btn_setInUser).setOnClickListener(v -> {
if(iUserTransfer != null){
try {
iUserTransfer.sendInUser(inUser);
inUser.setName("client_fix_inUser");
uiHandler.post(new Runnable() {
@Override
public void run() {
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
User outUser = new User("outUser", 2, "22222222");
stringOrginBuilder.append(outUser.toString() + "\n");
listUser.add(outUser);
findViewById(R.id.btn_setOutUser).setOnClickListener(v -> {
if(iUserTransfer != null){
try {
iUserTransfer.sendOutUser(outUser);
outUser.setName("client_fix_outUser");
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
User inoutUser = new User("inoutUser", 3, "33333333");
stringOrginBuilder.append(inoutUser.toString()+ "\n");
listUser.add(inoutUser);
findViewById(R.id.btn_setInOutUser).setOnClickListener(v -> {
if(iUserTransfer != null){
try {
iUserTransfer.sendInOutUser(inoutUser);
inoutUser.setName("client_fix_inoutUser");
uiHandler.postDelayed(inRunnable, 500);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
findViewById(R.id.btn_update).setOnClickListener(v -> {
if(iUserTransfer != null){
try {
textServerView.setText(iUserTransfer.update());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
textOriginView.setText(stringOrginBuilder.toString());
textResult = findViewById(R.id.tv_rsult);
}
private Runnable inRunnable = new Runnable() {
@Override
public void run() {
if(listUser != null){
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("修改后客户端的数据\n");
Log.e("yhp", "==================修改后客户端数据============");
for(User user: listUser){
stringBuilder.append(user.toString());
stringBuilder.append("\n");
Log.e("yhp", user.toString());
}
textResult.setText(stringBuilder.toString());
}
}
};
private void tryBindServer(){
if(serviceConnection != null){
Intent intent = new Intent();
intent.setAction("com.heezier.aidltest");
intent.setPackage("com.heezier.aidltest");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("yhp", "==================Client============onServiceConnected ");
iUserTransfer = IUserTransfer.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBind = false;
Log.e("yhp", "==================Client============onServiceDisconnected ");
}
};
private boolean isBind = false;
@Override
protected void onDestroy() {
super.onDestroy();
if(serviceConnection != null && isBind){
unbindService(serviceConnection);
}
}
}
主要步骤:
开启服务端的Server
private void tryBindServer(){
if(serviceConnection != null){
Intent intent = new Intent();
intent.setAction("com.heezier.aidltest");
intent.setPackage("com.heezier.aidltest");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
在服务连接成功onServiceConnected中获取IUserTransfer实例
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("yhp", "==================Client============onServiceConnected ");
iUserTransfer = IUserTransfer.Stub.asInterface(service);
}
接下来主要设置了五个按钮:
分别是服务端获取数据
向服务端设置数据,tag=in
向服务端设置数据,tag=out
向服务端设置数据,tag=inout
获取服务端的数据
分别对应IUserTransfer定义的方法,然后在每一种调用之后,客户端进行数据的更改,主要修改User中的name字段。
同时我们收集了三分数据:
我们传递给服务端之前的数据
客户端修改后的数据:修改User的name字段
服务端修改后的数据:修改User中的id字段
运行结果:
4.IN OUT INOUT
在 in 和 inout 作为定向 tag 的方法里,服务端能够正常的接收到客户端传过来的数据,但是在用 out 作为定向 tag 的方法里,服务端受到的是一个参数为空的 Book 对象
AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的参数为空的对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。
参考文章:https://blog.csdn.net/luoyanglizi/article/details/51958091