android中的IPC机制

前言:碎片化学习是不好的习惯,必须整理总结成自己的知识体系

一.进程,线程

1.进程与线程之间的关系

从操作系统的角度来说:进程是系统资源分配的基本单位,线程是CPU调度最小的调度单位.一个进程可以包含多个线程。

从应用进程的角度来说:一个应用进程被Zygote fork出来,就已经有了一些默认的线程,比如最重要的a)主线程:应用的生命周期函数都是在主线程中调用;b)binder线程:binder线程是应用中最重要的基础线程,它负责与ams跨进程沟通,完成组件的创建,启动,销毁,同时请求系统的服务,如JobSchecduleService.

eg-1:system_server进程中的众多线程

system 2050 632 2381384 148564 unix_strea 0000000000 S system_server

system 2052 2050 2381384 148564 do_sigtime 0000000000 S Signal Catcher

system 2054 2050 2381384 148564 futex_wait 0000000000 S ReferenceQueueD

system 2056 2050 2381384 148564 futex_wait 0000000000 S FinalizerDaemon

system 2058 2050 2381384 148564 futex_wait 0000000000 S FinalizerWatchd

system 2060 2050 2381384 148564 futex_wait 0000000000 S HeapTaskDaemon

system 2593 2050 2381384 148564 binder_thr 0000000000 S Binder:2050_1

system 2600 2050 2381384 148564 binder_thr 0000000000 S Binder:2050_2

system 2938 2050 2381384 148564 SyS_epoll_ 0000000000 S android.bg

system 2939 2050 2381384 148564 SyS_epoll_ 0000000000 SActivityManager

system 2940 2050 2381384 148564 SyS_epoll_ 0000000000 S android.ui

system 2941 2050 2381384 148564 SyS_epoll_ 0000000000 S ActivityManager

system 2942 2050 2381384 148564 SyS_epoll_ 0000000000 S android.fg

system 2943 2050 2381384 148564 inotify_re 0000000000 S FileObserver

system 2944 2050 2381384 148564 SyS_epoll_ 0000000000 S android.io

system 2945 2050 2381384 148564 SyS_epoll_ 0000000000 S android.display

system 2946 2050 2381384 148564 futex_wait 0000000000 S CpuTracker

system 2947 2050 2381384 148564 SyS_epoll_ 0000000000 SPowerManagerSer

system 2948 2050 2381384 148564 pm_get_wak 0000000000 S system_server

system 2949 2050 2381384 148564 futex_wait 0000000000 S BatteryStats_wa

system 3005 2050 2381384 148564 SyS_epoll_ 0000000000 SPackageManager

eg-2:一个应用进程中的众多线程

USER PID PPID VSIZE RSS WCHAN PC NAME

u0_a55 4104 308 772924 28336 sys_epoll_ b6d2999c S com.xxx.xxx

u0_a55 4109 4104 772924 28336 do_sigtime b6d29c70 S Signal Catcher   这个线程是用来捕获linux信号和做一些后续处理的。如SIGBUS

u0_a55 4110 4104 772924 28336 poll_sched b6d29b8c S JDWP

u0_a55 4111 4104 772924 28336 futex_wait b6d00644 S ReferenceQueueD

u0_a55 4112 4104 772924 28336 futex_wait b6d00644 S FinalizerDaemon

u0_a55 4113 4104 772924 28336 futex_wait b6d00644 S FinalizerWatchd

u0_a55 4114 4104 772924 28336 futex_wait b6d00644 S HeapTaskDaemon

u0_a55 4115 4104 772924 28336 binder_thr b6d29ac8 S Binder:4104_1   binder线程池

u0_a55 4116 4104 772924 28336 binder_thr b6d29ac8 S Binder:4104_2

u0_a55 4121 4104 772924 28336 inotify_re b6d2a978 S FileObserver

u0_a55 4124 4104 772924 28336 sys_epoll_ b6d2999c S local_job_dispa

u0_a55 4164 4104 772924 28336 sys_epoll_ b6d2999c S remote_job_disp

2.Android中的多线程模型

[2.1]Android 中的多进程一般指一个应用中存在多个进程的情况。我们可以通过指定

android :process属性,让一个应用的不同组件运行在指定进程。

以“:”开头的进程属于当前应用的私有进程,其它应用的组件不能通过ShareUID与它跑在一个进程

指定全名的进程属于全局进程。其它应用的组件可以通过ShareUID与它跑在一个进程.

[2.2]多进程带来的问题

Android 为每个应用进程分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,所以运行在不同进程中的四大组件是不同通过内存来共享数据。多进程之间组件一定要共享数据吗?不一定,但是我们只要使用多进程模型,一般都是需要跨进程通信的。

一般来说,使用多进程会造成以下几个问题:(参考任玉刚老师的书):

1.静态成员和单例完全失效

解释:同一个类在不同进程中拥有不同的地址空间,虽然类名,类变量一样,但是分配的地址不一样,所以同一个类(指类名称)在不同的进程中是完全不同的类了。

2.线程同步机制失效

解释:不同地址空间,不同类,所以锁对象,锁全局类都不同了

3.Application会多次创建

当指定一个组件跑在另外一个进程中时,Zygote会fork新的进程,当然会分配独立的虚拟机,整个过程就是启动了一个应用的过程。创建了一个新的Application对象。

总结:运行在同一个进程中的组件属于同一个虚拟机同一个Application。多进程中不同进程的组件拥有不同的虚拟机,不同的Application,不同的内存地址。

[2.3]多进程之间的通信

由于不同进程所拥有的地址是两块不同的地址空间,所以不能直接通过共享内存共享数据了。直接上Linux,Android的通信方式:

Linux常用跨进程通信方式:管道,信号量,共享内存,socket 。。。

Android常用跨进程通信方式:Intent ,共享文件,SharedPreferences,Binder,socket,基于Binder的Messenger.下面详细学习进程间的通信方式。

二,进程之间的通信方式

1.Binder

Binder

是安卓中最重要的IPC通信方式了,一个应用进程的创建,应用进程中组件的生命周期的调度,系统服务的使用…处处都是binder,从而也可以看出,

安卓中进程间的通信是非常频繁的,一句话来说,在Binder通信机制的强有力支持下,安卓进程间进行了友好的数据交互。

Binder

Binder印象

Binder是进程间通信的一种架构,这个架构分为服务端接口,客户端接口,Binder驱动

1.1

aidl工具会把应用程序中的aidl文件生成一个非常复杂的java类。拆分

由aidl生成的java文件(一个接口文件;该接口文件中嵌套一个抽象的类Stub,该类实现外边的接口文件,没有实现具体接口方法,接口方法由其子类

实现;抽象类中有一个proxy类,该类是抽象Stub的代理类,同时也实现了最外层的接口文件,proxy文件的作用是将客户端的输入包装成统一形式,

具体的业务实现在服务端

Stub的子类中)

由aidl文件系统帮我们生成的三个文件,

a) 接口 IXXX extends IInterface

b) abstract class Stub extends Binder imp IXXX

该类重载了binder的 OnTransact方法:

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags){

……………..

case TRANSACTION_startScan:

{

data.enforceInterface(DESCRIPTOR);  //校验

java.lang.String[] _arg0;

_arg0= data.createStringArray(); //读取参数

com.miui.guardprovider.aidl.IVirusObserver _arg1;

_arg1= com.miui.guardprovider.aidl.IVirusObserver.Stub.asInterface(data.readStrongBinder());//读取参数

boolean _arg2;

_arg2= (0!=data.readInt());//读取参数

int _result = this.startScan(_arg0, _arg1, _arg2);//调用服务端服务函数

reply.writeNoException();

reply.writeInt(_result);//返回给客户端的结果

return true;

}

……………………….

}

c) static class Proxy imp IXXX

服务端的代理类,跨进程通信中,客户端使用代理类访问服务。要想使用服务端,首先要获取服务端在Binder驱动中对应的mRemote变量的引用

int

startScan(java.lang.String[] paths,

com.miui.guardprovider.aidl.IVirusObserver virusObserver, boolean

isCloud) 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.enforceInterface(DESCRIPTOR)

_data.writeStringArray(paths);//写入参数

_data.writeStrongBinder((((virusObserver!=null))?(virusObserver.asBinder()):(null)));////写入参数

_data.writeInt(((isCloud)?(1):(0)));////写入参数

mRemote.transact(Stub.TRANSACTION_startScan, _data, _reply, 0);//调用

_reply.readException();

_result = _reply.readInt();

}

finally {

_reply.recycle();

_data.recycle();

}

return _result;

}

一个完整的通信过程:客户端获取服务端的Binder对象引用 ,然后调用相关服务。

客户端得到binder对象引用,通过 transact()将调用目标函数的参数写入包裹,再通过底层binder驱动转发到服务端,经过OnTransact分发调用具体实现,最终调用到service中的具体业务实现。

a)客户端如何获得服务端的Binder对象引用

系统服务:使用getSystemService()方法获取的服务,系统服务一般不使用Service类实现,一般都是基于binder类,可以仅使用Binder类扩展系统服务。系统服务由ServiceManager来管理,系统服务在使用前向ServiceManager进行注册,客户端使用服务时向ServiceManager获取服务的引用。注意ServiceManager也是一个系统服务,它的代理架构跟其他系统服务一样的

应用程序自定义服务:客户端服务则必须基于Service类来编写,通过bindService获取相关服务的binder引用,asInterface提供了统一的查询接口,如果IPC通信,返回Proxy对象引用,如果进程内部使用服务则返回 Stub的具体实现类对象引用。

自定义服务需要注册吗?

两种获取方式最终都是通过AMS服务查询得到服务端的Binder引用

b)binder 和 Service的关系

在Service中实现具体的业务,然后通过Binder载体将具体业务的实现暴露给客户端。binder框架在整个通信过程中不做具体的业务实现,整

个框架只负责运输数据,将客户端的请求精确传达给服务端,同时将服务端的处理结果传递给客户端。只做数据传输不做具体业务

c)Framework层的binder架构与native层的binder架构之间的关系

framework

层的binder通过JNI调用native的binder架构,所以framework层(Java层)对native层(c/c++层)进行了一层包

装,提供给应用层进行调用。native层binder是C/S架构,framework层的架构与相关类的设计原理与native层类似。所以理解了任

意一层都很好理解另一层.

1.2   binder的死亡监听

onServiceDisconnected()

1.3   aidl的权限验证

androidmanifest.xml文件中定义permission.在onBind函数中进行权限校验

2.ContentProvider

ContentProvider底层实现是binder

ContentProvider的唯一标识android:authorities=”xxx”

ContentProvider是对外界提供数据的,为了对数据进行保护,所以需要相关权限验证

第三方应用进程访问时需要声明访问权限。(由此想到,不管是service,ContentProvider,还是broadcast,对访问者来说他们都是一种受保护的资源,所以整个访问过程中需要添加权限校验。不能随便访问。要安全的获取数据和服务,android是一个开源的系统,在开源的系统上防范变得很重要)

contentprovider是提供数据访问的接口,至于底层数据的组织可以由Sqlite数据库存储组织,内存List组织,MatrixCursor组织

contentprovider通过Uri来区分外界要访问的数据集合

一般为了区分要访问的数据:定义唯一的Uri 和Uri_code,再通过UriMatcher将两者关联起来。

数据访问流程:根据Uri—>Uri_code—>表名—>访问数据

需要注意的是CRUP四大方法是存在多线程并发访问,因此方法内部要做好线程同步。

如果底层数据是一块内存的话,如List,特别要做好数据同步。

3.Socket

通过Socket套接字进行跨进程的通信,eg:让服务端循环监听一个端口,客户端请求连接服务端。Socket本身支持传输任意字节流

4.文件

    原文作者:笑羋
    原文地址: https://www.jianshu.com/p/c9ba8a71dd60
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞