学习下使用AIDL实现IPC进程间通信。
几个概念
No. | Name | Detail |
---|---|---|
1 | AIDL | Android Interface Definition Language,即Android接口定义语言。【百度】 |
2 | IPC | Inter-Process Communication,进程间通信【百度】 |
3 | Binder | android的一个类,是android用来实现IPC的方式 |
原理图
网上各种文章说的很详细了,我们简单理解其实就是两个进程间通过共享内存的方式来进行通信或者说数据交换。
比如下图,为了保证安全性和独立性,进程1和进程2之间是独立的,进程1可以向共享内存写入数据,然后进程2就可以读取共享内存来或者数据了。android的实现方式就是Binder。
代码实现
服务端
aidl
android studio中新建工程,然后对工程右键,新建aidl文件
// ILoginAidlInterface.aidl
package com.qefee.androidaidlserver;
// Declare any non-default types here with import statements
interface ILoginAidlInterface {
/**
* 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 register(String name, String password);
/**
* 自定义登录接口
*/
String login(String name, String password);
}
然后编译工程(Make Project),这个时候会帮你生成一个ILoginAidlInterface
public interface ILoginAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.qefee.androidaidlserver.ILoginAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.qefee.androidaidlserver.ILoginAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.qefee.androidaidlserver.ILoginAidlInterface interface,
* generating a proxy if needed.
*/
public static com.qefee.androidaidlserver.ILoginAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.qefee.androidaidlserver.ILoginAidlInterface))) {
return ((com.qefee.androidaidlserver.ILoginAidlInterface)iin);
}
return new com.qefee.androidaidlserver.ILoginAidlInterface.Stub.Proxy(obj);
}
@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
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_register:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _result = this.register(_arg0, _arg1);
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_login:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _result = this.login(_arg0, _arg1);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.qefee.androidaidlserver.ILoginAidlInterface
{
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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.lang.String register(java.lang.String name, java.lang.String password) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeString(password);
mRemote.transact(Stub.TRANSACTION_register, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public java.lang.String login(java.lang.String name, java.lang.String password) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeString(password);
mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_register = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public java.lang.String register(java.lang.String name, java.lang.String password) throws android.os.RemoteException;
public java.lang.String login(java.lang.String name, java.lang.String password) throws android.os.RemoteException;
}
其中有个内部类Stub
,就是继承了Binder
的,用来实现IPC。
然后我们实现一个service,绑定后返回Binder
,不要忘记在manifest中注册服务
<service
android:name=".LoginService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.qefee.androidaidlserver.LoginService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
service
public class LoginService extends Service {
// 略
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: ");
return loginBinder;
}
private IBinder loginBinder = new ILoginAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public String register(String name, String password) throws RemoteException {
int code;
String msg;
if (TextUtils.isEmpty(name)) {
code = -1;
msg = "用户名为空";
} else if (TextUtils.isEmpty(password)) {
code = -2;
msg = "密码为空";
} else if (userMap.containsKey(name)) {
code = -3;
msg = "用户已经被注册";
} else {
code = 0;
msg = "注册成功";
userMap.put(name, password);
}
JSONObject obj = new JSONObject();
try {
obj.put("code", code);
obj.put("msg", msg);
} catch (JSONException e) {
throw new RemoteException("json化异常");
}
return obj.toString();
}
@Override
public String login(String name, String password) throws RemoteException {
int code;
String msg;
if (TextUtils.isEmpty(name)) {
code = -1;
msg = "用户名为空";
} else if (TextUtils.isEmpty(password)) {
code = -2;
msg = "密码为空";
} else if (!userMap.containsKey(name)) {
code = -3;
msg = "用户还未注册";
} else {
String pass = userMap.get(name);
if (TextUtils.equals(pass, password)) {
code = 0;
msg = "登录成功";
} else {
code = -4;
msg = "密码不正确";
}
}
JSONObject obj = new JSONObject();
try {
obj.put("code", code);
obj.put("msg", msg);
} catch (JSONException e) {
throw new RemoteException("json化异常");
}
return obj.toString();
}
};
}
客户端
aidl
android studio中新建工程,然后把服务端的aidl文件复制过来,重新编译工程(Make Project)
Activity
客户端剩下的就是绑定服务,然后进行通信了,直接看代码
UI上就几个控件
- 绑定服务按钮
- 解绑服务按钮
- 注册按钮
- 登录按钮
- name输入框
- password输入框
public class MainActivity extends AppCompatActivity {
/**
* log tag for MainActivity
*/
private static final String TAG = "MainActivity";
private ServiceConnection serviceConnection;
private ILoginAidlInterface loginAidlInterface;
private EditText et_name;
private EditText et_password;
private boolean bindService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(this::onClickFab);
Button btn_bind = findViewById(R.id.btn_bind);
btn_bind.setOnClickListener(this::onClickBind);
Button btn_unbind = findViewById(R.id.btn_unbind);
btn_unbind.setOnClickListener(this::onClickUnBind);
Button btn_register = findViewById(R.id.btn_register);
btn_register.setOnClickListener(this::onClickRegister);
Button btn_login = findViewById(R.id.btn_login);
btn_login.setOnClickListener(this::onClickLogin);
et_name = findViewById(R.id.et_name);
et_password = findViewById(R.id.et_password);
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected: ");
loginAidlInterface = ILoginAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected: ");
}
};
}
/**
* 点击了绑定服务按钮
*
* @param view view
*/
private void onClickBind(View view) {
Intent intent = new Intent();
intent.setPackage("com.qefee.androidaidlserver");
intent.setAction("com.qefee.androidaidlserver.LoginService");
intent.setComponent(new ComponentName("com.qefee.androidaidlserver", "com.qefee.androidaidlserver.LoginService"));
bindService = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
Log.i(TAG, String.format("onClickBind: bind = %b", bindService));
}
/**
* 点击了解绑服务按钮
*
* @param view view
*/
private void onClickUnBind(View view) {
Log.i(TAG, "onClickUnBind: ");
if (bindService) {
unbindService(serviceConnection);
}
}
private void onClickFab(View view) {
Log.i(TAG, "onClickFab: ");
}
/**
* 点击了注册按钮
*
* @param view view
*/
private void onClickRegister(View view) {
Log.i(TAG, "onClickRegister: ");
if (loginAidlInterface != null) {
String name = et_name.getText().toString().trim();
String password = et_password.getText().toString().trim();
try {
String json = loginAidlInterface.register(name, password);
Snackbar.make(view, json, Snackbar.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
Snackbar.make(view, e.getMessage(), Snackbar.LENGTH_SHORT).show();
}
} else {
Snackbar.make(view, "还没有连接服务器", Snackbar.LENGTH_SHORT).show();
}
}
/**
* 点击了登录按钮
*
* @param view view
*/
private void onClickLogin(View view) {
Log.i(TAG, "onClickLogin: ");
if (loginAidlInterface != null) {
String name = et_name.getText().toString().trim();
String password = et_password.getText().toString().trim();
try {
String json = loginAidlInterface.login(name, password);
Snackbar.make(view, json, Snackbar.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
Snackbar.make(view, e.getMessage(), Snackbar.LENGTH_SHORT).show();
}
} else {
Snackbar.make(view, "还没有连接服务器", Snackbar.LENGTH_SHORT).show();
}
}
}
代码中可以看到,绑定服务成功的话onServiceConnected
,我们可以获取到IBinder
,然后通过asInterface
转换为ILoginAidlInterface
,使用这个对象我们就可以进行远程调用方法了,比如我们在aidl中定义的register和login方法。