Android进程间通信

  • 格式:pdf
  • 大小:150.80 KB
  • 文档页数:3

Android进程间通信

进程间通信(ipc)

IPC⽅法总是产⽣客户/服务端模式的调⽤,也即是客户端组件(Activity/Service)持有服务端Service的组件,只能是客户端主动调⽤服务端

的⽅法,服务端⽆法反过来调⽤客户端的⽅法,因为IPC的另⼀端Service⽆法获取客户端的对象。

binder

Binder 是⼀种进程间通信机制。安卓中跨进程通讯就是通过binder。当绑定服务的时候会返回⼀个binder对象,然后通过他进⾏多进程间的

通信。Binder只需要⼀次数据拷贝,性能上仅次于共享内存。

在 Android 系统中,这个运⾏在内核空间,负责各个⽤户进程通过 Binder 实现通信的内核模块就叫 Binder 驱动(Binder Dirver)。

Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是操作系统中⼀种内存映射的⽅法。内存映射简单的讲就是将⽤户空间

的⼀块内存区域映射到内核空间。映射关系建⽴后,⽤户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改

也能直接反应到⽤户空间。

内存映射能减少数据拷贝次数,实现⽤户空间和内核空间的⾼效互动。两个空间各⾃的修改能直接反映在映射的内存区域,从⽽被对⽅空间

及时感知。也正因为如此,内存映射能够提供对进程间通信的⽀持。

Binder IPC 正是基于内存映射(mmap)来实现的

Binder 通信中的代理模式

我们已经解释清楚 Client、Server 借助 Binder 驱动完成跨进程通信的实现机制了,但是还有个问题会让我们困惑。A 进程想要 B 进程中某

个对象(object)是如何实现的呢?毕竟它们分属不同的进程,A 进程 没法直接使⽤ B 进程中的 object。

前⾯我们介绍过跨进程通信的过程都有 Binder 驱动的参与,因此在数据流经 Binder 驱动的时候驱动会对数据做⼀层转换。当 A 进程想要获

取 B 进程中的 object 时,驱动并不会真的把 object 返回给 A,⽽是返回了⼀个跟 object 看起来⼀模⼀样的代理对象 objectProxy,这个

objectProxy 具有和 object ⼀摸⼀样的⽅法,但是这些⽅法并没有 B 进程中 object 对象那些⽅法的能⼒,这些⽅法只需要把把请求参数交给

驱动即可。对于 A 进程来说和直接调⽤ object 中的⽅法是⼀样的。

当 Binder 驱动接收到 A 进程的消息后,发现这是个 objectProxy 就去查询⾃⼰维护的表单,⼀查发现这是 B 进程 object 的代理对象。于是

就会去通知 B 进程调⽤ object 的⽅法,并要求 B 进程把返回结果发给⾃⼰。当驱动拿到 B 进程的返回结果后就会转发给 A 进程,⼀次通信

就完成了

其实进程间通信就是为了实现数据共享。⼀个程序不同组件在不同进程也叫多进程,和俩个应⽤没有本质区别。使⽤process属性可以实现

多进程,但是会带来很多⿇烦,主要原因是共享数据会失败,弊端有:静态和单利失效,同步失效,sharedprefer也变的不可靠等问题。

多进程通信的⽅式

1.使⽤intent的附加信息extras来传递,通过bundle,传递的是bundle⽀持的类型,⽐如基本数据类型、实现pracellable或serializeable的对

/**指定包名和带包名的Activity的名字*/

ComponentName componentName = new ComponentName("com.example.androidaidl", "com.example.androidaidl.MainActivity");

Intent intent = new Intent();

intent.putExtra("id", 1001);

intent.setComponent(componentName);

startActivity(intent);

2.使⽤⽂件共享,序列化或是sharedpre,不过不适⽤于读写并发的操作

3.⼴播:Android的⼴播是系统级的,只要传递的Action⼀样,就可以接收到其他进程⼴播的消息,⼴播中可以通过Intent传递数据。

4.scheme协议是android中的⼀种页⾯内跳转协议,通过定义⾃⼰的scheme协议,可以⾮常⽅便跳转app中的各个页⾯,并且传递数据,还

是可以通过H5页⾯跳转指定页⾯等。

5.ContentProvider(进程间数据共享)和message⼀样,底层也是binder,除了oncreate⽅法其他⽅法(crud)都是运⾏在bindler线程⾥。所

以在oncerate⾥不能做耗时操作。Android本⾝就提供了不少的ContentProvider访问,⽐如联系⼈、相册等。 访问ContentProvider,需要通

过Uri,需要以“content://”开头。在其他应⽤访问通过uri(主机名):

ContentResolver resolver = getActivity().getContentResolver();

/**com.mh.getdata/stock这个要和Provider所在进程中添加的Uri⼀致*/

Uri uri = Uri.parse("content://com.mh.getdata/stock");

Cursor cursor = resolver.query(uri, null, null, null, null);

常规通讯

只有允许不同应⽤的客户端⽤ IPC ⽅式访问服务,并且想要在服务中处理多线程(多任务)时,才有必要使⽤ AIDL。 如果您不需要执⾏跨

越不同应⽤的并发 IPC,就应该通过实现⼀个 Binder 创建接⼝;或者,如果您想执⾏ IPC,但根本不需要处理多线程,则使⽤ Messenger

类来实现接⼝。⽆论如何,在实现 AIDL 之前,请您务必理解绑定服务。

1.通过 Messenger进⾏传递(handler),在远程服务⾥创建handler(接收客户端发送的消息)、 Messenger对像,在onbind⾥返回

( Messenger.getbinder)。在客户端绑定服务,拿着 Messenger对象发消息(可以⽤bundle)。在远程服务的handlermessage⽅法就会收到。他是⼀个个处理的,如果⼤量并发请求⽤aidl, Messenger底层就是aidl

在客户端中创建⼀个Messenger。然后,当客户端收到 onServiceConnected() 回调时,会向服务发送⼀条 Message,并在

其 send() ⽅法的 replyTo 参数中包含客户端的 Messenger。

注意:Messenger和Message是俩个东西

public void sayHello(View v) {

if (!mBound) return;

Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);

try {

mService.send(msg);

} catch (RemoteException e) {

e.printStackTrace();

}

}

2.直接使⽤Binder对象:缺点是这种⽅式不能进⾏跨进程,跨应⽤程序的函数调⽤。只能实现在同⼀个进程之中,同⼀个应⽤程序之中的不

同的组件之间通讯。

⽤法:继承Binder,然后在service⾥return

继承Binder⽤它的对象返回,客户端将bind对象强转成⾃定义Bind

AIDL

Android interface definition language (android接⼝定义语⾔) , ⽤来跨进程的访问⽅法。

aidl操作步骤:

1.在两个项⽬中新建普通⽂件(new ->General->File),后缀名改成(aidl),客户端和服务端中这个⽂件所在的包名要保持⼀致,内容也

要⼀样

编译之后, 会在gen⽬录下,⾃动产⽣同名的,后缀为 java 的⽂件。⾥⾯有我们要⽤到的 Stub类。

public static abstract class Stub extends android.os.Binder implements com.example.aidl.AidlFunctions

2.在接⼝⽂件AIDLFunctions.aidl中,我们定义⼀个⽅法 show

interface AidlFunctions{

void show();

}

3.AIDL的使⽤,需要⼀个Service配合,所以我们在服务端还要声明⼀个Service

public class AIDLService extends Service {

//stub就是系统⾃动产⽣的

AidlFunctions.Stub binder;

@Override

public void onCreate() {

// TODO Auto-generated method stub

super.onCreate();

}

@Override

public IBinder onBind(Intent intent) {

// TODO Auto-generated method stub

binder = new AidlFunctions.Stub() {

@Override

//这⾥是我们在接⼝中声明的⽅法的实现

public void show() throws RemoteException {

// TODO Auto-generated method stub

System.out.println("--------------------收到----------------------");

}

};

return binder;

}

}

4.客户端:

//绑定服务,要⽤到ServiceConnection

private ServiceConnection serviceConnection;

//⾃定义的接⼝,和服务端⼀样

private AidlFunctions aidlFunctions;

serviceConnection = new ServiceConnection() {

@Override