IPC的全称是 inter-progress communication就是进程间通信,IPC不是Android独有的任何一个操作系统都需要IPC机制。当然了,我们只说Android,因为别的我也不会……。我们先说说AIDL的用处,只要就是跨进程通讯,例如我们打开的程序1想访问另外一个程序2中的某些方法或者数据时,就可能会用到AIDL,其实我们一般都用过AIDL只是你可能不知道,就像Android中的ContentProvider还有Messenger的底层其实都是AIDL实现的。
用法:
工程1:新建一个包,包里新建一个.aidl结尾的文件,.aidl文件的写法和接口类似,只是不能有修饰符,之后刷新工程,会在gen目录下系统自动生成一个包里面有个.java文件,然后把这个包连同里面的文件拷贝到工程2的gen目录下。
工程2:把接收到的数据通过stub类的asInterface方法转换成需要的类型,然后就可以使用里面的方法了。
上面的说法感觉很抽象,接下来就举个栗子吧,这个栗子实现的功能就是同时开启两个程序,程序2可以获取程序1方法中的数据:
两个工程一个叫AIDL服务,一个叫AIDL客户端
AIDL服务的工程目录如下:
MainActivity.java里都是自动生成那些,我什么都没写。
Data.aidl的代码如下,这里的两个方法都是随便起的:
package com.zhangdi.aidl;
interface Data {
String getName ();
void setNumber(String num);
}
Myservice.java的代码如下:
package com.example.aidlservice;
import com.zhangdi.aidl.Data;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return new Mybinder();
}
private class Mybinder extends Data.Stub {//继承AIDL中的stub类
@Override
public String getName() throws RemoteException {//实现.aidl文件中定义的方法
// TODO Auto-generated method stub
return "zhangsan";
}
@Override
public void setNumber(String num) throws RemoteException {
Log.i("zhangsan", "num = " + num);
}
}
}
Manifest文件配置如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.aidlservice"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.example.aidlservice.MyService">
<intent-filter>
<action android:name="com.zhangdi.startService"/>
</intent-filter>
</service>
</application>
</manifest>
AIDL客户端的工程目录如下:
com.zhangdi.aidl这个包是从AIDL服务里考过来的。
MainActivity.java里的代码如下:
package com.example.aidlclient;
import com.zhangdi.aidl.Data;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
public class MainActivity extends Activity {
private ServiceConnection conn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void startService(View v) {
Intent intent = new Intent("com.zhangdi.startService");//启动服务的隐式意图
conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try{
Data data = Data.Stub.asInterface(service);
Log.i("zhangdi", data.getName());//获取AIDL服务里getName方法中的数据
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
bindService(intent, conn, BIND_AUTO_CREATE);
}
}
这个栗子很简单,接下来我们来分析一下系统自动生成的在gen目录下的com.zhangdi.aidl中的Data.java类。
我为了好看修改了一下文件的顺序,蓝色Data是最外面的接口,紫色是Data的内部类,黄色是紫色的内部类。Data类集成了IInterface接口,同时他自己也还是一个接口,所有可以在Binder中传输的接口都需要继承IInterface接口。首先Data类声明了两个方法,很显然这都是我们在Data.aidl中声明的方法,这也是为了后续实现Data接口类能够实现我们需要的方法,然后他在内部抽象类Stub中声明了两个常量用来区分两个方法,Stub同样没有实现我们需要的两个方法,显然这个两个方法需要由Stub的继承类来实现,asInterface方法判断了传递进来的IBinder对象是是在同一个进程中还是不同进程,相同进程则返回Stub类本身对象,不调用transact方法保存可以跨进程传递的信息,只有在不同进程返回的是Stub.Proxy对象,才调用transact方法,调用transact方法的逻辑由Stub的内部类Proxy来完成。
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\workspace6\\AIDL服务\\src\\com\\zhangdi\\aidl\\Data.aidl
*/
package com.zhangdi.aidl;
public interface Data extends android.os.IInterface
{
public java.lang.String getName() throws android.os.RemoteException;
public void setNumber(java.lang.String num) throws android.os.RemoteException;
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.zhangdi.aidl.Data
{
//Binder的唯一标示
private static final java.lang.String DESCRIPTOR = "com.zhangdi.aidl.Data";
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setNumber = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.zhangdi.aidl.Data interface,
* generating a proxy if needed.
*/
//asInterface方法是把相同进程中的IBinder对象强转为Data类型接口对象的方法,如果在不同进程则产生一个proxy类型的对象
public static com.zhangdi.aidl.Data asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.zhangdi.aidl.Data))) {
return ((com.zhangdi.aidl.Data)iin);
}
return new com.zhangdi.aidl.Data.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
//这个方法是运行在服务端的Binder线程池中,当客户端发起跨进程访问请求时,远程请求会通过系统底层封装后交由这个方法来处理。这个方法的原型是protected boolean onTransact(int code, Parcel data, Parcel reply,
int flags),服务端通过code可以确定客户端请求的目标是哪个方法,接着从data中取出方法所需参数,然后执行目标方法,最后通过reply写入返回值,最后需要注意的是如果最后方法返回的boolean值是false,则客户端请求失败,因此我们可以通过这个特性做权限校验,毕竟我们不希望任何进程都可以远程调用我们的进程。
@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_getName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_setNumber:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setNumber(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.zhangdi.aidl.Data
{
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;
}
//这个方法是运行在客户端的,实现过程:首先创建该方法需要的Parcel类型的输入对象_data和输出对象_reply以及返回值对象_result,如果该方法有参数则把参数写入_data中,接着调用transact方法来发起远程过程调用请求,同时当前线程挂起,在transact方法中会调用服务器端的OnTransact方法,直至远程过程调用返回结果,当前线程才继续执行,从_reply中取出结果,最后返回_reply中的数据。
@Override public java.lang.String getName() 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);
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
//这个方法与上边的方法实现原理基本一样。
@Override public void setNumber(java.lang.String num) 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.writeString(num);
mRemote.transact(Stub.TRANSACTION_setNumber, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
}
}
最后注意,有时候我们跨进程需要传递的参数或者返回值是自定义类型时,我们需要把实体类定义到aidl包下,并且在aidl包里再声明一个实体类对应的.aidl文件,如下:
package com.zhangdi.aidl;
parcelable MyUser;
然后在data.aidl里用到这个类的对象时需要
import com.zhangdi.aidl.MyUser;
如果使用这个类的对象作为参数时,需要指明参数是in还是out型
void setUser(in MyUser user);
我写了一下小demo上传到了github上 demo地址