进程间常用的通信方式有:
1、广播
2、AIDL
3、Messenger
4、ContentProvider
有的人平时可能很少会用到两个进程交互的场景,一般都把功能做到一个app内就可以实现需求了。我最近就遇到一个要用到两个程序配合使用的场景:App1是一个人脸识别程序,因为人脸识别首先需要添加样本图片,当然这个功能在App1内是实现了的。现在有需求是另外的一个程序App2也可以添加样本到App1的数据库中,而且App1在识别到人脸后要把结果返回给App2。实现这个功能方法有很多,这里采用AIDL的方式。
思路:App2调用App1内的添加样本接口,通过参数把要传递的信息给App1,App1再去做对应的处理(插入数据库之类的)。App1人脸识别成功后通过异步回调的方式告诉App2识别结果。闲言碎语不要讲,开撸:
1、创建两个工程,分别叫AidlServer和AidlClient。包名分别是:
com.aidltest.server
com.aidltest.client
2、AidlServer内创建AIDL文件:在main文件夹下右击新建AIDL File
然后命名这个文件,我的命名是:IUserManager
然后发现IDE自动新建了和app一样的包名,并把aidl文件放在了下面
IUserManager.aidl:
// IUserManager.aidl
package com.aidltest.server;
// Declare any non-default types here with import statements
interface IUserManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
basicTypes这个是系统自己生成的方法没啥乱用,就是给你提示了一下参数支持的基本数据类型:
int , long , boolean , float ,double , String 。删掉就行,添加自己需要的方法:
// IUserManager.aidl
package com.aidltest.server;
interface IUserManager {
boolean addUser(inout User user);
boolean removeUser(String userId);
List getUserList();
void setRecognizeCallback(IRecognizeCallback callback);
void removeRecognizeCallback(IRecognizeCallback callback);
}
addUser 添加用户样本,注意参数是一个User对象
removeUser 删除用户样本,参数是String类型的用户id
getUserList 获取当前已经添加的用户样本列表
setRecognizeCallback 是设置Server端人脸识别之后给客户端的回调
明显此时还缺User 和IRecognizeCallback 这两个对象,先编译一下试试看:
果然用到两个对象的地方报错了。需要一个User的aidl文件,因为此处涉及到传递对象,所以要对其进行序列化,还需要IRecognizeCallback 的aidl文件,一个一个来。
在aidl/com/aidltest下创建一个bean包,再在bean下面新建一个User.aidl
User.aidl里面就两行:
// User.aidl
package com.aidltest.server.bean;
parcelable User;
注意
com.aidltest.server.bean
是User.aidl的所在的包,parcelable 全部是小写
然后相应的在java/com/aidltest下新建bean包,在bean包下创建User.java:
User.java:
public class User implements Parcelable {
private String name;
private int age;
private String id;
private String imgPath;
public User(String name, int age, String id, String imgPath) {
this.name = name;
this.age = age;
this.id = id;
this.imgPath = imgPath;
}
public User() {
}
protected User(Parcel in) {
name = in.readString();
age = in.readInt();
id = in.readString();
imgPath = in.readString();
}
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 String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getImgPath() {
return imgPath;
}
public void setImgPath(String imgPath) {
this.imgPath = imgPath;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeInt(this.age);
dest.writeString(this.id);
dest.writeString(this.imgPath);
}
public void readFromParcel(Parcel in) {
name = in.readString();
age = in.readInt();
id = in.readString();
imgPath = 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 String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", id='" + id + '\'' +
", imgPath='" + imgPath + '\'' +
'}';
}
}
注意不要遗漏readFromParcel这个方法,因为通过aidl生成的java文件会调用到它
User创建完毕,则此时IUserManager.aidl就可以导入User的包import com.aidltest.bean.User;
:
// IUserManager.aidl
package com.aidltest.server;
import com.aidltest.bean.User;
interface IUserManager {
boolean addUser(inout User user);
boolean removeUser(String userId);
List getUserList();
void setRecognizeCallback(IRecognizeCallback callback);
void removeRecognizeCallback(IRecognizeCallback callback);
}
编译,发现还差一个IRecognizeCallback 报错,在和IUserManager.aidl同一级下创建IRecognizeCallback .aidl:
IRecognizeCallback.aidl:
// IRecognizeCallback.aidl
package com.aidltest.server;
import com.aidltest.bean.User;
interface IRecognizeCallback {
void onUserRecognized(inout User user);
}
此时IUserManager.aidl再导入IRecognizeCallback的包
import com.aidltest.server.IRecognizeCallback;
:
// IUserManager.aidl
package com.aidltest.server;
import com.aidltest.bean.User;
import com.aidltest.server.IRecognizeCallback;
interface IUserManager {
boolean addUser(inout User user);
boolean removeUser(String userId);
List getUserList();
void setRecognizeCallback(IRecognizeCallback callback);
void removeRecognizeCallback(IRecognizeCallback callback);
}
到此为止server端的aidl文件已经全部创建完毕,编译之后发现IDE自动生成了:IUserManager.java和IRecognizeCallback.java。因为aidl的跨进程调用本质是通过binder,故此时需要创建server端的service。我创建的service叫RemoteService:
RemoteService.java:
public class RemoteService extends Service {
private final String TAG = "RemoteService";
private IRecognizeCallback mCallback;
public RemoteService() {
}
@Override
public IBinder onBind(Intent intent) {
IUserManager.Stub stub = new IUserManager.Stub() {
@Override
public boolean addUser(User user) throws RemoteException {
Log.d(TAG,"调用了addUser :" + user.toString());
return false;
}
@Override
public boolean removeUser(String userId) throws RemoteException {
Log.d(TAG,"调用了removeUser:" + userId);
return false;
}
@Override
public List getUserList() throws RemoteException {
return null;
}
@Override
public void setRecognizeCallback(IRecognizeCallback callback) throws RemoteException {
Log.d(TAG,"调用了setRecognizeCallback");
mCallback = callback;
}
@Override
public void removeRecognizeCallback(IRecognizeCallback callback) throws RemoteException {
}
};
return stub;
}
}
3、AidlClient内创建AIDL文件,这里就简单单了,因为在AidlServer内已经创建过AIDL文件了,直接把整个aidl包拷贝过来,外加一个User.java:
注意bean包的位置。编译后发现生成了对应的java文件
4、在MianActivity中绑定服务端的RemoteService ,布局很简单只有两个按钮:
MainActivity.java:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private final String TAG = "MainActivity";
private ExecutorService mThreadPool = Executors.newCachedThreadPool();
private Button mBtnBind;
private Button mBtnAdd;
private IUserManager mIUserManager;
private IRecognizeCallback.Stub callback = new IRecognizeCallback.Stub() {
@Override
public void onUserRecognized(User user) throws RemoteException {
Log.d(TAG, "onUserRecognized user = " + user.toString());
}
};
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIUserManager = IUserManager.Stub.asInterface(service);
Log.d(TAG, "RemoteService 连接成功");
try {
mIUserManager.setRecognizeCallback(callback);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "RemoteService 断开");
mIUserManager = null;
unbindService(connection);
bindRemoteService();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnBind = findViewById(R.id.btn_bind);
mBtnAdd = findViewById(R.id.btn_add);
mBtnBind.setOnClickListener(this);
mBtnAdd.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v == mBtnBind) {
bindRemoteService();
} else if (v == mBtnAdd) {
if (mIUserManager == null) {
return;
}
User user = new User();
user.setName("Tony");
user.setAge(27);
user.setId("12abc52d");
user.setImgPath("sdcard/tmp/img/12abc52d.jpg");
try {
mIUserManager.addUser(user);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
private void bindRemoteService() {
mThreadPool.submit(new Runnable() {
@Override
public void run() {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.aidltest.server", "com.aidltest.server.service.RemoteService"));
while (true) {
boolean isConnect = bindService(intent, connection, Context.BIND_AUTO_CREATE);
Log.i(TAG, "绑定远程服务,连接状态: " + isConnect);
if (isConnect && mIUserManager != null) {
break;
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
}
先点击绑定服务RemoteService绑定成功后,再点击添加样本,发现调用到RemoteService内的addUser方法了。RemoteService端识别到人脸后会回调onUserRecognized,这样就可以相互通信了。
注意bindRemoteService这个方法我是放到一个线程池内去执行的。因为AidlServer内的RemoteService是由AidlClient调起的,如果RemoteService被杀掉他们之间的通信就会断掉,断掉时会调用onServiceDisconnected此时再去重新绑定RemoteService就可以了,最后程序退出时记得解除绑定 :unbindService(connection);
。
Demo已上传GitHub: https://github.com/RainUtopia/AidlTest