本篇Blog主要讲解Android中的IPC机制。我们将通过介绍Android的多进程模式,IPC的基础概念(Serializable,Parcelable,Binder),Android中常用的六种多进程通信机制(Bundle,文件共享,Messenger,AIDL,ContentProvider,Socket),最后分析如何选用合适的IPC方式。
一 Android的多进程模式
(1).开启多进程模式
在Android中使用多进程只有一种方法,那就是给四大组件在Manifest中指定android:process属性。也就是说我们无法给一个线程或一个实体类指定其运行时所在的进程。
在为android:process赋值的时候,我们可以有两种写法。
Android:process=”:remote”
Android:procss=”进程名”
第一种方式会在当前进程名上附加上当前的包名,这是一种简写的方式,另外以”:”开头的进程属于当前应用的私有进程,其他应用组件不可以和它跑在同一个进程.而不是折后在那个私有进程,则可以通过ShareUID方式和它跑在一个进程。Android为每个应用都分配一个唯一的UID,具有相同UID的应用才能共享数据。两个应用通过ShareUID跑在同一个进程中是有要求的,需要两个应用具有相同的ShareUID并且签名相同才可以。
(2).多进程运行机制
使用Android的多进程,是容易造成很多问题的,因此,我们要搞清楚使用多进程会造成什么影响。
我们知道,Android为每个应用分配了一个独立的虚拟机,或者说为每个进程都分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致在不同的虚拟机中房屋一个类的对象会产生多分副本.所有运行在不同进程中的四大组件,只要他们中间通过共享内存来共享数据,都回共享失败,这也是多进程带来的主要影响。
一般来说,使用多进程会造成如下几方面的问题:
1.静态成员和单例模式完全失效(共享内存)
2.线程同步机制完全失效(既然内存不是同一块了,锁对象或锁全局类就没有意义了)
3.SharedPreferences的可靠性降低,因为SharedPreferences不支持两个进程同时去执行读写,因为SharePrefereneces底层功过读写xml实现的。
4.Application会多次创建,因为系统在创建新的进程同时分配独立的虚拟机,其实就是启动一个应用的过程。
由于多进程无法共享内存,为了解决这个问题,所以才有了IPC机制。我们先了解一些IPC的基础概念。
二 IPC基础概念
Serializable和Parcelable接口可以完成对象的序列化过程,当我们需要通过Intent和Binder传输数据时,就需要使用Serializable和Parcelable。还有的时候,我们需要持久化到存储设备上或通过网络传输时,这个时候,也需要使用Serializable来持久化。
(1).Serializable
Serializable是Java所里所提供的一个序列化接口,它是一个空借口,为对象提供的标准序列化和反序列号操作。使用Serializable非常简单,只要在类的声明中指定一个标识即可自动实现默认的序列化过程。
Private static final long serialVersionUID =Value;
其实,这个serialVersionUID 也不是必须的,我么不声明这个serialVersionUID 也可以实现序列化,但是这将会对范序列化过程产生影响(该属性标识当前类和序列化的类相比是否发生了某些变换,比如变量的数量或类型发生变化,这个时候,反序列化会报错)。Serializable 底层采用ObjectOutputStream 和 ObjectInputStream 即可实现。
ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream(“cache.txt”));
out.writeObject(Object);
out.close();
(2).Parcelable
Parcelable也是一个实现序列化与反序列化的接口。
Parcelable 实现序列化也很简单,我也不再赘述了,就简单说一下Parcelable与Serializable的区别吧。Serializable是Java中的序列化接口,使用起来简单但开销很大,序列化和反序列化需要大量I/O操作。而Parcelable是Android中的序列化方式,依你更适合用在Android平台上,她的缺点就是用起来稍微麻烦。。。。但是效率高,是Android推荐的序列化方式,因此首选Parcelable.Parcelable主要用在内存序列化上,而Parcelable对象序列化到存储设备中或者将对象序列化后通过网络传输也都可以的,但是这个过程会稍微复杂,因此这种情况下建议用Serializable。
(3).Binder
Binder是Android中的一个类,它实现了IBinder接口。从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。
接下来就要上代码了。我们有必要了解一下Binder的运行机制,是怎么完成进程间的通信的。
我们新建Book.java,Book.aidl,IBookManager.aidl 三个文件 代码如下:
//Book.java
package cn.tyssen.android.androidstudy;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by Tyssen on 2016/1/18.
*/
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book() {
}
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(bookId);
out.writeString(bookName);
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
public Book createFromParcel(Parcel in) {
return new Book(in);
}
public Book[] newArray(int size) {
return new Book[size];
}
};
private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
@Override
public String toString() {
return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
}
}
//Book.aidl
// Book.aidl
package cn.tyssen.android.androidstudy;
// Declare any non-default types here with import statements
parcelable Book;
//IBookManager.aidl
// Book.aidl
package cn.tyssen.android.androidstudy;
import cn.tyssen.android.aidl.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
以及编译器自动为我们生成的IBookManager.Java:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: G:\\Github\\AndroidStudy\\app\\src\\main\\aidl\\cn\\tyssen\\android\\androidstudy\\IBookManager.aidl
*/
package cn.tyssen.android.androidstudy;
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements cn.tyssen.android.androidstudy.IBookManager
{
private static final java.lang.String DESCRIPTOR = "cn.tyssen.android.androidstudy.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an cn.tyssen.android.androidstudy.IBookManager interface,
* generating a proxy if needed.
*/
public static cn.tyssen.android.androidstudy.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof cn.tyssen.android.androidstudy.IBookManager))) {
return ((cn.tyssen.android.androidstudy.IBookManager)iin);
}
return new cn.tyssen.android.androidstudy.IBookManager.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_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<cn.tyssen.android.androidstudy.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
cn.tyssen.android.androidstudy.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = cn.tyssen.android.androidstudy.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements cn.tyssen.android.androidstudy.IBookManager
{
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.util.List<cn.tyssen.android.androidstudy.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<cn.tyssen.android.androidstudy.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(cn.tyssen.android.androidstudy.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(cn.tyssen.android.androidstudy.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<cn.tyssen.android.androidstudy.Book> getBookList() throws android.os.RemoteException;
public void addBook(cn.tyssen.android.androidstudy.Book book) throws android.os.RemoteException;
}
在本篇博客中,我们大致了解一下Binder是怎么回事(读者也可以分析一下IBookManager.java都是由哪些内容构成的, asInterface,asBinder,onTransact,Poxy#getBookList,Proxy#addBook 分别的作用是什么,在哪里运行的,这些也是学习Binder需要了解的知识),我们会在进程通信中对AIDL进行详解。