android在设计理念上强调组件化,组件之间的依赖性很小。我们往往发一个intent请求就可以启动另一个应用的activity,或者一个你不知道在哪个进程的service,或者可以注册一个广播,只要有这个事件发生你都可以收到,又或者你可以查询一个contentProvider获得你想要的数据,这其实都需要跨进程通信的支持。只是android将其封装的如此简单,应用开发者甚至完全不用关注它是不是和我在一个进程里。
我们有没有想过安全性问题,如此简单就可以跨进程的访问,安全性问题怎么保证。本来每个进程都是一个孤岛,而通过ipc,这个孤岛却可以和世界通信了。这里简单介绍下android中的安全机制。
android的安全机制分为三层。最基础的一层,android将数据分为system和data两个区。其中system是只读的,data用来存放应用自己的数据,这保证了系统数据不会被随意改写。第二层用来使应用之间的数据相互独立。每个应用都会有一个user id和group id,只有相同的user id并且来自同一个作者,才能访问它们的数据。作者通过对apk签名来标识自己。签名和uid构成了双重的保证。第三个层次就是权限体系,这个就不用多说了。
拉回正题,那么android是如何实现ipc的呢?答案是binder。我打算用两篇来介绍android的binder机制,这一篇着重如何使用,介绍跨进程调用的过程和aidl。另一篇着重binder实现机制。
Binder并不是android最早开始使用,它发源于Be和Palm之前的OpenBinder,由Dianne Hackborn领导开发。Hackborn现在就在google,是android framework的工程师,我们可以从https://lkml.org/lkml/2009/6/25/3看一下,Hackborn如何描述binder。一句话总结:
In the Android platform, the binder is used for nearly everything that
happens across processes in the core platform.
可是android将binder几乎封装的不可见,我们看下层次结构是怎么样的。
最底层的是android的ashmen(Anonymous shared memoryy)机制,它负责辅助实现内存的分配,以及跨进程所需要的内存共享。
AIDL(android interface definition language)对Binder的使用进行了封装,可以让开发者方便的进行方法的远程调用,后面会详细介绍。
Intent是最高一层的抽象,方便开发者进行常用的跨进程调用。
关于如何使用intent去跨进程的启动一个activity或者service等,这里就不再介绍了,是android中非常基础的内容。
这里讲如何实现远程的方法调用。在android中对方法的远程调用无处不在,随便打开framework/base中的包,都会发现很多aidl文件。AIDL是android为了方便开发者进行远程方法调用,定义的一种语言。使用aidl完成一个远程方法调用只需要三个步骤:
1.用aidl定义需要被调用方法接口。
2.实现这些方法。
3.调用这些方法。
我们拿ApiDemo中的例子来学习。在app包下面有一个ISecondary.aidl
interface {
/**
* Request the PID of this service, to do evil things with it.
*/
int getPid();
/**
* This demonstrates the 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);
}
看起来和java没有什么区别。可以看到它定义个了两个接口方法。从这里我们可以知道AIDL(android接口定义语言的由来)。android会将该aidl生成一个java文件(如果你使用eclipse,会自动生成。在gen目录下。),生成的代码如下:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /home/dd/workspace/ApiDemos/src/com/example/android/apis/app/ISecondary.aidl
*/
package com.example.android.apis.app;
/**
* Example of a secondary interface associated with a service. (Note that
* the interface itself doesn't impact, it is just a matter of how you
* retrieve it from the service.)
*/
public interface ISecondary extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.android.apis.app.ISecondary
{
private static final java.lang.String DESCRIPTOR = "com.example.android.apis.app.ISecondary";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.android.apis.app.ISecondary interface,
* generating a proxy if needed.
*/
public static com.example.android.apis.app.ISecondary asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.android.apis.app.ISecondary))) {
return ((com.example.android.apis.app.ISecondary)iin);
}
return new com.example.android.apis.app.ISecondary.Stub.Proxy(obj);
}
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_getPid:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
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;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.android.apis.app.ISecondary
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
* Request the PID of this service, to do evil things with it.
*/
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;
}
/**
* This demonstrates the 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
{
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();
}
}
}
static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* Request the PID of this service, to do evil things with it.
*/
public int getPid() throws android.os.RemoteException;
/**
* This demonstrates the 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;
}
我们分析下,android工具将我们写的aidl文件生成了怎样的一个文件,它都做哪些工作。
首先这个接口继承了android.os.IInterface.它是所有由aidl文件生成的基类。接口里有一个内部类Stub,它继承自Binder并实现了这个生成的java接口ISecondary。但是它并没有实现我们定义的接口方法。而这些接口方法其实就是留给我们去实现的。在ApiDemo中,RemoteService类实现了这些方法:
/**
* A secondary interface to the service.
*/
private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() {
public int getPid() {
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
}
};
这就是我们要做的第二部操作,实现这些方法。
继续看这个接口类。在stub中实现了一个很重要的方法asInterface(android.os.IBinder obj)。该方法中会去查询是否有一个ISecondary的实例,这其实是去查询是不是在同一个应用里去调用它,那我们就不用实行远程调用,直接本地调用就可以了。如果不是本地接口,这时候会返回一个Proxy对象。Proxy类是Stub的一个内部类,也同样实现了ISecondary接口。但是它却已经实现了这些接口方法。这就意味着如果要进行远程调用,必须获取一个Proxy类的实例,自然是通过stub类的asInterface方法获得。看下ApiDemo里如何获取该实例。
/**
* Class for interacting with the secondary interface of the service.
*/
private ServiceConnection mSecondaryConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// Connecting to a secondary interface is the same as any
// other interface.
mSecondaryService = ISecondary.Stub.asInterface(service);
mKillButton.setEnabled(true);
}
public void onServiceDisconnected(ComponentName className) {
mSecondaryService = null;
mKillButton.setEnabled(false);
}
};
可以看到是在onServiceConnected里获得了这个远程实例,具体如何得到?
ServiceConnection对象其实是在更早之前用来绑定service而调用的bindService方法的参数。
bindService(new Intent(ISecondary.class.getName()),
mSecondaryConnection, Context.BIND_AUTO_CREATE);
ActivityManagerService在bindService时,会调用ActivityThread的方法,并会传递一个Binder引用,而ActivityThread会回调ServiceConnection中的OnServiceConnected方法,并将这个Binder对象传入,也就是anInterface方法中的这个service。这样整个流程走完就获得了远程实例,我们一般会把它保存到一个全局变量中,供以后调用远程方法。
这时候我们就可以执行第三步了,进行方法调用。
int pid = mSecondaryService.getPid();
其实这时候我们已经完成了远程调用,获取了pid的值。
不过我们不妨继续看下去。我们看另一个方法basicTypes,apidemo没有使用,但是另一个方法传入了参数,更具代表意义,我们去实现basicTypes方法,并通过Proxy进进行远程调用它(代码就不贴了)。此时这个调用会被proxy对象转换成可以用pacel包装的基础数据类型,参数也被序列化写入一个数据包。一个用户定义的int型code将会被指派给transaction,这个code用来标识方法名,因为Binder此时只允许传递int类型。这就需要客户端和远程服务端做好约定。
方法实现如下:
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();
}
方法首先通过obtain方法获取两个Parcel对象。调用writeInterfaceToken方法用来标识,以便服务端能够识别。然后写入参数,注意这个写入顺序和取出顺序必须是一致的。然后对传给Proxy的binder对象调用了transact方法,该方法中就将code作为参数传入。pacel对象通过jni接口传递到Binder的C++空间,最终传递到Binder驱动。binder驱动会让客户端进程休眠,并且将传过来的pacel数据从客户端进程映射到服务端进程。然后反向的传递,从binder驱动传递到C++中间层,再通过JNI传递到java层。此时Stub的ontransact方法会被调用。方法如下:
@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_getPid:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
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;
}
}
return super.onTransact(code, data, reply, flags);
}
首先通过对code的判断,执行对应方法的内容,对数据按顺序一一解包,读出参数。最终调用方法,并将返回值写入parcel,传递给binder驱动。binder驱动重新唤醒客户端进程并把返回值传递给proxy对象,并最后被解包并作为proxy方法的返回值。
从这一个流程下来,我们可以知道aidl主要就帮助我们完成了包装数据和解包的过程,并调用了transact过程。而用来传递的数据包我们就称为parcel。关于parcel,我们直接看下官方文档的描述;
Container for a message (data and object references) that can be sent through an IBinder. A Parcel can contain both flattened data that will be unflattened on the other side of the IPC (using the various methods here for writing specific types, or the general [Parcelable](http://developer.android.com/reference/android/os/Parcelable.html)
interface), and references to live [IBinder](http://developer.android.com/reference/android/os/IBinder.html)
objects that will result in the other side receiving a proxy IBinder connected with the original IBinder in the Parcel.
如果要传递的参数不是基础类型,那就需要对其进行包装,成为parcelable的实例。如下:
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}
最后看下这张图:
是不是很明了了?我想大家看完以后手动写一个远程调用而不使用aidl也是可以完成了。不得不说,android设计的非常好,也用aidl让需要用到ipc的时候对开发者非常友好。android中ipc通信的使用和过程大致如此。欢迎转载,请注明出处,谢谢了。http://blog.csdn.net/notice520/article/details/8135600
手机扫一扫
移动阅读更方便
你可能感兴趣的文章