《Android开发艺术探索》笔记(二)——IPC进程间通信

《Android开发艺术探索》笔记(二)——IPC进程间通信

作者:cmad 时间:2016-05-11 分类:Android 评论:0条 浏览:668

第二章 IPC机制

1.Android中的多进程模式

开启多线程模式

给四大组件在AndroidManifest中指定android:process属性。

android:process=":remote"
或:
android:process="com.cm.example.remote"

开启多进程后的问题:

  1. 静态成员和单例模式完全失效
  2. 线程同步机制完全失效
  3. SharedPreference的可靠性下降
  4. Application会多次创建

因为开启多进程后,系统会给每个进程分配一个独立的虚拟机,独立的内存空间。这就导致访问同一个类的静态变量其实是访问的当前内存空间的这个类的静态变量,相当于同一个类在每个进程有一个副本。线程同步也是同样的问题,因为线程锁的对象不是同一个了。SharedPreference不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失。最后一个问题当一个应用有多个进程时相当于应用要启动多次,这就导致了application会创建多次。

2.IPC基础概念介绍

Serializable

  Serializable是java提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作,相信很多人都用过这个接口,在intent进行传递数据的时候可以使用它,实现这个接口也非常简单,只需要实现Serializable接口然后在类里添加如下即可:

private static final long serialVersionUID = 1L; 

  其实甚至不用添加这个属性也可以,那么这个serialVersionUID 是干嘛用的呢?

serialVersionUID是用来辅助序列化和反序列化过程的,原则上序列化的数据的serialVersionUID和反序列化的类的serialVersionUID要相同才能够正常的被反序列化,否则会报错。

  那手动指定serialVersionUID的值和不写serialVersionUID有什么区别呢?不写serialVersionUID的话默认是当前类的hash值,如果你序列化后修改了这个类导致类的hash值发生了改变,那么反序列化的时候就会导致序列化数据的serialVersionUID与反序列化的类的serialVersionUID不相同导致序列化失败。如果手动指定serialVersionUID值的话就不会存在这个问题,当然了如果你修改了类的名称或者属性的类型等导致类的结构有了毁灭性的改变,此时尽管serialVersionUID值相同也会序列化失败。

Parcelable

  Parcelable是android提供的序列化接口,相对于Serializable来说实现要复杂得多,需要在类里实现序列化和反序列化的方法。但是Serializable开销很大,序列化和反序列化过程需要大量I/O操作,而Parcelable是android中的序列化方式因此更适合用在android平台上。

推荐一个实现Parcelable序列化接口的插件android parcelable code generator,在Android Studio中安装此插件可以使用此插件自动实现Parcelable序列化和反序列化的代码。

3.Android中的IPC方式

1)使用Bundle

  通过Intent启动Activity、Service、Receiver时可以通过Bundle传递实现了序列化接口的数据

2)文件共享

  不同进程通过读写同一个文件实现数据的共享。

3)使用Messenger

  Messenger底层也是通过AIDL实现的。

  服务端进程创建一个Service同时创建一个Handler并通过Handler创建一个Messenger对象,然后在service的onBind中返回这个Messenger对象底层的Binder。

  客户端进程首先需要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger向服务端发送Message对象,Message中设置我们要发的数据。

  也可以在客户端发送的Message的replyTo传入客户端的根据Handler创建的Messenger对象,然后服务端通过replyTo取得这个Messenger然后向客户端发送消息。

服务端MessengerService:

public class MessengerService extends Service {

private static final String TAG = "MessengerService";

private static class MessengerHandler extends Handler {
@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"));
Messenger client = msg.replyTo;
Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString("reply", "嗯,你的消息我已经收到,稍后会回复你。");
relpyMessage.setData(bundle);
try {
client.send(relpyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}

private final Messenger mMessenger = new Messenger(new MessengerHandler());

@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}

@Override
public void onCreate() {
super.onCreate();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}

}

AndroidManifest里注册:

<service
android:name=".messenger.MessengerService"
android:process=":remote" />

客户端Activity中使用:

public class MessengerActivity extends Activity {

private static final String TAG = "MessengerActivity";

private Messenger mService;
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());

private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MyConstants.MSG_FROM_SERVICE:
Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
break;
default:
super.handleMessage(msg);
}
}
}

private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
Log.d(TAG, "bind service");
Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "hello, this is client.");
msg.setData(data);
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}

public void onServiceDisconnected(ComponentName className) {
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
Intent intent = new Intent("com.ryg.MessengerService.launch");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}

4)使用AIDL

简单AIDL的实现

服务端

IBookManager.aidl

package com.ryg.chapter_2.aidl;

import com.ryg.chapter_2.aidl.Book;

interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}

注:AIDL中用到的自定义Parcelable对象和AIDL对象必须要显式的import进来,不管它们是否和当前的AIDL文件位于同一个包内。比如这里的Book。

  AIDL中除了基本数据类型,其他类型的参数必须标上方向:inout或者inoutin表示输入型参数,out表示输出型参数,inout表示输入输出型参数。比如上面的book用in标记表示为输入型参数。

  上面的IBookManager.aidl用到了自定义Parcelable的Book类,所以还要添加一个Book.aidl文件,如下:

package com.ryg.chapter_2.aidl;

parcelable Book;

注:AIDL中每个实现了Parcelable接口的类型都需要按照上面这种方式去创建相对应的AIDL文件并声明为parcelable。

自定义BookManagerService.java

public class BookManagerService extends Service {

private static final String TAG = "BMS";

private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

private Binder mBinder = new IBookManager.Stub() {

@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}

@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
};

@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
new Thread(new ServiceWorker()).start();
}

@Override
public IBinder onBind(Intent intent) {
return mBinder;
}

@Override
public void onDestroy() {
super.onDestroy();
}

}

至此服务端进程的代码就完成了。

注:AIDL只支持List的ArrayList,但是看到我们上面却用了CopyOnWriteArrayList,那是因为我们在getBookList方法用的是List接口,AIDL在返回的时候会自动将CopyOnWriteArrayList转化为ArrayList,而CopyOnWriteArrayList支持并发读/写,所以这里用了CopyOnWriteArrayList。

客户端进程实现

BookManagerActivity.java

public class BookManagerActivity extends Activity {

private static final String TAG = "BookManagerActivity";

private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
List<Book> list = bookManager.getBookList();
Log.i(TAG, "query book list, list type:"
+ list.getClass().getCanonicalName());
Log.i(TAG, "query book list:" + list.toString());
Book newBook = new Book(3, "Android进阶");
bookManager.addBook(newBook);
Log.i(TAG, "add book:" + newBook);
List<Book> newList = bookManager.getBookList();
Log.i(TAG, "query book list:" + newList.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}

public void onServiceDisconnected(ComponentName className) {
Log.d(TAG, "onServiceDisconnected. tname:" + Thread.currentThread().getName());
}
};


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}

}

客户端的代码相对简单。

注:我们需要将服务端aidl包以及下面的文件包括Book.java、Book.aidl、IBookManager.aidl拷贝到客户端项目里,并保持包结构相同。

简单的AIDL的使用就完成了。

为AIDL添加监听接口

接下来看给服务端AIDL添加监听接口,监听添加书籍以后自动通知客户端。

在服务端新建一个IOnNewBookArrivedListener.aidl文件

package com.ryg.chapter_2.aidl;

import com.ryg.chapter_2.aidl.Book;

interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}

修改IBookManager.aidl文件如下:

package com.ryg.chapter_2.aidl;

import com.ryg.chapter_2.aidl.Book;
import com.ryg.chapter_2.aidl.IOnNewBookArrivedListener;

interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}

BookManagerService.java修改:

public class BookManagerService extends Service {

private static final String TAG = "BMS";

private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();

private Binder mBinder = new IBookManager.Stub() {

@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}

@Override
public void addBook(Book book) throws RemoteException {
onNewBookArrived(book);
}

@Override
public void registerListener(IOnNewBookArrivedListener listener)
throws RemoteException
{
mListenerList.register(listener);

final int N = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.d(TAG, "registerListener, current size:" + N);
}

@Override
public void unregisterListener(IOnNewBookArrivedListener listener)
throws RemoteException
{
boolean success = mListenerList.unregister(listener);

if (success) {
Log.d(TAG, "unregister success.");
} else {
Log.d(TAG, "not found, can not unregister.");
}
final int N = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.d(TAG, "unregisterListener, current size:" + N);
};

};

@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
}

@Override
public IBinder onBind(Intent intent) {
return mBinder;
}

@Override
public void onDestroy() {
super.onDestroy();
}

private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
if (l != null) {
try {
l.onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
}
}

  注意上面我们用到了RemoteCallbackList这个类,为什么要用这个不用List呢?是因为我们的Listener是在两个不同的进程间,客户端设置的listener跟服务端的listener其实不是同一个对象,如果用List的话那unregisterListener的时候其实是移除不了的,所以用了RemoteCallbackList,是系统提供的用户删除跨进程listener的接口。其内部是用的Map的key、value来保存listener的。并且当客户端终止后它能自动的移除客户端注册的listener。

注:RemoteCallbackList的beginBroadcast和finishBroadcast必须成对出现。

客户端的修改:

public class BookManagerActivity extends Activity {

private static final String TAG = "BookManagerActivity";
private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

private IBookManager mRemoteBookManager;

@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
Log.d(TAG, "receive new book :" + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};

private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
mRemoteBookManager = bookManager;
try {
List<Book> list = bookManager.getBookList();
Log.i(TAG, "query book list, list type:"
+ list.getClass().getCanonicalName());
Log.i(TAG, "query book list:" + list.toString());
Book newBook = new Book(3, "Android进阶");
bookManager.addBook(newBook);
Log.i(TAG, "add book:" + newBook);
List<Book> newList = bookManager.getBookList();
Log.i(TAG, "query book list:" + newList.toString());
bookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}

public void onServiceDisconnected(ComponentName className) {
mRemoteBookManager = null;
Log.d(TAG, "onServiceDisconnected. tname:" + Thread.currentThread().getName());
}
};

private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {

@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook)
.sendToTarget();
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
if (mRemoteBookManager != null
&& mRemoteBookManager.asBinder().isBinderAlive()) {
try {
Log.i(TAG, "unregister listener:" + mOnNewBookArrivedListener);
mRemoteBookManager
.unregisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}

}

  同样需要将IOnNewBookArrivedListener.aidl文件拷贝到客户端对应包下,IOnNewBookArrivedListener对象然后在onServiceConnected里设置监听,在onDestroy里解除监听。需要注意一点是我们在IOnNewBookArrivedListener接口的onNewBookArrived方法里用了handler,这是因为onNewBookArrived方法在服务端调用的时候其实是运行在客户端的Binder线程池中,不能进行UI操作所以用到了handler。

  同样需要注意的是客户端调用服务端的方法,服务端的方法是运行在服务端的Binder线程池中,如果方法有耗时操作的话可能会导致客户端出现ANR的情况,所以尽量将调用服务端的方法放在线程中执行,防止出现ANR的情况。

注:AIDL中客户端调用服务端方法,该方法运行在服务端的Binder线程池中;

 服务端通过接口调用客服端方法,该方法运行在客户端的Binder线程池中。

权限验证

  接下来看看如何在AIDL中进行权限验证,验证失败则无法连接我们的服务或者调用服务的方法。这里介绍两种方式。

第一种在onBind中通过permission进行验证

  在AndroidManifest中声明所需权限

<permission
android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE"
android:protectionLevel="normal" />

验证方法如下:

@Override
public IBinder onBind(Intent intent) {
int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
Log.d(TAG, "onbind check=" + check);
if (check == PackageManager.PERMISSION_DENIED) {
return null;
}
return mBinder;
}

验证失败直接返回null。

客户端只需要在AndroidManifest中加入该权限就可以了。

<uses-permission android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE" />
第二种方法在onTransact中进行验证

  在onTransact中可以采用第一种方法的permission验证,也可以采用Uid和Pid来验证,通过getCallingUid和getCallingPid可以拿到客户端所属的应用Uid和Pid,通过这个两个参数我们可以做一些验证工作,比如验证包名。如下验证包名以“com.ryg”开始

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException
{
int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
Log.d(TAG, "check=" + check);
if (check == PackageManager.PERMISSION_DENIED) {
return false;
}

String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(
getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
Log.d(TAG, "onTransact: " + packageName);
if (!packageName.startsWith("com.ryg")) {
return false;
}

return super.onTransact(code, data, reply, flags);
}

  上面我们加上了permission和包名的双重验证,验证失败返回false客户端就无法调用服务端的方法。

5)使用ContentProvider

  ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式。和Messenger一样,ContentProvider底层实现同样也是Binder,由此可见Binder在Android系统中是何等的重要。ContentProvider使用起来很简单,自定义ContentProvider只需要经常ContentProvider并实现6个抽象方法即可:onCreatequeryupdateinsertdeletegetType。这六个方法很好理解,getType返回Uri请求对应的MIME类型(媒体类型)。需要注意的是onCreate运行在main线程,queryupdateinsertdelete运行在Binder线程池中。

AndroidManifest里注册:

<provider
android:name=".provider.BookProvider"
android:authorities="com.ryg.chapter_2.book.provider" >

</provider>

  authorities是ContentProvider的唯一标识,其他应用通过这个属性访问我们的ContentProvider。

Uri uri = Uri.parse("content://com.ryg.chapter_2.book.provider");
getContentResolver().query(uri, null, null, null, null);

6)使用Socket

  Socket也称为”套接字”,是网络通信中的概念,它分为流式套接字和用户数据报套接字两种,分别对应于网络传输控制层中的TCP和UDP。通过Socket实现进程间通信实际上就是网络通信,跟我们客户端通过Socket与服务端进行通信是一样的,只是这里客户端和服务端都在同一个Android系统上。

  使用Socket进行进程间通信需要在服务创建ServerSocket监听特定端口,客户端通过ip地址和端口与服务端进行Socket通信。

4.Binder连接池

  当我们的项目有多个不同模块需要使用AIDL来进行进程间通信的时我们应该怎么处理呢?写多个AIDL服务?这样确实可以,但是感觉不太好。Service作为Android四大组件之一,本身就是一种系统资源,使用太多会不会太浪费资源?那我们应该怎么做呢?

  每个业务模块创建自己的AIDL接口,并实现此接口,然后向服务端提供自己的唯一标识和其对应的Binder对象。对于服务端来说只需要一个Service就够了,服务端提供一个queryBinder接口,这个接口根据业务模块的特性返回对应的Binder对象,不同业务模块拿到所需的Binder对象后就可以进行远程方法调用了。

  下面看一个例子:我们为两个模块提供两个AIDL接口ISecurityCenter和ICompute

ISecurityCenter.adil

interface ISecurityCenter {
String encrypt(String content);
String decrypt(String password);
}

ICompute.aidl

interface ICompute {
int add(int a, int b);
}

ISecurityCenter的AIDL接口实现SecurityCenterImpl.java:

public class SecurityCenterImpl extends ISecurityCenter.Stub {

private static final char SECRET_CODE = '^';

@Override
public String encrypt(String content) throws RemoteException {
char[] chars = content.toCharArray();
for (int i = 0; i < chars.length; i++) {
chars[i] ^= SECRET_CODE;
}
return new String(chars);
}

@Override
public String decrypt(String password) throws RemoteException {
return encrypt(password);
}

}

ICompute的AIDL接口实现ComputerImpl.java

public class ComputeImpl extends ICompute.Stub {

@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}

}

  我们实现了上面两个模块的AIDL接口,还需要实现一个IBinderPool的接口,里面提供一个queryBinder的方法。

IBinderPool.aidl:

interface IBinderPool {

/**
* @param binderCode, the unique token of specific Binder<br/>
* @return specific Binder who's token is binderCode.
*/

IBinder queryBinder(int binderCode);
}

IBinderPool的AIDL接口实现BinderPoolImpl.java

public static class BinderPoolImpl extends IBinderPool.Stub {
public static final int BINDER_NONE = -1;
public static final int BINDER_COMPUTE = 0;
public static final int BINDER_SECURITY_CENTER = 1;

public BinderPoolImpl() {
super();
}

@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch (binderCode) {
case BINDER_SECURITY_CENTER: {
binder = new SecurityCenterImpl();
break;
}
case BINDER_COMPUTE: {
binder = new ComputeImpl();
break;
}
default:
break;
}

return binder;
}
}

  queryBinder方法通过code返回对应的模块的AIDL接口的Binder。

下面再来看Service的实现

BinderPoolService.java

public class BinderPoolService extends Service {

private static final String TAG = "BinderPoolService";

private Binder mBinderPool = new BinderPoolImpl();

@Override
public void onCreate() {
super.onCreate();
}

@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return mBinderPool;
}

@Override
public void onDestroy() {
super.onDestroy();
}

}

  接下来看客户端连接池的实现BinderPool.java

import java.util.concurrent.CountDownLatch;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class BinderPool {
private static final String TAG = "BinderPool";


private Context mContext;
private IBinderPool mBinderPool;
private static volatile BinderPool sInstance;
private CountDownLatch mConnectBinderPoolCountDownLatch;

private BinderPool(Context context) {
mContext = context.getApplicationContext();
connectBinderPoolService();
}

public static BinderPool getInsance(Context context) {
if (sInstance == null) {
synchronized (BinderPool.class) {
if (sInstance == null) {
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}

private synchronized void connectBinderPoolService() {
mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
Intent service = new Intent(mContext, BinderPoolService.class);
mContext.bindService(service, mBinderPoolConnection,
Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

/**
* query binder by binderCode from binder pool
*
* @param binderCode
* the unique token of binder
* @return binder who's token is binderCode<br>
* return null when not found or BinderPoolService died.
*/

public IBinder queryBinder(int binderCode) {
IBinder binder = null;
try {
if (mBinderPool != null) {
binder = mBinderPool.queryBinder(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}

private ServiceConnection mBinderPoolConnection = new ServiceConnection() {

@Override
public void onServiceDisconnected(ComponentName name) {
// ignored.
}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mConnectBinderPoolCountDownLatch.countDown();
}
};

private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.w(TAG, "binder died.");
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
}

  这里面用到了CountDownLatch这个类,将bindService异步操作转换成了同步操作。还有DeathRecipient 这个对象,是为Serivce添加一个死亡监听,就是当Service因为异常死亡以后通过这个接口监听然后重新启动连接。

  接下来看看客户端的使用:

BinderPoolActivity.java

public class BinderPoolActivity extends Activity {
private static final String TAG = "BinderPoolActivity";

private ISecurityCenter mSecurityCenter;
private ICompute mCompute;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binder_pool);
new Thread(new Runnable() {

@Override
public void run() {
doWork();
}
}).start();
}

private void doWork() {
BinderPool binderPool = BinderPool.getInsance(BinderPoolActivity.this);
IBinder securityBinder = binderPool
.queryBinder(BinderPoolImpl.BINDER_SECURITY_CENTER);
;
mSecurityCenter = (ISecurityCenter) SecurityCenterImpl
.asInterface(securityBinder);
Log.d(TAG, "visit ISecurityCenter");
String msg = "helloworld-安卓";
System.out.println("content:" + msg);
try {
String password = mSecurityCenter.encrypt(msg);
System.out.println("encrypt:" + password);
System.out.println("decrypt:" + mSecurityCenter.decrypt(password));
} catch (RemoteException e) {
e.printStackTrace();
}

Log.d(TAG, "visit ICompute");
IBinder computeBinder = binderPool
.queryBinder(BinderPoolImpl.BINDER_COMPUTE);
;
mCompute = ComputeImpl.asInterface(computeBinder);
try {
System.out.println("3+5=" + mCompute.add(3, 5));
} catch (RemoteException e) {
e.printStackTrace();
}
}

  使用也很简单,至于这里为啥要用线程,就是我们上面讲到的BinderPool中使用了CountDownLatch将异步转化成了同步,那么binderService就可能是耗时操作,所以这里用了线程。

  

5.IPC方式的优缺点和适用场景

名称 优点 缺点 适用场景
Bundle 简单易用 只能传输Bundle支持的数据类型 四大组件间的进程通信
文件共享 简单易用 不适合高并发场景,并且无法做到进程间的即时通信 无并发访问情形,交换简单的数据实时性不高的场景
AIDL 功能强大,支持一对多并发通信,支持实时通信 使用稍复杂,需要处理好线程同步 一对多通信且有RPC需求
Messenger 功能一般,支持一对多串行通信,支持实时通信 不能很好处理高并发情形,不支持RPC,数据通过message进行传输,因此只能传输Bundle支持的数据类型 低并发的一对多即时通信,无RPC需求,或者无须返回结果的RPC需求
ContentProvider 在数据访问方面功能强大,支持一对多并发数据共享,可通过Call方法扩展其他操作 可以理解为受约束的AIDL,主要提供数据源的CRUD操作 一对多的进程间的数据共享
Socket 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 实现细节稍微有点繁琐,不支持直接的RPC 网络数据交换


相关推荐
更多

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>