http://victorzhong.github.io/2015/09/09/Android%E8%B7%A8%E8%BF%9B%E7%A8%8B%E9%80%9A%E4%BF%A1%E2%80%94%E2%80%94AIDL/
AIDL定义
当一个应用需要调用别的应用的Service(比如天气,地图)的时候,或者一个应用开启了多个进程(除主进程外还有网络或者通知进程),而不同的进程间需要通讯时,需要用到这个东西——AIDL。官方文档的定义是Android Interface Definition Language (AIDL),在Android Studio里看到的是Android Interface Description Language。AIDL是一种接口描述语言,编译器会搞定那些繁琐的东西,我们专注接口的实现和调用就行了。下面通过一个Demo来简单介绍一下AIDL。
设计接口
首先,用Android Studio建立一个工程AIDLServer充当服务端,提供接口给别的应用调用。在
在MainActivity.java的同级目录下新建一个AIDL文件,命名为IAidl,AS会帮我们生成一个Iaidl.aidl。
注意,这个aidl的包名是com.victor.aidlserver,客户端调用此接口的时候务必保存客户端拥有一个跟服务端完全一致的aidl文件,所以如果客户端也用这个包名肯定会报错,我们要先多做一步,把这个aidl移动一下:
1)在资源管理器中查看此aidl文件,剪切。
2)粘贴到上一级目录中,接着把aidlserver(是个空包)删了。
3)回到AS中,打开该aidl,把包名改成正确的,如下图(注意与上图的区别)。
ok,我们在aidl里定义两个接口:
// IAidl.aidl
package com.victor;
// Declare any non-default types here with import statements
interface IAidl {
// say hello to name
String sayHello(String name);
// 获取进程ID
int getPid();
}
编译,切换到Project视图,可以看到AS为我们生成了一个同名的.java文件:
格式化后:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\workspace\\Android\\AIDLServer\\app\\src\\main\\aidl\\com\\victor\\IAidl.aidl
*/
package com.victor;
// Declare any non-default types here with import statements
public interface IAidl extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.victor.IAidl {
private static final java.lang.String DESCRIPTOR = "com.victor.IAidl";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.victor.IAidl interface,
* generating a proxy if needed.
*/
public static com.victor.IAidl asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.victor.IAidl))) {
return ((com.victor.IAidl) iin);
}
return new com.victor.IAidl.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_sayHello: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.sayHello(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_getPid: {
data.enforceInterface(DESCRIPTOR);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.victor.IAidl {
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;
}
// say hello to name
@Override
public java.lang.String sayHello(java.lang.String name) 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);
mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
// 获取进程ID
@Override
public int getPid() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
// say hello to name
public java.lang.String sayHello(java.lang.String name) throws android.os.RemoteException;
// 获取进程ID
public int getPid() throws android.os.RemoteException;
}
结构是这样的:
要注意的是抽象内部类Stub,这个类扩展自 android.os.Binder ,并实现了 com.victor.IAidl ,类里面有个asInterface()的公共静态方法,用以将一个IBinder对象强制转换成我的接口的类型,必要的时候呢还会生成一个代理的对象(Cast an IBinder object into an com.victor.IAidl interface, generating a proxy if needed)。
接口有了,我们来实现一下,新建一个AidlService.java:
package com.victor.aidlserver;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import com.victor.IAidl;
/**
* Created by Administrator on 2015/9/9.
*/
public class AidlService extends Service {
private String TAG = "AidlService";
private IAidl.Stub binder = new IAidl.Stub() {
@Override
public String sayHello(String name) throws RemoteException {
return "Hello, " + name;
}
@Override
public int getPid() throws RemoteException {
return android.os.Process.myPid();
}
};
@Override
public void onCreate() {
Log.i(TAG, "onCreate");
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return binder;
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
super.onDestroy();
}
}
代码比较简单,当别的进程绑定AidlService这个服务时会调用onBind方法,返回一个IBinder对象,而这个对象就是我们实例出来的AIDL Stub对象,重写sayHello和getPid()方法就行了。
最后,记得在AndroidManifest.xml的下启用这个Service:
<service android:name=".AidlService">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="ANDROID.INTENT.CATEGORY.DEFAULT"/>
</intent-filter>
</service>
这样,服务端的接口就设计完成了。接下来看看客户端。
客户端
另外建个工程AIDLClient。
前面说过,Server和Client要有同样的AIDL才能进行通信,我们把Server的aidl整个文件夹复制到Client工程里对应的同级目录中。
同样的,在Client要编译一下,才能产生相应的.java文件。这样,Server跟Client就有了共同语言,可以开始通信了。
package com.victor.aidlclient;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.*;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.victor.IAidl;
public class MainActivity extends Activity {
private String TAG = "MainActivity";
private TextView currentTv, serverTv;
private Button sayBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initService();
initViews();
}
private void initViews() {
currentTv = (TextView) findViewById(R.id.tv_current_pid);
currentTv.setText(android.os.Process.myPid() + "");
serverTv = (TextView) findViewById(R.id.tv_server_pid);
sayBtn = (Button) findViewById(R.id.btn_say);
sayBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
String result = myAidl.sayHello("Victor");
int pid = myAidl.getPid();
serverTv.setText(result + "服务端PID为:" + pid);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
private IAidl myAidl;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected");
myAidl = IAidl.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected");
}
};
private void initService() {
//传入Server端的service的名字供本进程绑定
Intent intent = new Intent("android.intent.action.AIDLService");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(serviceConnection);
super.onDestroy();
}
}
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="当前进程:"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_current_pid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="null"
android:textSize="20sp" />
<Button
android:id="@+id/btn_say"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Say Hello"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="服务进程:"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_server_pid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="null"
android:textSize="20sp" />
</LinearLayout>
先把服务端跑起来,再运行客户端,按下Say Hello后:
demo地址:A demo to test the AIDL(