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