您的位置:首页 > 其它

IPC(Inner Process Comunication)机制基本了解

2016-02-02 15:35 375 查看
IPC(Inner Process Comunication),中文就是进程间通信,在Android中有特色的进程间通信方式就是Binder(当然除了Binder,Android还支持Socket)

1.Android多进程模式

(1)多进程的创建方式

<1>.在清单文件中写一个Android:Process(四大组件都行)

Android:process的两种写法包名是com.packageAndroid:Process = “:remote”

“:”含义是指要在当前的进程名前面附加上当前的包名(也就是当前的进程名com.mypackage:remote(这个写法属于当前应用
的私 有进程,其他应用的组件不可以和它 跑同一个进程中


Android:Process = “com.mypackage.remote

这种方法其他应用可以通过ShareUID方式可以和它跑在同一进程中

Android系统会为每个应用分配一个唯一的UID,具有相同的UID的应用才能共享数据,需要满足两个条件,两个应用通过shareUID跑一个进程的条件

(1).相同的shareUID

(2).签名相同

<2>.JNI在native层去fork一个新的进程(不常用)(待添加

(2) 多进程需要注意的地方

Q:一个应用中两个进程分别改变同一个静态变量,然后读取这个静态变量,他的值是否是最后一个改变的结果?答案肯定不是

Android会为每个进程分配一个独立的虚拟机,不同的虚拟机在内存分配上有不 同的地址空间,这就导致在不同的虚
拟机上访 问同一个对象会产生多分副本。

一般来说,使用多进程会造成如下几个方面的问题:

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

2)线程同步机制完全失效(都不是一块内存了,那么不管是锁对象还是锁全局类都无法保证线程同步,因为不同进程锁的不是同一个对象)

3)SharePreference的可靠性下降。(不支持两个进程同时去执行,底层是通过读写XML)

4)Application会多次创建(分配独立的虚拟机---->相当于启动一个应用的过程--->自然会创建新的application)

2.IPC基础概念介绍

当我们需要Intent和Binder传输数据时就需要使用Paracelable或者SeriaLizable
我们需要对象持久化到存储设备上
通过网络传输给其他客户端,这个时候也需要Serializable来完成对象的持久化

(1).SeriaLizable接口(适用于将对象序列化到存储设备中,或者将对象序列化通过网络传输)

这是Java提供的一个序列化接口,他是一个空接口(也就是起到标记作用,然并软)

<1>实现条件 4

实现Serializable接口
申明一个serialVersionUID(但是貌似不是必须的,但是会对反序列化产生影响)

Public class user implements Serializable{
Private static final long serialVersionUID = 232342342332L;
....
.....
}<span style="font-size:14px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">		</span>


注意:serialVersionUID 的详细工作机制

1.序列化的时候会把当前类的serialVersionUID写入序列化的文件中(也有可能是其他媒介),

2.当反序列化的时候系统会去检测文件中的serilaVersionUID,看他是否和当前类的serialVersionUID一致,如果一致说明序列化的类的版本和当前类的版本是相同的,
这个时候可以成功反序列化,

3.serialVersionUID 可以手动指定可以用hashcode(如果发生成员变量个数之类的发生变化,就不能成功),手动指定的(写死了
serialVersionUID ,基本就可以成功除了
(1).改变类名 (2).修改成员变量的类
型(增删成员变量没事))


3.1自动获取SerialVersionUID









<2>序列化过程和反序列化过程 4

序列化过程

User user = new User(0,"jake",true);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(user);
out.close();


反序列话过程

//反序列化
ObjectInputStream in  = new ObjectInputStream(new FileInputStream("cache.txt"));
User newUser = (User) in.readObject();
in.close();<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">	</span>


注意:恢复出来的 newUserUser虽然内容一样,但是两者并不是同一个对象!!!

<3>注意事项

静态变量属于类不属于对象,所以不会参与序列化过程:其次用transient关键字标记的成员不参与序列化







(2).Parcelable接口(用起来麻烦,但是效率很高,适合用于Android用,这是Android推荐的序列化,主要用在内存序列化)

public class User implements Parcelable, Serializable {
private static final long serialVersionUID = 519067123721295773L;

public int userId;
public String userName;
public boolean isMale;

public Book book;

public User() {
}

public User(int userId, String userName, boolean isMale) {
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}

public int describeContents() {
return 0;
}
序列化功能由这个完成

public void writeToParcel(Parcel out, int flags) {
out.writeInt(userId);
out.writeString(userName);
out.writeInt(isMale ? 1 : 0);
out.writeParcelable(book, 0);
}
反序列化有这个完成

public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
public User createFromParcel(Parcel in) {
return new User(in);
}

public User[] newArray(int size) {
return new User[size];
}
};

private User(Parcel in) {
userId = in.readInt();
userName = in.readString();
isMale = in.readInt() == 1;
//由于book是另一个可序列化对象,所以它的反序列化过程需要传递当前线程的上下文类加载器,否则会报无法找到类的错误

book = in
.readParcelable(Thread.currentThread().getContextClassLoader());
}

@Override
public String toString() {
return String.format(
"User:{userId:%s, userName:%s, isMale:%s}, with child:{%s}",
userId, userName, isMale, book);
}

}


createFromParcel(Parcel in )从序列化后的对象中创建原始对象

New Array(int size ) 创建指定长度的原始对象数据

User(Parcel in) 从序列化后的对象中创建原始对象

writeToParcel(Parcel out , int flags) 将当前对象写入序列化结构中,其中flags标识有两种值:0或1,为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况为0

PARCELABLE_WRITE_RETURN_VALUE(1)

describleContents 返回当前对象的内容描述,如果含有文件描述,返回1,否则返回0,几乎所有情况都返回0 CONTENTS_FILE_DESCRIPTOR(1)

系统为我们提供了许多实现了Parcelable接口的类,他们是可以直接序列化 ,比如Intent,Bundle,Bitmap等,同时List和Map也可以序列化,前提是它们里面的每个元素都是可以序列化的

(3).Binder 3

不同角度理解binder

(1).IPC角度来说Binder是Android中的一种跨进程通信方式

(2)还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder

(3)从Android FrameWork角度来说,Binder是ServiceManager连接各种Manager(ActivityManager,WindowManager,等等)和相应ManagerService的桥梁

(4)从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder
对象,客户端就可以获取服务端提供的服务或者数据,这里的服 务包括普通服务和基于AIDL的服务.

Android开发中,Binder主要用在Service中,包括AIDL和Messager,其中普通Service中的binder不涉及进程间通信,所以较为简单,无法触及Binder的核心

分析Binder的工作机制

而Messager的底层其实是AIDL,所以我们这里就选择AIDL来分析Binder的工作机制

IService.java(由Iservice.aidl自动生成的一个Binder类)

/*
* This file is auto-generated.  DO NOT MODIFY.
* Original file: D:\\AndroidEclipseWorkspace\\text_day8_�������\\src\\com\\itheima\\text\\alipay\\IService.aidl
*/
package com.dx.text.launchmodetext;

public interface IService extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.itheima.text.alipay.IService {
//DESCRIPTOR是Binder的唯一标识!!!!

private static final String DESCRIPTOR = "com.itheima.text.alipay.IService";

/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an com.itheima.text.alipay.IService interface,
* generating a proxy if needed.
*/
public static com.itheima.text.alipay.IService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.itheima.text.alipay.IService))) {

//同一进程返回stud本身 !!!

return ((com.itheima.text.alipay.IService) iin);
}
//不是同一个进程返回stub.proxy
return new com.itheima.text.alipay.IService.Stub.Proxy(obj);
}

@Override
public android.os.IBinder asBinder() {
//返回当前Binder对象
return this;
}
//code
确定客户端所请求的方法

//data 取出目标方法所需要的参数

//reply 目标方法执行完返回reply

//返回值 返回false那么客户端请求失败,因此我们可以利用这个特性来做权限验证
@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_callBackSafeAlipay: {
data.enforceInterface(DESCRIPTOR);
String _arg0;
_arg0 = data.readString();
String _arg1;
_arg1 = data.readString();
int _arg2;
_arg2 = data.readInt();
long _arg3;
_arg3 = data.readLong();
int _result = this.callBackSafeAlipay(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.itheima.text.alipay.IService {
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
mRemote = remote;
}

@Override
public android.os.IBinder asBinder() {
return mRemote;
}

public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
//客户端调用此方法的过程

//1.输入型Parcel对象_data,输出型对象_reply,返回值对象List,写入_data

//2.调用transact发起RPC(远程调用请求),注意远程请求是很耗时的,那么不能再UI线程中发起此远程请求, 此时线程挂起

//3.服务端的onTranact会被调用直到RPC返回后,当前线程继续执行并从_reply取出RPC过程的结果

//4.最后返回_reply中的数据
public int callBackSafeAlipay(String username, String password, int money, long time) 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.writeString(username);
_data.writeString(password);
_data.writeInt(money);
_data.writeLong(time);
mRemote.transact(Stub.TRANSACTION_callBackSafeAlipay, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
//整型ID用于标识在Tranact过程中客户端所请求的到底是哪个方法
static final int TRANSACTION_callBackSafeAlipay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

public int callBackSafeAlipay(String username, String password, int money, long time) throws android.os.RemoteException;
}


从FrameWork层看

客户端和服务端

1.两者在同一个进程 ,不会走跨进程的transact

2.两者在不同进程,需要走transact,由stub的内部代理类proxy完成

注意:上面代码的onTransact是运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。



IService.java(由Iservice.aidl自动生成)从上述分析过程来看我们完全可以不提供AIDL即可实现Binder,之所以提供AIDL是为了方便系统给我们生成代码,也就是生成一个Binder类

说白了,这个类是由两部分组成,首先它本身是一个Binder的接口(继承了IInterface),其次它的内部由个Stub类,这个类就是个Binder

AIDL文件的本质就是系统为我们提供了一种快速实现Binder的工具,快速生成了一个Binder类

Binder两个很重要的方法linkToDeath和unlinkToDeath)

1.背景:Binder运行在服务端进程,如果服务端进程由于某种原因异常终止,这个时候我们服务端的Binder链接断裂(称之为Binder死亡),会导致我们的远程调用失败,如果我们不知道Binder链接已经断裂,那么客户端的功能就
会受到影响,为了解决这个问题,Binder中提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath设置一个死亡代理当Binder死亡时,那么我们就会收到通知,这个时候我
们就可以重新发起连接请求从而恢复连接

当Binder死亡的时候,系统就会回调binderDied方法,然后我们就可以移除之前绑定的binder代理并重新绑定远程服务

private Binder.DeathRecipient mDeathRecipient  = new IBinder.DeathRecipient(){
@Override
public void binderDied() {
if(mBookManager == null){
return ;
}
mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
mBookManager = null;
//TODO :这里重新绑定远程Service
}
}


其次,在客户端绑定远程服务成功后,给Binder设置一个死亡代理:

Binder.linkToDeath = IMessageBoxManager.Stub.asInterface(binder);

binder.linkToDeath(mDeathRecipient,0);

第二个参数直接设置为0 就可以了,经过上面两个步骤,就给我们的Binder设置了死亡代理,当Binder死亡的时候我们就可以收到通知了,另外通过BInder的方法isBinderAlive也可以判断BInder是否死亡

3.Android中的IPC方式

(1).使用文件共享方式

sharePreference是安卓中提供的轻量级存储方案,他通过键值对的方式来存储数据,在底层实现上它采用XML文件方式,每个应用的SharedPreference文件都可以在当前包所在的data目录下查看到,一般来说它的目录位
于/data/data/package name/shared_prefs目录中,

从本质来讲,sharePreference也属于文件中的一种,但是系统对他的读写有一定的缓存策略,即在内存中会有一份sharePreference文件缓存,所以在多进程模式下,系统对他的读写变得不可靠,当面对高并发的读写访问SharePreference有很
大几率会丢失数据,因此不建议在进程间通信使用sharePreference

(2).使用Messenger(底层AIDL)(串行的,只能一个一个处理且不能跨进程)

一次只能处理一个请求,因此在服务端我们不用考虑线程同步的问题

(1)实现一个Messager有如下几部,分为服务端,客户端

服务端进程

首先在服务端创建一个service来处理客户端的链接请求,同时创建一个Hander并通过他来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可

客户端进程

(1)首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messager,通过这个Messager就可以向服务端发送消息了,发送消息类型为Message.

(2)首先如果要服务端回应客户端,那么客户端就需要和服务端一样,创建一个Handler并创建一个新的Messager,

首先并把这个Messager对象通过Message的replyTo参数传递给服务端(客户端发送消息时候要带上这个Messager(也就是谁接受服务端的回信)),服务端通过这个replyTo参数就可以回应客户端

//服务端

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);
//通过对应的Client处理消息

} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
//将客户端发送的消息传递给MessengerHander处理

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);
}
}

//客户端

public class MessageActivity extends Activity {

private static final String TAG = "MessageActivity";

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);


//指定接收服务器信息的Client

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);
//"com.ryg.MessengerService.launch"
//        public Intent(String action) {
//            setAction(action);
//        }
Intent intent = new Intent("com.ryg.MessengerService.launch");//这里面就是写Action
//        intent.setClass(this,MessengerService.class);//这个也可以
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}

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

可以看出和普通的调用服务的写法基本一致

1.通过binder发送数据给服务端(顺便带上接收服务端回信Messager),服务端自动调用对应的Messager的handleMessager

2.接收到服务端的信息会调用对应接收回信的Messager的HandleMessager

总结:Messenger是以串行的方式来处理客户端发来的消息,如果大量的消息同时发送到服务端,服务端仍然只能一个个处理,如果有大量的并发请求,那么用Messenger就不太合适了,同时Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形用Messenger就无法坐到(不能跨进程,只能去用aidl了)

Messenger的工作原理



(3).AIDL(Android Interface Defined Language)

基本数据类型(八大基本数据类型)
String 和 CharSequence;
List只支持ArrayList,里面每个元素都必须能够被AIDL支持
Map只支持HashMap,里面每个元素都必须能够被AIDL支持,包括key和value
Parcelable:所有实现了Parcelable接口的对象(需要显示Import)
持AIDL:所有AIDL接口本身也可以在AIDL中使用(需要显示Import)
AIDL中不能使用普通接口,只能使用AIDL接口

注意:

(1)如果AIDL文件中用到了自定义的Parcelable对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型

Package 包名

Parcelable Book(自定义的Parcelable对象);

(2)AIDL的包结构在服务端和客户端要保持一致,否则运行会出错,因为客户端需要反序列化服务端中和AIDL接口相关的所有类,如果类的完整路径不一样的话,就无法成功反序列化

具体怎么使用参照这篇博客 http://blog.csdn.net/u011889786/article/details/50628400
例子(下面的AIDL调用的例子,主要讲1.观察者设计思想 2.跨进程传输的对象(内容一样,但不是同一个)3.线程池的一些说法

//一些AIDL文件

注意:这里使用接口是AIDL接口,因为AIDL中不能使用普通接口




IOnNewBookArrivedListener.aidl(接口B)

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);
}
上面体现了 支持AIDL:所有AIDL接口本身也可以在AIDL中使用(需要显示Import)

//服务端

BookManagerService.java

public class BookManagerService extends Service {

private static final String TAG = "BMS";

private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);

private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
//CopyOnWriteArrayList支持并发读/写(AIDL方法是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,会有多个线程同时访问的情形,所以我们用这个来进行自动的线程同步)

//AIDL中所支持的是抽象的List,而List只是一个接口,因此虽然服务端返回的是 //CopyOnWriteArrayList,但是在Binder中会按照List的规范去访问数据并最终形成一个ArrayList传递给客户端
// private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =
// new CopyOnWriteArrayList<IOnNewBookArrivedListener>();

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

private Binder mBinder = new IBookManager.Stub() {

@Override
public List<Book> getBookList() throws RemoteException {
SystemClock.sleep(5000);
//CopyOnWriteArrayList,但是在Binder中会按照List的规范去访问数据并最终形成一个ArrayList传递给客户端

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

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);
}

@Override
public void registerListener(IOnNewBookArrivedListener listener)
throws RemoteException {


//把所有注册了监听的放在一个List中

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"));


开启5s添加一本书的线程

new Thread(new ServiceWorker()).start();
}

@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;
}

@Override
public void onDestroy() {
mIsServiceDestoryed.set(true);
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();
}
开启一个线程(每5S添加一本书)

<span style="font-size: 12px;">    private class ServiceWorker implements Runnable {
@Override
public void run() {
// do background processing here.....
while (!mIsServiceDestoryed.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId, "new book#" + bookId);
try {
</span>


调用自己的onNewBookArrived

<span style="font-size:12px;"><span style="font-size: 12px;">               onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}</span></span>


//客户端

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 IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());
if (mRemoteBookManager == null)
return;
mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mRemoteBookManager = null;
// TODO:这里重新绑定远程Service
}
};

private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
mRemoteBookManager = bookManager;
try {
mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);
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());
}
};

复写onNewBookArrived,然后发送消息给Handler处理(这里就是发送消息说书到了)

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);
}

public void onButton1Click(View view) {
Toast.makeText(this, "click button1", Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {

@Override
public void run() {
if (mRemoteBookManager != null) {
try {
List<Book> newList = mRemoteBookManager.getBookList();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}).start();
}

@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();
}

}


总结.这里用到的观察者设计思想分为下面几个步骤

1.背景,图书馆里增加了书,需要通知那些注册了的用户(也就是需要知道增加书的用户)

2.步骤

(1)创建一个接口AIDL接口(B)(因为在其他AIDL文件中要用到,所以要用AIDL接口,不能用普通的),里面写一个方法暴露给外面实现void onNewBookArrived(in Book newBook);

(2).在服务端(图书馆)开一个线程,每隔5s添加一本书,然后调用服务端的 onNewBookArrived(newBook);的这个方法,然后服务端的这个方法会调用接口B中的onNewBookArrived中

(3)B接口中的onNewBookArrived在客户端被复写了,里面会给自己的handler发送消息说书到了

3.问题:注销注册会发现Listener在集合中并没有

因为多进程中,Binder(这个是唯一不会变的)会把客户端传递过来的对象重新转化并生成一个新的对象别忘了对象是不能跨进程直接传输的,对象的跨
进程传输本质上都是反序列化的过程
,这就是为什么AiDL中的自定义对象都必须实现Parcelable接口,所以虽然内容一样但是实际上已经不能同一个对象

(1)解决方法

用RemoteCallBackList(系统专门提供的用于删除跨进程listener的接口,RemoteCallBackList是一个泛型,支持任意的AIDL接口,可以从下面的声明看出来)

Public class RemoteCallBackList<E extends IInterface>

所有的AIDL接口都继承自IInterface

<1>工作原理

内部有一个Map结构专门用来保存所有的AIDL回调,这个Map的key是IBinder类型,value是CallBack类型,

IBinder key = listener.asBinder();(返回当前的Binder)

Callback value = new callback(listener , cookie)

可以看出虽然多次跨进程传输客户端的同一个对象会在服务端生成不同的对象,但是他们底层的Binder对象是同一个

所以我们只要找出那个解注册的listener具有相同Binder对象的服务端listener并他删掉即可,这就是RemoteCallBackList为我们做的事

<2>RemoteCallBackList的功能

1.除了上面说的那个

2.当客户端进程终止后,它能够自动移除客户端所注册的listener

3.内部自动实现了线程同步的功能,所以我们使用它来注册和解注册时,不需要做额外的线程同步工作

(2)解决步骤

1.在BookManagerService.java中用RemoteCallBackList对象替代之前的CopyOnWriteArrayList

2.修改注册跟解注册函数还有服务端的onNewBookArrived(book)

@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);
};


(3)注意事项

使用RemoteCallBackList有一点需要注意,我们无法像操作List一样去操作它,它并不是list,beginBroadCast和finishBroadCast必须配对使用

AIDL总结



服务端方法都是运行在服务端的Binder线程池中

(1)客户端调用服务端方法的执行线程(A)挂起

(2)服务端方法可以执行耗时操作

(3)因为线程A被挂起了,直到服务端方法返回(但是假如是操作),所以要运行在子线程(不能再UI线程,否则阻塞导致ANR)



客户端方法都是运行在客户端的Binder线程池中

比如BookManagerService的onNewBookArrived,内部调用了那个AIDL接口(IOnNewBookArrivedListener)中的onNewBookArrived方法,所以客户端这个onNewBookArrived方法如果比较耗时,那么请确保BookManagerService(服务
端)运行在非UI线程中,否则将导致服务端ANR

(1)这时候客户端的方法可以执行耗时操作

(2)避免服务端调用这个方法的线程是UI线程

注意:

1.客户端的onServiceConnected和onServiceDisconnected 方法都运行在UI线程中,所以也不可以在他们里面直接调用服务端的耗时方法!!!!

2.由于客户端的IOnNewBookArrivedListener中的onNewBookArrived方法运行客户端的Binder线程池中,所以不能在它里面去访问UI相关的内容,如果要访问UI,使用Hander切换到UI线程(比如前面打印收到书的handler)



3.考虑到Binder意外死亡

给binder设置DeathRecipient监听,当Binder死亡时,我们会收到BinderDied方法的回调,在binderDied方法中我们可以重连远程服务(BinderDied在客户端的Binder的线程池中被回调)
onServiceDisconnnected中重连远程服务,(这个方法是在客户端的UI线程中被回调)

4.给AIDL进行权限验证

在onBind中进行验证,验证不通过就直接返回null,这样验证失败的客户端直接无法绑定服务。

(1).声明permission(定义方式查资料????)

例子:

public IBinder onBind(Intent intent){
int check = checkCallingOrSelfPermission("包名.permission.Access_Book_Service");(里面写就是清单文件的权限Android:name)
if(check == PackageManager.PERMISSION_DENIED){
return null;
}
return mBinder;
}


(2).在服务端的onTransact方法验证,如果验证失败就返回false这样服务端就不会终止执行AIDL中的方法从而达到保护服务端的效果

(1)声明权限(同上)

(2)通过Uid和Pid来做验证(getCallingUid和getCallingPid)(代码待添加????)

开始讲Socket(可以和基础结合起来一起做一下,待添加????)

4.Binder线程池

多个Aidl使用binder连接池的原理图



单个Aidl调用的基本原理图

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: