進(jìn)程間常用的通信方式有:
1、廣播
2、AIDL
3、Messenger
4、ContentProvider
有的人平時可能很少會用到兩個進(jìn)程交互的場景,一般都把功能做到一個app內(nèi)就可以實現(xiàn)需求了。我最近就遇到一個要用到兩個程序配合使用的場景:App1是一個人臉識別程序,因為人臉識別首先需要添加樣本圖片,當(dāng)然這個功能在App1內(nèi)是實現(xiàn)了的?,F(xiàn)在有需求是另外的一個程序App2也可以添加樣本到App1的數(shù)據(jù)庫中,而且App1在識別到人臉后要把結(jié)果返回給App2。實現(xiàn)這個功能方法有很多,這里采用AIDL的方式。
思路:App2調(diào)用App1內(nèi)的添加樣本接口,通過參數(shù)把要傳遞的信息給App1,App1再去做對應(yīng)的處理(插入數(shù)據(jù)庫之類的)。App1人臉識別成功后通過異步回調(diào)的方式告訴App2識別結(jié)果。閑言碎語不要講,開擼:
1、創(chuàng)建兩個工程,分別叫AidlServer和AidlClient。包名分別是:
com.aidltest.server com.aidltest.client
2、AidlServer內(nèi)創(chuàng)建AIDL文件:在main文件夾下右擊新建AIDL File

然后命名這個文件,我的命名是:IUserManager

然后發(fā)現(xiàn)IDE自動新建了和app一樣的包名,并把a(bǔ)idl文件放在了下面
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這個是系統(tǒng)自己生成的方法沒啥亂用,就是給你提示了一下參數(shù)支持的基本數(shù)據(jù)類型:
int , long , boolean , float ,double , String 。刪掉就行,添加自己需要的方法:
// IUserManager.aidl
package com.aidltest.server;
interface IUserManager {
boolean addUser(inout User user);
boolean removeUser(String userId);
List<User> getUserList();
void setRecognizeCallback(IRecognizeCallback callback);
void removeRecognizeCallback(IRecognizeCallback callback);
}
addUser 添加用戶樣本,注意參數(shù)是一個User對象
removeUser 刪除用戶樣本,參數(shù)是String類型的用戶id
getUserList 獲取當(dāng)前已經(jīng)添加的用戶樣本列表
setRecognizeCallback 是設(shè)置Server端人臉識別之后給客戶端的回調(diào)
明顯此時還缺User 和IRecognizeCallback 這兩個對象,先編譯一下試試看:

果然用到兩個對象的地方報錯了。需要一個User的aidl文件,因為此處涉及到傳遞對象,所以要對其進(jìn)行序列化,還需要IRecognizeCallback 的aidl文件,一個一個來。
在aidl/com/aidltest下創(chuàng)建一個bean包,再在bean下面新建一個User.aidl

User.aidl里面就兩行:
// User.aidl
package com.aidltest.server.bean;
parcelable User;
注意
com.aidltest.server.bean是User.aidl的所在的包,parcelable 全部是小寫
然后相應(yīng)的在java/com/aidltest下新建bean包,在bean包下創(chuàng)建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<User> CREATOR = new Creator<User>() {
@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文件會調(diào)用到它
User創(chuàng)建完畢,則此時IUserManager.aidl就可以導(dǎo)入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<User> getUserList();
void setRecognizeCallback(IRecognizeCallback callback);
void removeRecognizeCallback(IRecognizeCallback callback);
}
編譯,發(fā)現(xiàn)還差一個IRecognizeCallback 報錯,在和IUserManager.aidl同一級下創(chuàng)建IRecognizeCallback .aidl:

IRecognizeCallback.aidl:
// IRecognizeCallback.aidl
package com.aidltest.server;
import com.aidltest.bean.User;
interface IRecognizeCallback {
void onUserRecognized(inout User user);
}
此時IUserManager.aidl再導(dǎo)入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<User> getUserList();
void setRecognizeCallback(IRecognizeCallback callback);
void removeRecognizeCallback(IRecognizeCallback callback);
}
到此為止server端的aidl文件已經(jīng)全部創(chuàng)建完畢,編譯之后發(fā)現(xiàn)IDE自動生成了:IUserManager.java和IRecognizeCallback.java。因為aidl的跨進(jìn)程調(diào)用本質(zhì)是通過binder,故此時需要創(chuàng)建server端的service。我創(chuàng)建的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,"調(diào)用了addUser :" + user.toString());
return false;
}
@Override
public boolean removeUser(String userId) throws RemoteException {
Log.d(TAG,"調(diào)用了removeUser:" + userId);
return false;
}
@Override
public List<User> getUserList() throws RemoteException {
return null;
}
@Override
public void setRecognizeCallback(IRecognizeCallback callback) throws RemoteException {
Log.d(TAG,"調(diào)用了setRecognizeCallback");
mCallback = callback;
}
@Override
public void removeRecognizeCallback(IRecognizeCallback callback) throws RemoteException {
}
};
return stub;
}
}
3、AidlClient內(nèi)創(chuàng)建AIDL文件,這里就簡單單了,因為在AidlServer內(nèi)已經(jīng)創(chuàng)建過AIDL文件了,直接把整個aidl包拷貝過來,外加一個User.java:

注意bean包的位置。編譯后發(fā)現(xiàn)生成了對應(yīng)的java文件
4、在MianActivity中綁定服務(wù)端的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, "綁定遠(yuǎn)程服務(wù),連接狀態(tài): " + isConnect);
if (isConnect && mIUserManager != null) {
break;
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
}
先點擊綁定服務(wù)RemoteService綁定成功后,再點擊添加樣本,發(fā)現(xiàn)調(diào)用到RemoteService內(nèi)的addUser方法了。RemoteService端識別到人臉后會回調(diào)onUserRecognized,這樣就可以相互通信了。
注意bindRemoteService這個方法我是放到一個線程池內(nèi)去執(zhí)行的。因為AidlServer內(nèi)的RemoteService是由AidlClient調(diào)起的,如果RemoteService被殺掉他們之間的通信就會斷掉,斷掉時會調(diào)用onServiceDisconnected此時再去重新綁定RemoteService就可以了,最后程序退出時記得解除綁定 :unbindService(connection);。
Demo已上傳GitHub: https://github.com/RainUtopia/AidlTest