AIDL其实是一个IPC通信机制, 即进程之间也可以相互通信,传递有效数据,那么怎么创建使用呢?第一次创建一整套AIDL还是有点懵逼,而且我们平时不经常使用AIDL,今天记录下创建的整个过程,以备不时之需。
我们知道AIDL是跨进程通信,那么肯定是在两个进程中,我们这里创建两个工程,分别作为server端和client端,整个项目的目录结构如图:
server端: client端:
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable{
private String mUserName;
private String mUserAge;
public Person(String userName, String userAge) {
mUserName = userName;
mUserAge = userAge;
}
public String getmUserName() {
return mUserName;
}
public void setmUserName(String mUserName) {
this.mUserName = mUserName;
}
public String getmUserAge() {
return mUserAge;
}
public void setmUserAge(String mUserAge) {
this.mUserAge = mUserAge;
}
@Override
public String toString() {
return super.toString();
}
// 这里的读的顺序必须与writeToParcel(Parcel dest, int flags)方法中写的顺序一致
// 否则数据会有差错
public Person(Parcel in) {
mUserName = in.readString();
mUserAge = in.readString();
}
public static final Creator CREATOR = new Creator() {
// 从Parcel中读取数据
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
//作用:把值写入Parcel中
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mUserAge);
dest.writeString(mUserAge);
}
}
// IPersonAidlInterface.aidl
package com.example.server;
parcelable Person;
// IRomteAidlInterface.aidl
package com.example.server;
// Declare any non-default types here with import statements
//此处的导包是按需导入
interface IRomteAidlInterface {
/**
* 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);
String getPersonUserName();
String getPersonUserAge();
}
创建或修改过AIDL文件后需要clean下工程,使系统及时生成我们需要的文件。注意如果这个AIDL需要使用Person类的话,则需要明确导包,将Person类给import进来,同理,客户端同样需要把Person类给拷贝过去然后进行导包。本例中我们没有使用AIDL不支持的外来类,因此这里就不需要导包操作了。
接下来需要来创建一个 Service 供客户端远程绑定,这里实现的两个方法就是AIDL接口中的那两个方法的具体实现。
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
public class RmoteService extends Service{
private Person mPerson;
private String mUserName;
private String mUserAge;
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
mPerson = new Person("buder_mm", "18");
return new RomteBinder();
}
private class RomteBinder extends IRomteAidlInterface.Stub {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public String getPersonUserName() throws RemoteException {
mUserName = mPerson.getmUserName();
return mUserName;
}
@Override
public String getPersonUserAge() throws RemoteException {
mUserAge = mPerson.getmUserAge();
return mUserAge;
}
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
}
把服务端的AIDL文件以及Person类复制过来,将 aidl 文件夹整个复制到和Java文件夹同个层级下,需要创建和服务端Person类所在的相同包名来存放 Person类,不需要改动任何代码。创建或修改过AIDL文件后需要clean下工程,使系统及时生成我们需要的文件。 这里我们不需要将Person类复制过来,因为客户端的AIDL没有使用到Person类,如果使用到的话就需要一并粘贴过来。
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
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.Toast;
import com.example.server.IRomteAidlInterface;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final String BIND_SERVICE_ACTION = "android.intent.action.RESPOND_AIDL_MESSAGE";
private IRomteAidlInterface iRomteAidlInterface;
private Button mConnectButton;
private Button mAcquireButton;
private String mUsername;
private String mUserage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mConnectButton = findViewById(R.id.connect);
mAcquireButton = findViewById(R.id.acquire_info);
mConnectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
bindService();
}
});
mAcquireButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (null != iRomteAidlInterface) {
try {
mUsername = iRomteAidlInterface.getPersonUserName();
mUserage = iRomteAidlInterface.getPersonUserAge();
Toast.makeText(getApplicationContext(), "mUsername = " + mUsername + ",mUserage = " + mUserage, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iRomteAidlInterface = IRomteAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iRomteAidlInterface = null;
}
};
private void bindService() {
Intent intent = new Intent();
intent.setAction(BIND_SERVICE_ACTION);
final Intent newIntent = new Intent(achieveExplicitFromImplicitIntent(this, intent));
bindService(newIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private Intent achieveExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
Intent explicitIntent = new Intent(implicitIntent);
explicitIntent.setComponent(component);
return explicitIntent;
}
private void unBindService() {
unbindService(serviceConnection);
}
@Override
protected void onDestroy() {
super.onDestroy();
unBindService();
}
}
客户端绑定service的方法里面需要注意, 这个方法achieveExplicitFromImplicitIntent是为了显示跨进程启动service,这里需要设置远程service的包名和service名,如果不设置的话,将无法启动远程service,将会报 Service Intent must be explicit: Intent { act=android.intent.action.RESPOND_AIDL_MESSAGE } 详见 https://blog.csdn.net/cpcpcp123/article/details/90205693
demo参考:https://www.jianshu.com/p/83bdeac67ee7
上面两个项目已上传到git上,可以对比查看:
server端:https://github.com/buder-cp/base_component_learn/tree/master/server
client端:https://github.com/buder-cp/base_component_learn/tree/master/client
更多的例子可以参考:https://github.com/buder-cp/IPCSamples