AIDL
google-AIDL
在Android ,进程独立,内存也独立,IPC binder 通信,数据与进程绑定,使用完成进行释放,因此数据分割合理。
如果使用binder 也可以绑定通信,为啥使用AIDL呢,使用AIDL 方面,工具生成code方便。
如果使用binder 然后调用transact()函数发送给服务端,而且还得制定一个小协议,参数谁先谁后,服务端和客户端都必须一致,否则就会出错,如果接口非常多,浪费了时间,因此采用AIDL 工作生成是效率最高的。
采用AIDL 情况分析:
1- 多个客户端通过IPC 访问服务,多线程处理,采用AIDL
2- 一个客户端,多线程服务可以采用 Binder
3-一个客户端,不采用多线程服务可以使用 Messenger
定义AIDL 接口
您必须在 .aidl 文件中使用 Java 编程语言的语法定义 AIDL 接口,然后将其保存至应用的源代码(在 src/ 目录中)内,这类应用会托管服务或与服务进行绑定。
如要使用 AIDL 创建绑定服务,请执行以下步骤:
- 创建 .aidl 文件
- 实现接口
- 向客户端公开接口
1-创建 .aidl 文件
您必须使用 Java 编程语言构建 .aidl 文件。每个 .aidl 文件均须定义单个接口,并且只需要接口声明和方法签名
aidl 支持的数据类型 int、long、char、boolean ,String, List ,Map,CharSequence等等
IRemoteService.aidl
package com.example.android
// Declare any non-default types here with import statements
/** Example service interface */
internal interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
val pid:Int
/** Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
fun basicTypes(anInt:Int, aLong:Long, aBoolean:Boolean, aFloat:Float,
aDouble:Double, aString:String)
}
您只需将 .aidl
文件保存至项目的src/
目录内,这样在构建应用时,SDK 工具便会在项目的 gen/
目录中生成 IBinder接口文件。生成文件的名称与 .aidl
文件的名称保持一致,区别在于其使用 .java
扩展名(例如,IRemoteService.aidl
生成的文件名是 IRemoteService.java
)
2-实现接口
当您构建应用时,Android SDK 工具会生成以 .aidl 文件命名的 .java 接口文件。生成的接口包含一个名为 Stub 的子类(例如,IRemoteService.Stub)
Server 端定义Stub binder:
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
AIDL 实现规则:
- 考虑线程安全,服务多线程处理情况
- 考虑耗时,如果是耗时的接口,则不应该在MainAcivity 中使用,考虑使用线程进行处理
- 服务引发的任何异常都不会回传给调用方
3-向客户端公开接口
IRemoteService.Stub 是返回到客户端的binder
RemoteService 实现onBind 供客户端调用
public class RemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
return binder;
}
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
}
客户端必须有接口类访问权限,因此如果客户端和服务在不同应用
客户端应用的src/
目录内必须包含.aidl
文件(该文件会生成 android.os.Binder
接口,进而为客户端提供 AIDL 方法的访问权限)的副本。
客户端如何实现onServiceConnected 到Server;
onServiceConnected-->IRemoteService.Stub.asInterface 返回的类型是:iRemoteService ,因此调用到Server 端
客户端onServiceConnected:
IRemoteService iRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
iRemoteService = IRemoteService.Stub.asInterface(service);
///iRemoteService 就是服务器端运行的service 代理,可以认为是透明的调用,服务器函数
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
iRemoteService = null;
}
};
4-IPC 传递对象
您可以通过 IPC 接口,将某个类从一个客户进程发送至另一个server进程,您必须确保 IPC 通道的另一端可使用该类的代码,并且该类必须支持 Parcelable
接口, 其实就是客户进程发送的数据class, 在服务器端能够解析,因此实现默认的Parcelable 协议即可
Rect.aidl:
package android.graphics;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;
Rect
实现Parcelable
协议的步骤:
- 让您的类实现
Parcelable
接口。 - 实现
writeToParcel
,它会获取对象的当前状态并将其写入Parcel
。 - 为您的类添加
CREATOR
静态字段,该字段是实现Parcelable.Creator
接口的对象。 - 最后,创建声明
Parcelable
类的.aidl
文件
以下示例展示 Rect
类如何实现 Parcelable
协议
import android.os.Parcel;
import android.os.Parcelable;
public final class Rect implements Parcelable {
public int left;
public int top;
public int right;
public int bottom;
public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {
public Rect createFromParcel(Parcel in) {
return new Rect(in);
}
public Rect[] newArray(int size) {
return new Rect[size];
}
};
public Rect() {
}
private Rect(Parcel in) {
readFromParcel(in);
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
public void readFromParcel(Parcel in) {
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
public int describeContents() {
return 0;
}
}
5-IPC 传递软件包(包含 Parcelable 类型)
IRectInsideBundle.aidl:
// IRectInsideBundle.aidl
package com.example.android;
/** Example service interface */
interface IRectInsideBundle {
/** Rect parcelable is stored in the bundle with key "rect" */
void saveRect(in Bundle bundle);
}
Server 端binder 实现:
private final IRectInsideBundle.Stub binder = new IRectInsideBundle.Stub() {
public void saveRect(Bundle bundle){
bundle.setClassLoader(getClass().getClassLoader());--》必须,否则ClassNotFoundException
Rect rect = bundle.getParcelable("rect");
process(rect); // Do more with the parcelable.
}
};
6-AIDL- IPC 客户端使用例子
- 在项目的
src/
目录中加入.aidl
文件。 - 声明一个
IBinder
接口实例(基于 AIDL 生成)。 - 实现
ServiceConnection
- 调用
Context.bindService()
,从而传入您的ServiceConnection
实现。 - 在
onServiceConnected()
实现中,您将收到一个IBinder
实例(名为service
)。调用xxxInterfaceName.Stub.asInterface((IBinder) service)
,以将返回的参数转换为xxxInterface
类型。 - 调用您在接口上定义的方法。您应始终捕获
DeadObjectException
异常,系统会在连接中断时抛出此异常。您还应捕获SecurityException
异常,当 IPC 方法调用中两个进程的 AIDL 定义发生冲突时,系统会抛出此异常。 - 如要断开连接,请使用您的接口实例调用
Context.unbindService()
。
public static class Binding extends Activity {
/** The primary interface we will be calling on the service. */
IRemoteService mService = null;
/** Another interface we use on the service. */
ISecondary secondaryService = null;
Button killButton;
TextView callbackText;
private InternalHandler handler;
private boolean isBound;
/**
* Standard initialization of this activity. Set up the UI, then wait
* for the user to poke it before doing anything.
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.remote_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(unbindListener);
killButton = (Button)findViewById(R.id.kill);
killButton.setOnClickListener(killListener);
killButton.setEnabled(false);
callbackText = (TextView)findViewById(R.id.callback);
callbackText.setText("Not attached.");
handler = new InternalHandler(callbackText);
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IRemoteService.Stub.asInterface(service);
killButton.setEnabled(true);
callbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
killButton.setEnabled(false);
callbackText.setText("Disconnected.");
// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
/**
* Class for interacting with the secondary interface of the service.
*/
private ServiceConnection secondaryConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// Connecting to a secondary interface is the same as any
// other interface.
secondaryService = ISecondary.Stub.asInterface(service);
killButton.setEnabled(true);
}
public void onServiceDisconnected(ComponentName className) {
secondaryService = null;
killButton.setEnabled(false);
}
};
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
// Establish a couple connections with the service, binding
// by interface names. This allows other applications to be
// installed that replace the remote service by implementing
// the same interface.
Intent intent = new Intent(Binding.this, RemoteService.class);
intent.setAction(IRemoteService.class.getName());
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
intent.setAction(ISecondary.class.getName());
bindService(intent, secondaryConnection, Context.BIND_AUTO_CREATE);
isBound = true;
callbackText.setText("Binding.");
}
};
private OnClickListener unbindListener = new OnClickListener() {
public void onClick(View v) {
if (isBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
unbindService(secondaryConnection);
killButton.setEnabled(false);
isBound = false;
callbackText.setText("Unbinding.");
}
}
};
private OnClickListener killListener = new OnClickListener() {
public void onClick(View v) {
// To kill the process hosting our service, we need to know its
// PID. Conveniently our service has a call that will return
// to us that information.
if (secondaryService != null) {
try {
int pid = secondaryService.getPid();
// Note that, though this API allows us to request to
// kill any process based on its PID, the kernel will
// still impose standard restrictions on which PIDs you
// are actually able to kill. Typically this means only
// the process running your application and any additional
// processes created by that app as shown here; packages
// sharing a common UID will also be able to kill each
// other's processes.
Process.killProcess(pid);
callbackText.setText("Killed service process.");
} catch (RemoteException ex) {
// Recover gracefully from the process hosting the
// server dying.
// Just for purposes of the sample, put up a notification.
Toast.makeText(Binding.this,
R.string.remote_call_failed,
Toast.LENGTH_SHORT).show();
}
}
}
};
// ----------------------------------------------------------------------
// Code showing how to deal with callbacks.
// ----------------------------------------------------------------------
/**
* This implementation is used to receive callbacks from the remote
* service.
*/
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
/**
* This is called by the remote service regularly to tell us about
* new values. Note that IPC calls are dispatched through a thread
* pool running in each process, so the code executing here will
* NOT be running in our main thread like most other things -- so,
* to update the UI, we need to use a Handler to hop over there.
*/
public void valueChanged(int value) {
handler.sendMessage(handler.obtainMessage(BUMP_MSG, value, 0));
}
};
private static final int BUMP_MSG = 1;
private static class InternalHandler extends Handler {
private final WeakReference<TextView> weakTextView;
InternalHandler(TextView textView) {
weakTextView = new WeakReference<>(textView);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
TextView textView = weakTextView.get();
if (textView != null) {
textView.setText("Received from service: " + msg.arg1);
}
break;
default:
super.handleMessage(msg);
}
}
}
}