IPC(跨进程通信,进程间通信,Inter-Process Communication):指两个进程之间进行数据交换的过程。
可以实现进程间通信。
Binder类实现了IBinder接口。
也可以理解为是一种虚拟的物理设备,设备驱动是/dev/binder。
从Android Framework角度:Binder是ServiceManager连接各种Manager和相应ManagerService的桥梁;
从Android应用层角度:Binder是客户端和服务端进行通信的媒介。如bindService,服务端会返回一个包含了服务端调用的Binder对象,客户端可以通过它获取服务端提供的服务或数据。
相比其它IPC方式,优点在于:
i>高性能
从数据拷贝次数来看Binder只需要进行一次内存拷贝,而管道、消息队列、Socket都需要两次,共享内存不需要拷贝,Binder的性能仅次于共享内存。
ii>稳定性
上面说到共享内存的性能优于Binder,那为什么不适用共享内存呢,因为共享内存需要处理并发同步问题,控制负责,容易出现死锁和资源竞争,稳定性较差。而Binder基于C/S架构,客户端与服务端彼此独立,稳定性较好。
iii>安全性
我们知道Android为每个应用分配了UID,用来作为鉴别进程的重要标志,Android内部也依赖这个UID进行权限管理,包括6.0以前的固定权限和6.0以后的动态权限,传荣IPC只能由用户在数据包里填入UID/PID,这个标记完全
是在用户空间控制的,没有放在内核空间,因此有被恶意篡改的可能,因此Binder的安全性更高。
IBinder接口的实现类,该类用以提供客户端用来与服务进行交互的编程接口,该接口可以通过三种方法定义接口:
场景:服务仅供本地应用使用,不需要跨进程工作
原理:通过扩展 Binder 类并从 onBind() 返回它的一个实例来创建接口。客户端收到 Binder 后,可利用它直接访问 Binder 实现中以及Service 中可用的公共方法。
实现:
1.创建BindService服务端,继承自Service并在类中,创建一个实现IBinder 接口的实例对象并提供公共方法给客户端调用
2.从 onBind() 回调方法返回此 Binder 实例。
3.在客户端中,从 onServiceConnected() 回调方法接收 Binder,并使用提供的方法调用绑定服务。
public class LocalService extends Service{
private final static String TAG = "wzj";
private int count;
private boolean quit;
private Thread thread;
private LocalBinder binder = new LocalBinder();
/**
* 创建Binder对象,返回给客户端即Activity使用,提供数据交换的接口
*/
public class LocalBinder extends Binder {
// 声明一个方法,getService。(提供给客户端调用)
LocalService getService() {
// 返回当前对象LocalService,这样我们就可在客户端端调用Service的公共方法了
return LocalService.this;
}
}
/**
* 把Binder类返回给客户端
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Service is invoke Created");
thread = new Thread(new Runnable() {
@Override
public void run() {
// 每间隔一秒count加1 ,直到quit为true。
while (!quit) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
}
});
thread.start();
}
/**
* 公共方法
* @return
*/
public int getCount(){
return count;
}
/**
* 解除绑定时调用
* @return
*/
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "Service is invoke onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.i(TAG, "Service is invoke Destroyed");
this.quit = true;
super.onDestroy();
}
}
public class BindActivity extends Activity {
protected static final String TAG = "wzj";
Button btnBind;
Button btnUnBind;
Button btnGetDatas;
/**
* ServiceConnection代表与服务的连接,它只有两个方法,
* onServiceConnected和onServiceDisconnected,
* 前者是在操作者在连接一个服务成功时被调用,而后者是在服务崩溃或被杀死导致的连接中断时被调用
*/
private ServiceConnection conn;
private LocalService mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bind);
btnBind = (Button) findViewById(R.id.BindService);
btnUnBind = (Button) findViewById(R.id.unBindService);
btnGetDatas = (Button) findViewById(R.id.getServiceDatas);
//创建绑定对象
final Intent intent = new Intent(this, LocalService.class);
// 开启绑定
btnBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "绑定调用:bindService");
//调用绑定方法
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
// 解除绑定
btnUnBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "解除绑定调用:unbindService");
// 解除绑定
if(mService!=null) {
mService = null;
unbindService(conn);
}
}
});
// 获取数据
btnGetDatas.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mService != null) {
// 通过绑定服务传递的Binder对象,获取Service暴露出来的数据
Log.d(TAG, "从服务端获取数据:" + mService.getCount());
} else {
Log.d(TAG, "还没绑定呢,先绑定,无法从服务端获取数据");
}
}
});
conn = new ServiceConnection() {
/**
* 与服务器端交互的接口方法 绑定服务的时候被回调,在这个方法获取绑定Service传递过来的IBinder对象,
* 通过这个IBinder对象,实现宿主和Service的交互。
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "绑定成功调用:onServiceConnected");
// 获取Binder
LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
mService = binder.getService();
}
/**
* 当取消绑定的时候被回调。但正常情况下是不被调用的,它的调用时机是当Service服务被意外销毁时,
* 例如内存的资源不足时这个方法才被自动调用。
*/
@Override
public void onServiceDisconnected(ComponentName name) {
mService=null;
}
};
}
}
可以实现任意两个终端/进程的通信。
在创建时分配一个page大小的内存,缓存区大小比较有限;
信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。
当它的值大于0时,表示当前可用资源的数量;
当它的值小于0时,其绝对值表示等待使用该资源的进程个数。
注意,信号量的值仅能由PV操作来改变。
一般来说,信号量S³0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S£0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。
不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
默认进程的进程名为包名。
我们可以在eclipse的DDMS视图中查看进程信息。
"com.test.SecondActivity"
android:label="@string/app_name"
android:process=":remote"/>
//当SecondActivity启动时,系统会为它创建一个单独的进程,进程名为:`com.test:remote`。对于以`:`开头的进程属于私有进程,其他组件不可和他在一个进程中跑;否则为全局进程,如:`android:process="com.test.remote"`,其它应用通过ShareUID方式可以和它跑在同一个进程中。(Android为每个应用分配一个唯一的UID,相同UID的应用才能共享数据。所以两个应用要跑在同一个进程中必须保证:有相同的ShareUID并且签名相同,这样才可以互相访问对方的私有数据:共享data、组件信息、内存数据。
//对于MainActivity,没有指定进程名,作为入口Activity,它运行在默认进程中,进程名是包名。
Android为每个进程都分配了一个独立的VM,不同VM在内存上有不同的地址空间。不同进程进行的操作不会影响到其它进程。所以一般不采用多进程。
不同进程中的四大组件无法通过内存共享数据。
Android为每一个进程分配了一个独立的虚拟机,不同的VM在内存上有不同的地址空间,导致不同的VM访问同一个类的对象会产生多份副本。
不同进程在不同内存中,锁对象(不是同一个对象)无法保证线程同步。
SharedPreferences不支持两个进程同时执行写操作,会丢失数据。
在不同的VM上,会创建不同的Application。
在Intent中附加extras来传递信息。
Activity、Service、Receiver都支持Intent中传递Bundle数据。
Bundle实现了Parcelable接口,可以在不同进程间传输。
Bundle中的数据必须能够被序列化:如基本类型、实现了Parcellable接口的对象、实现了Serializable接口的对象或者是Android支持的特殊对象。
支持跨进程访问。
适合用于数据同步要求不高的进程间通信,要妥善处理并发读/写的问题。
在面对高并发的读/写访问时:
i>文件支持多个进程同时写,所以高并发情况下读取的内容可能不是最新的。ii>SharedPreferences:由于其读写的缓存策略,即在内存中有一份SharePreferences文件的缓存,多并发情况下,有很大几率丢失数据,不建议用于跨进程通信。
注意:handler是跨线程!
Messenger是轻量级的IPC方案,它的底层实现是AIDL。Messenger封装了AIDL,一次处理一个请求,不存在多线程并发的问题。
在不同的进程中共传递Message对象(Handler中的Messager,因此 Handler 是 Messenger 的基础)。
Messenger和Message实现了Parcelable接口。
Messenger载体:
what、arg1、arg2、Bundle、replyTo、object(只有系统定义的实现了Parcelable接口的对象才可以跨进程传递,自定义的实现了Parcelable接口的对象不可通过该字段传输)
使用 Messenger 为服务创建接口,客户端就可利用 Message 对象向服务发送命令。同时客户端也可定义自有 Messenger(在Message中可以存放我们需要传递的数据),以便服务回传消息。
因为 Messenger 会在单一线程中创建包含所有请求的队列,以串行的方式处理客户端发来的消息,所以不需要担心线程安全问题。
举例1:
通过Messenger来传输Message,Message中能用的载体只有what、arg1、arg2、Bundle以及replyTo。object在跨进程通信中实用性降低,一般用Bundle来传递大量的数据类型。
通过Handle来实现Messenger的进程 间通信:
//1,服务端进程(Android进程)
public class MessengerService extends Service{//在服务端创建一个Service来处理客户端的连接请求
private static final String TAG = "MessengerService";
private static class MessengerHandler extends Handler{//创建一个Handler并通过它来创建一个Messenger对象
@Override
public void handleMessage(Message msg){
switch(msg.what){
case MyConstants.MSG_FROM_CLIENT:
Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
break;
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent){//在Service的onBind中返回这个Messenger对象底层的Binder即可
return mMessenger.getBinder();
}
}
需要 注册service,并让其运行在单独的进程中:
<service
android:name="com.ret.MessengerService"
android:process=":remote">
//2,客户端进程(Android进程)
//首先绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象。
//如果需要服务端能回应客户端,还需要创建一个Handler并创建一个新的Messenger,把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。
public class MessengerActivity extends Activity{
private static final String TAG = “MessengerActivity”;
private Messenger mService;
private SeerviceConnection mConnection = new ServiceConnection(){
public void onServiceConnected(ComponentName className, IBinder service){
mService = new Messenger(service);
Message msg = Message.obtain(null,MyConstants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString(“msg”,”hello,this is client”);
msg.setData(data);
try{
mService.send(msg);
}catch(RemoteException e){
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className){
}
};
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
...
Intent intent = new Intent(this,MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy(){
unbindeService(mConnection);
super ...
}
}
举例二:
1.服务实现一个 Handler,由其接收来自客户端的每个调用的回调
2.Handler 用于创建 Messenger 对象(对 Handler 的引用)
3.Messenger 创建一个 IBinder,服务通过 onBind() 使其返回客户端
4.客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,然后使用Messenger将 Message 对象发送给服务
5.服务在其 Handler 中(在 handleMessage() 方法中)接收每个 Message
public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
private static final String TAG ="wzj" ;
/**
* 用于接收从客户端传递过来的数据
*/
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Log.i(TAG, "thanks,Service had receiver message from client!");
break;
default:
super.handleMessage(msg);
}
}
}
/**
* 创建Messenger并传入Handler实例对象
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* 当绑定Service时,该方法被调用,将通过mMessenger返回一个实现
* IBinder接口的实例对象
*/
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "Service is invoke onBind");
return mMessenger.getBinder();
}
}
public class ActivityMessenger extends Activity {
/**
* 与服务端交互的Messenger
*/
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
/**
* 实现与服务端链接的对象
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
/**
* 通过服务端传递的IBinder对象,创建相应的Messenger
* 通过该Messenger对象与服务端进行交互
*/
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false;
}
};
public void sayHello(View v) {
if (!mBound) return;
// 创建与服务交互的消息实体Message
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
//发送消息
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenager);
Button bindService= (Button) findViewById(R.id.bindService);
Button unbindService= (Button) findViewById(R.id.unbindService);
Button sendMsg= (Button) findViewById(R.id.sendMsgToService);
bindService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("zj","onClick-->bindService");
//当前Activity绑定服务端
bindService(new Intent(ActivityMessenger.this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
});
//发送消息给服务端
sendMsg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sayHello(v);
}
});
unbindService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Unbind from the service
if (mBound) {
Log.d("zj","onClick-->unbindService");
unbindService(mConnection);
mBound = false;
}
}
});
}
}
注意:把Service放在单独的进程中:
<service android:name=".messenger.MessengerService"
android:process=":remote"
/>
Messenger以串行的方式处理客户端发来的消息,对于大量的并发请求需要一个个处理,效率低。
AIDL是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口.编译器可以通过扩展名为aidl的文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的。
Android系统中的进程之间不能共享内存,Android通过AIDL来公开服务的接口,将这种可以跨进程访问的服务称为AIDL服务。
AIDL可以生成进程间的接口的代码,诸如service可能使用的接口。
在Service(服务)的onBind(Intent intent)方法中返回mBinder实现了aidl接口的对象
实现跨进程的方法调用。
想让服务同时处理多个请求,则应该使用 AIDL。
使用这些类型时不需要import声明。
List:只支持ArrayList,里面每个元素都必须被AIDL支持。
Map:只支持HashMap,里面每个元素都必须被AIDL支持,包括key和value。
Parcelable:所有实现了Parcelable接口的对象。
AIDL:所有的AIDL接口本身也可以在AIDL文件中使用。
(这些类型内所包含的数据成员也只能是简单数据类型,String等其他支持类型)
创建一个Service即可完成多个AIDL接口的工作。
i>在Eclipse Android工程的Java包目录中建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同。
ii>如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(*.java)。
iii>建立一个服务类(Service的子类)。
iv>实现由aidl文件生成的Java接口。
v>在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,
标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。
i>aidl对应的接口名称必须与aidl文件名相同不然无法自动编译。
ii>aidl对应的接口的方法不能加访问权限修饰符,也不能用final,static。
iii>自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中。
iv>在aidl文件中所有非Java基本类型参数必须加上in、out、inout标记,以指明参数是输入参数、输出参数还是输入输出参数。
v>Java原始类型默认的标记为in,不能为其它标记。
Socket分为流式套接字和用户数据报套接字。
注意:
A.不要在主线程访问网络(Android4.0以上设备会抛出异常:android.os.NetworkOnMainThreadException)
B.当Activity退出时,关闭当前Socket
JAVA进程间通信的方法主要有以下几种:
管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。
命名管道克服了管道没有名字的限制,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送 信号给进程本身。
消息队列是消息的链接表,包括Posix消息队列system V消息队列。