前言:笔者之前也看了很多关于IPC之类的文章和书籍,发现这个东西真的很难理解清楚,很少有人把它介绍清楚,因为关于IPC真的特别复杂,其实我们应该都明白在Android里面,要想研究一些底层的实现,那么我们不可避免的去看源码。鉴于IPC,要想真正理解透彻你只有从源码角度看起,但是你会发现关于IPC这块底层几乎全部都是C/C++代码,看起来真的很头疼。介于技术笔者这块也只能说上层原理和作用。
IPC是Inter-Process Communication的缩写,是进程间通信意思。在说这个话题之前,我们知道在PC端我们经常也会听到关于IPC机制,Android系统是基于Linux内核的,在Linux上面它是怎么进行进程间通信的呢?在大学里面如果是学过关于操作系统,应该会了解点,进程之间通信的方式有:信号量(Signal),消息队列,消息邮箱,还有传统的管道(Pipe),或者通过文件共享的方式等等,比如说A进程将数据写入文件,B进程再去读取这个文件。其实读者应该明白,关于两个进程之间是不能够直接通信的,这个是肯定的,很简单,这是两个彼此独立的应用程序,拥有不同的内存地址,那么他们的通信其实都是通过操作系统的支持,A进程向操作系统申请一块内存空间,把该共享的数据放入,B进程也向操作系统申请读取这边内存空间,这时候就可以进行数据交换了。
那么在Android中关于进程间通信是否也像上面说的Linux一样的?
答案是否了,Android并没有继承关于Linux IPC之间的秉性,而是采用了一套自己都有的方式——Binder机制
什么是Binder?又该如何了解Binder呢?
Binder是Android之中进行进程间通信最有特色的一种,根据在工作中的性质可能会大量用到,直接来说Binder是Android系统里面提供的一个类,实现了IBinder接口。从IPC角度来说呢Binder是Android中一种跨进程通信方式,从Android Frameword角度来说,Binder是ServiceManager连接各种Manager(ActivityManager,WindowManager等等)和相应ManagerService的桥梁。从Android应用层角度来说是客户端和服务端通信的媒介。可想而知其重要性了,到这里我相信很多人都用过,在bindService的时候服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象我们就可以获取我们想要的数据。
我们知道在Android中进行进程中的通信只有一种方式,那就是在AndroidMenifest文件中给四大组件(Activity,Service,Receiver,ContentProvider)指定android:process属性,这是在一个应用之间,让不同的组件独立于一不同的进程。也就实现了多进程通信了,比如说我可以在我的Activity配置文件中加入android:process=romove属性这样就让我的这个Activity运行在一个独立的进程里面了,此时让他和service通信也就实现了IPC。这和在不同的应用之间进行进程间通信是一样的,这点需要读者注意,理解清楚。读者在网上可以看到很多通过配置android:process属性在一个应用中来实现IPC其实和我刚刚说的不同应用都是一样的。 为了让读者看起来更清楚点,我们采用在不同的应用来进行进程间通信。
Android中IPC方式主要有哪些?
1.Bundle,Intent
2.文件共享
3.AIDL
4.Messenger
5.ContentProvider
6.Socket
接下来会一一讲解,本文主要是讲解关于Android中的IPC(重点:Binder机制),关于一些服务的基本概念将不再叙述,可以参考网上的其他文章。在Android开发中Binder主要是用于服务中,接下来我们先来看一个案例,通过案例来分析。先从大家熟悉的AIDL(Android Interface Definition Language)说起。
新建项目MyProject,里面包含主Activity MainActivity.java,MyService.java,ImiddlePerson.aidl.
MainActivity.java如下:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
MyService.java如下:
public class MyService extends Service {
@Override
public IBinder onBind(Intent arg0) {
return new MyBind();
}
private class MyBind extends ImiddlePerson.Stub{
@Override
public String revers(String str) throws RemoteException {
return str.toUpperCase();
}
@Override
public void print() throws RemoteException {
System.out.println("print...");
}
@Override
public int sun(int a, int b) throws RemoteException {
return a+b;
}
}
}
ImiddlePerson.aidl.如下:
package com.xyy.demo;
interface ImiddlePerson
{
String revers(String str);
void print();
int sun(int a,int b);
}
从上面代码可以看出来,代码异常简单,我们建立了ImiddlePerson.aidl.文件,在文件之中我们声明了三个简单的方法,revers方法用于客户端调用传替参数并将其转成大写。print方法无参只是打印了一句字符串,sum方法用户客户端传替两个参数过来将其相加。关于AIDL文件你需要注意,里面没有修饰符一说,所以不能写修饰符,不然就会报错,还有虽然在同一个包下,但是还是需要导包,这就是AIDL文件的特殊之处,读者需注意。
接下来你会在gen目录下看到生成的ImiddlePerson .java文件,代码如下:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: C:\\TouchTest\\BadgeViewTest\\src\\com\\xyy\\demo\\ImiddlePerson.aidl
*/
package com.xyy.demo;
public interface ImiddlePerson extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
com.xyy.demo.ImiddlePerson {
private static final java.lang.String DESCRIPTOR = "com.xyy.demo.ImiddlePerson";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.xyy.demo.ImiddlePerson interface,
* generating a proxy if needed.
*/
public static com.xyy.demo.ImiddlePerson asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.xyy.demo.ImiddlePerson))) {
return ((com.xyy.demo.ImiddlePerson) iin);
}
return new com.xyy.demo.ImiddlePerson.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_revers: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.revers(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_print: {
data.enforceInterface(DESCRIPTOR);
this.print();
reply.writeNoException();
return true;
}
case TRANSACTION_sun: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.sun(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.xyy.demo.ImiddlePerson {
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;
}
@Override
public java.lang.String revers(java.lang.String str)
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(str);
mRemote.transact(Stub.TRANSACTION_revers, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void print() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_print, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public int sun(int a, int b) 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);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_sun, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_revers = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_print = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_sun = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
public java.lang.String revers(java.lang.String str)
throws android.os.RemoteException;
public void print() throws android.os.RemoteException;
public int sun(int a, int b) throws android.os.RemoteException;
}
可以看到系统为我们自动生成的代码。这就是AIDL文件。
我们发现我们的ImiddlePerson是继承于IInterface接口,接着可以看到
public static abstract class Stub extends android.os.Binder implements com.xyy.demo.ImiddlePerson
到这里我们应该明白了。上面MyService.java里面的代码了。(不再叙述)。
接着我们在Manifest文件之中配置我们的MyService并配置上我们的action
<service android:name="com.xyy.demo.MyService">
<intent-filter>
<action android:name="com.xyy.demo.MyService"/>
</intent-filter>
</service>
将其部署到我们的手机,此时一个简单的远程服务以及部署到我们的手机上了。
接下来新建项目MyProject2客户端应用,这个就比较简单了,首先你需要将之前的AIDL文件copy过来,记住连同包名一起,而且一点不能出错,否则定会反序列化失败。接着在我们的主Activity里面完成如下代码:
/**
*
* @author xyy
*
*/
public class MainActivity extends Activity {
private ImiddlePerson iMiddPerson;
public static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
}
/**
* 绑定方式开启服务
* @param view
*/
public void bind(View view){
Intent intnet = new Intent();
intnet.setAction("com.xyy.demo.MyService");
bindService(intnet, new MyConn(), BIND_AUTO_CREATE);
}
private class MyConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMiddPerson = ImiddlePerson.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
/**
* 调用服务里面的方法
*/
public void call(View view){
try {
Log.d(TAG, iMiddPerson.revers("android"));
Log.d(TAG, iMiddPerson.sun(20, 30)+"");
iMiddPerson.print();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
可以看到我们以绑定的方式启动服务,在绑定成功后会拿到右Binder通过转换后的AIDL对象iMiddlePerson,最后调用方法。
打印结果如下:
09-17 04:35:38.450: D/MainActivity(20584): ANDROID
09-17 04:35:38.450: D/MainActivity(20584): 50
09-17 04:35:38.450: I/System.out(20075): print...
这样我们的一个通过AIDL的方式的远程服务也就结束了,(如果不懂没关系,下面可以叙述。)可以发现代码特别简单,实现起来也很快,几乎不需要太多逻辑。。。。其实不然,IPC哪有这样简单,这只不过我写的特别简单的一个案例,接下来我们就开始分析。