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 thathappens 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 generalParcelableinterface), and references to liveIBinderobjects 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通信的使用和过程大致如此。
相关推荐
在Android系统中,跨进程通信(IPC,Inter-Process Communication)是实现不同应用程序间数据交换的关键技术。Android的AIDL(Android Interface Definition Language)就是专为处理这种需求而设计的,它允许开发者...
1. **基本概念**:Aidl是一种接口定义语言,它的主要作用是在Android应用的不同进程中定义和实现接口,使得服务可以被其他进程调用,实现跨进程通信。 2. **接口定义**:在Aidl文件中,我们可以定义接口(interface...
AIDL允许开发者定义接口,使得一个应用能够通过Binder机制调用另一个应用中的服务,从而实现跨进程通信(IPC,Inter-Process Communication)。 ### 1. AIDL基本概念 AIDL是Android特有的一种接口定义语言,它类似...
Android编译器会自动生成对应的Java接口`IMyAidlInterface.java`,并提供 Binder 机制实现跨进程通信。 接下来,在`AidlService`的`onBind()`方法中,我们需要返回实现了`IMyAidlInterface`接口的Binder对象。这个...
AIDL在Android开发中扮演着至关重要的角色,尤其是在需要实现跨进程数据共享和远程方法调用的场景下。它不仅简化了开发者的编码工作,也确保了不同进程间通信的安全性和效率。 总之,AIDL是Android开发者必须掌握的...
在Android系统中,跨进程通信(IPC,Inter-Process Communication)是实现不同应用程序间数据交换的关键技术。本示例“安卓跨进程高效通信demo”旨在演示如何通过Binder和共享内存这两种方式来实现在Android应用程序...
在365MobileSecretary的源码中,AIDL可能被用来实现跨进程的服务调用,例如,如果手机助手提供了远程服务,如文件上传或下载,那么客户端应用就需要通过AIDL来调用这些服务。 AIDL文件的结构主要包括接口声明、数据...
总结来说,AIDL是Android进程间通信的关键技术,它简化了跨进程调用的复杂性,使得开发者能够像在同一进程中调用方法一样轻松地调用另一个进程中的方法。通过AIDL定义的接口,我们可以构建出高效、可靠的分布式应用...
AIDL文件定义了方法的输入和输出参数,编译后会自动生成相应的Java代码,帮助开发者实现跨进程通信。 六、Binder实例 在提供的“android通信机制binder实例”中,可能包含以下几个部分: 1. 服务端接口定义(AIDL...
在365MobileSecretary中,AIDL被用来定义跨进程的服务接口,使得不同应用程序组件可以在不同的进程中调用服务提供的方法,实现了组件间的通信。通过分析AIDL的使用,我们可以学习到以下关键知识点: 1. **AIDL基本...
Binder还支持远程过程调用(Remote Procedure Call, RPC),使得进程间通信更为高效。 再者,Content Provider是Android中用于存储和分享数据的组件,它可以跨应用提供数据访问。通过定义Uri(统一资源标识符)和...
在Android开发中,当需要在不同的应用程序组件之间进行跨进程通信(IPC, Inter-Process Communication)时,Android提供了一种强大的工具——AIDL(Android Interface Definition Language)。AIDL使得我们可以定义...
总结来说,“Android Binder简单实例”涉及到Android系统中进程间通信的核心技术——Binder,以及其基本的C/S架构和通信流程。通过学习和分析提供的代码示例,开发者能够更好地理解和掌握Binder在实际应用中的使用。
1. **异常处理**:避免在远程调用中抛出异常,因为跨进程抛出异常难以处理。 2. **同步调用**:IPC调用默认是同步的,如果服务端处理耗时较长,可能会导致客户端阻塞。 3. **性能优化**:对于耗时较长的操作,可以在...
总结而言,Service是Android框架中一个极其重要的组件,它不仅能够执行后台任务,还支持跨进程通信,极大地提升了应用的灵活性和性能。理解和掌握Service的创建、生命周期管理以及跨进程调用机制,对于开发高质量、...
5. **AIDL**:AIDL(Android Interface Definition Language)是Android提供的接口定义语言,用于生成跨进程通信所需的接口代码。如果ProcessPro涉及到复杂的接口调用,比如多个方法或复杂数据类型的传输,那么很...
Android提供了一种跨进程通信(IPC)机制——AIDL(Android Interface Definition Language),用于实现不同进程间的远程过程调用。通过AIDL,服务可以暴露接口给其他应用,实现服务的远程调用。 总结,Android开发...