1 IPC机制基础
1.1 Android IPC简介
(1)IPC是Inter-Process Communication的缩写,含义是进程间通信或者跨进程通信,是指两个进程间进行数据交互的一个过程。
(2)线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。
1.2 Android 中的多进程模式
Android中的多进程模式,通过给四大组件指定android:process属性,可以轻易的开启多进程模式。
1.2.1 开启多进程模式
(1)正常情况下,在Android中多进程是指一个应用中存在多个进程的情况,因此这里不讨论两个应用之间的情况,首先在Android中使用多进程只有一种方法,那就是给四大组件指定android:process。还有一种非常规的多进程方法,那就是通过JNI在native层去fork一个新的进程。
(2)示例如下:
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:launchMode="standard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.seniorlibs.binder.file.SecondActivity"
android:process=":remote" />
<activity
android:name="com.seniorlibs.binder.file.ThirdActivity"
android:process="com.seniorlibs.binderclient.remote" />
(3)“:remote”和包名“.remote”的区别?
①":remote"属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中,常用。
②".remote"属于全局进程,其他应用通过ShareUID方式可以和它跑在同一进程中。
1.2.2 多进程模式的运行机制
使用多进程会造成如下几方面的问题:
1、静态成员和单例模式完全失效
2、线程同步机制完全失效。
3、SharedPreferences的可靠性下降。
4、Application会多次创建。
(1)第1问题原因:Android为每一个应用都分配了一个独立的虚拟机,或者说为每个进程都分配一个独立的虚拟机,不同的虚拟机在内存上有不同的地址空间,这就导致在不同的虚拟机中访问同一个类的对象会产生多份副本。举例,在两个进程中都存在一个UserManager类,并且这两个类是互不干扰的,在一个进程中修改mUseld的值只会影响当前进程,对其他进程不会造成任何影响。所有运行在不同进程中的四大组件,只要它们之间需要通过内存来共享数据,都会共享失败这也是多进程带来的主要影响。
(2)第2问题原因:本质上和第一个问题是类似的,既然都不是一块内存了,那么不管是锁对象还是锁全局类都无法保证线程同步,因为不同进程锁都不是同一个对象。
(3)第3问题原因:SharedPreferences底层是通过读/写XML文件来实现的,并发写显然是可能出问题的,甚至并发读/写都有可能出问题;所以不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失。
(4)第4问题原因:由于系统要在创建新的进程同时分配独立的虚拟机,所以这个过程其实就是启动一个应用的过程。因此,相当于系统又把这个应用重启动了一遍,既然重新启动了,那么自然会创建新的Application。
1.3 IPC基础概念
1.3.1 Serializable
(1)Serializable是java所提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化的操作。
(2)想让一个对象实现序列化,只需要这个类实现Serializable接口并且声明一个serialversionUID即可:
private static final long serialVersionUID = 8711368828010083044L;
(3)serialversionUID的工作机制?
序列化的时候会把当前类的serialversionUID写进序列化的文件中(也可能是其他中介),当反序列化的时候系统会去检测文件中的serialversionUID,看它是否和当前类的serialversionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变换,比如成员,类型可能发生了变化,这个时候是无法正常的反序列化的。
(3)为什么建议手动指定serialversionUID的值?
如果不手动指定serialversionUID的值,反序列化时当前类有所改变,比如增加或者删除了某些成员变量,那么系统就会重新计算当前类的hasj值并把它赋值给serialversionUID,这个时候当前类的serialversionUID就和序列化数据中的serialversionUID不一致,于是反序列化失败,程序就会出现crash。
1.3.2 Parcelable
(1)在序列化过程中需要实现的功能有序列化、反序列化和内容描述
public class User implements Parcelable {
public int userId;
public String userName;
public boolean isMale;
public Book book;
public User(int userId, String userName, boolean isMale) {
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}
// 内容描述功能,几乎在所有情况下这个方法都应该返回0,仅当当前对象中存在文件描述符时,此方法返回1
@Override
public int describeContents() {
return 0;
}
// 序列化功能,最终是通过Parcel中的一系列write方法来完成的
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(userId);
dest.writeString(userName);
dest.writeByte((byte) (isMale ? 1 : 0));
}
// 反序列化功能,其内部标明了如何创建序列化对象和数组,并通过Parcel的一系列read方法来完成反序列化过程
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
protected User(Parcel in) {
userId = in.readInt();
userName = in.readString();
isMale = in.readByte() != 0;
}
}
(2)Parcelatle和Serializable之间该如何选取?
①Parcelatle和Serializable都能实现序列化并且都可用于Inient间的数据传递;
②Serializable是Java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量I/O操作。
③Parcelable是Andrord推荐的序列化方式,更适合用在Android平台上,它的缺点就是使用起来稍微麻烦点,但是它的效率很高,因此我们要首选Parcelatle。
④Parcelable主要用在内存序列化上;通过Parcelable将对象序列化到存储设备中,或者将对象序列化后通过网络传输这个过程会稍显复杂,因此建议使用Serializabie。
1.3.3 Binder
(1)概念
①直观来说,Binder是Android中的一个类,它实现了IBinder接口;
②Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有;
③从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,等等)和相应Managerservice的桥梁:
④从IPC角度来说,Binder是Android中的一种跨进程通信方式;
⑤从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务(包括普通服务和基于AIDL的服务)或者数据。
(2)进程间通信的整体架构
1.4 Binder工作机制分析
1.4.1 通过AIDL来分析Binder工作机制
(1)创建一个工程,在工程中点击右键New->AIDL->AIDL File,默认直接点确定,这时会在工程中出现一个aidl文件。
(2)由于AS是要手动编译才能生成对应AIDL的java文件,既然aidl文件是个接口,那就必须存在着实现这个接口的类,点击编译,系统自动生成一个java类,该java类的代码就是整个Binder机制的原理所在
(3)写服务端的代码,就要开始写服务了,创建一个类,继承Service
/**
* 服务端---服务类
*/
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
private IBinder iBinder = new IMyAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int add(int num1, int num2) throws RemoteException {
Log.i("TAG", "从客户端发来的AIDL请求:num1->" + num1 + "::num2->" + num2);
return num1 + num2;
}
};
}
(4)必须在manifests文件中配置
<service
android:name=".MyService"
android:process=":remote" />
(5)写客户端的代码,放一个”AIDL“的按钮,先绑定服务,然后点击按钮调用
public class MainActivity extends AppCompatActivity {
private IMyAidlInterface iMyAidlInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 绑定服务
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
/**
* 点击“AIDL”按钮事件
*/
public void add(View view) {
try {
int res = iMyAidlInterface.add(1, 2);
Log.i("TAG", "从服务端调用成功的结果:" + res);
} catch (Exception e) {
e.printStackTrace();
Log.i("TAG", "服务端未开启");
}
}
/**
* 服务回调方法
*/
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
@Override
protected void onDestroy() {
super.onDestroy();
//解绑服务,回收资源
unbindService(mConnection);
}
}
(6)测试结果(先开启服务端,开启服务后,接着开启客户端,绑定远程服务)
02-22 17:21:33.651 19492-19552/demo.binder I/TAG: 从客户端发来的AIDL请求:num1->1::num2->2
02-22 17:21:33.651 22610-22610/demo.binderb I/TAG: 从服务端调用成功的结果:3
1.4.2 Binder的工作机制原理分析-AIDL类源码
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\project\\project_my\\BinderB\\app\\src\\main\\aidl\\demo\\binder\\IMyAidlInterface.aidl
*/
package demo.binder;
// Declare any non-default types here with import statements
// 接口
public interface IMyAidlInterface extends android.os.IInterface {
/**
* 服务端.
*/
public static abstract class Stub extends android.os.Binder implements demo.binder.IMyAidlInterface {
// 当前Binder类的全名
private static final java.lang.String DESCRIPTOR = "demo.binder.IMyAidlInterface";
// Construct the stub at attach it to the interface.
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* 将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,obj是当前服务端的Binder对象
*/
public static demo.binder.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
// 如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,下面方法转换
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof demo.binder.IMyAidlInterface))) {
return ((demo.binder.IMyAidlInterface) iin);
}
// 如果客户端和服务端位不位于同一进程,返回的是系统封装后的Stub.Proxy(obj)
return new demo.binder.IMyAidlInterface.Stub.Proxy(obj);
}
/**
* 当前服务端的Binder对象
*/
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理
*/
@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_basicTypes: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/**
* 客户端
*/
private static class Proxy implements demo.binder.IMyAidlInterface {
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 void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public int add(int num1, int num2) 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.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
// 在这个类里面我们会发现有2个标识:用来区分两个方法,到底你远程请求哪个方法的唯一标识,代码定位到代理类的结尾
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* 首先本身继承Iinterface,所以也是个接口,接口中必须有方法,代码定位到结尾有2个方法,就是basicTypes和add,就是我们服务端的2个方法
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public int add(int num1, int num2) throws android.os.RemoteException;
}
1.4.3 Binder的工作机制原理分析-步骤
(1)客户端绑定一个服务MyService,在回调方法获取接口iMyAidlInterface,直接静态方法IMyAidlInterface里面的静态类Stub的asInterface的方法
// 绑定服务
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// asInterface方法中传入的obj是服务端MyService的的iBinder对象,返回的是Proxy代理类对象
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
// asInterface方法中传入的obj是服务端MyService的的iBinder对象,返回的是Proxy代理类对象
public static com.handsome.boke.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.handsome.boke.IMyAidlInterface))) {
return ((com.handsome.boke.IMyAidlInterface) iin);
}
// 返回Proxy代理对象
return new com.handsome.boke.IMyAidlInterface.Stub.Proxy(obj);
}
(2)客户端,iMyAidlInterface调用了Proxy代理类对象的add方法
int res = iMyAidlInterface.add(1, 2);
(3)Proxy类,add方法中把数据写进了_data,最后调用mRemote.transact()方法,传入_data和唯一标识Stub.TRANSACTION_add
private static class Proxy implements demo.binder.IMyAidlInterface {
@Override
public int add(int num1, int num2) throws android.os.RemoteException {
// 首先创建该方法所需要的输入型Parcel对象_data、输出型Parcel对象_reply、和返回值对象List
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// 然后把该方法的参数信息写入_data中(如果有参数的话)
_data.writeInt(num1);
_data.writeInt(num2);
// 接着调用mRemote.transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起
// 然后服务端的onTransact方法会被调用
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt(); //(8)
} finally {
_reply.recycle();
_data.recycle();
}
return _result; //(8)
}
}
(4)Proxy类,mRemote就是服务端MyService的的iBinder对象,即是Stub对象
private static class Proxy implements demo.binder.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
}
(5)Stub类,这个transact方法就是通过IBinder类内部实现,结束后调用stub类中的onTransact方法,通过判断唯一标识,确定方法
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
// 服务端通过code可以确定客户端所请求的目标方法是什么
switch (code) {
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt(); // 从data中取出目标方法所需要的参数
int _arg1;
_arg1 = data.readInt();
// 将传过来的参数解包,readInt方法,得到num1、num2,然后调用this.add方法
// this指的就是当前的Stub对象---MyService的onBind()中返回的iBinder对象,调用MyService重写的iBinder的add的方法
int _result = this.add(_arg0, _arg1);//(7.1)
// 将得到的结果,写入reply(传输结果)
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);//(7.2)
}
(5.1)服务端,this是服务端MyService的onBind()中返回的iBinder对象,此重写的add(int num1, int num2)就是this.add(_arg0, _arg1)具体实现的
startService(new Intent(this, MyService.class));
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
private IBinder iBinder = new IMyAidlInterface.Stub() {
@Override
public int add(int num1, int num2) throws RemoteException {
return num1 + num2;//计算公式
}
};
}
(5.2)Stub类,onTransact最后调用Binder的ontransact方法,在native层实现,传输结果reply
return super.onTransact(code, data, reply, flags);
(6)Proxy类,add方法中在远程过程调用过程结束–onTransact()返回后,当前线程继续执行,从reply读取_result,返回到客户端
private static class Proxy implements demo.binder.IMyAidlInterface {
@Override
public int add(int num1, int num2) throws android.os.RemoteException {
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
// 直到远程过程调用过程结束---onTransact()返回后,当前线程继续执行,
// 并从_reply中取出RPC过程的返回结果
_reply.readException();
// 得到结果,从reply读取_result
_result = _reply.readInt();
// 返回_result
return _result;
}
}
(7)客户端,获取到的结果就是_result
//(结果 res=3)
int res = iMyAidlInterface.add(1, 2);
1.4.4 不通过AIDL文件,手动实现一个Binder
AIDL文件并不是实现Binder的必需品。如果是我们手写的Binder,那么在服务端只需要创建一个BookManagerImpl的对象并在Service的onBind方法中返回即可。最后,是否手动实现Binder没有本质区别,二者的工作原理完全一样,AIDL文件的本质是系统为我们提供了一种快速实现Binder的工具,仅此而已。
(1)IInterface接口IMyAidlManager.java
public interface IMyAidlManager extends IInterface {
public static final java.lang.String DESCRIPTOR = "com.seniorlibs.binder.customaidl.IMyAidlManager";
// 在这个类里面我们会发现有2个标识:用来区分两个方法,到底你远程请求哪个方法的唯一标识,代码定位到代理类的结尾
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public int add(int num1, int num2) throws android.os.RemoteException;
}
(2)IInterface接口实现类
public abstract class MyAidlManagerImpl extends Binder implements IMyAidlManager {
public MyAidlManagerImpl() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* 将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,obj是当前服务端的Binder对象
*/
public static IMyAidlManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
// 如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的对象本身,下面方法转换
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IMyAidlManager))) {
return ((IMyAidlManager) iin);
}
// 如果客户端和服务端位不位于同一进程,返回的是封装后的MyAidlManagerImpl.Proxy(obj)
return new MyAidlManagerImpl.Proxy(obj);
}
/**
* 当前服务端的Binder对象
*/
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理
*/
@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_basicTypes: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_add: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/**
* 客户端
*/
private static class Proxy implements IMyAidlManager {
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 void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public int add(int num1, int num2) 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.writeInt(num1);
_data.writeInt(num2);
mRemote.transact(TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
(3)客户端
public class MyAidlManagerActivity extends Activity {
private IMyAidlManager iMyAidlManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_aidl_manager);
// 绑定服务
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
/**
* 点击“AIDL”按钮事件
*/
public void add(View view) {
try {
int res = iMyAidlManager.add(1, 2);
Log.i("TAG", "从服务端调用成功的结果:" + res);
} catch (Exception e) {
e.printStackTrace();
Log.i("TAG", "服务端未开启");
}
}
/**
* 服务回调方法
*/
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlManager = MyAidlManagerImpl.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlManager = null;
}
};
@Override
protected void onDestroy() {
super.onDestroy();
//解绑服务,回收资源
unbindService(mConnection);
}
}
(4)服务端
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
private IBinder iBinder = new MyAidlManagerImpl() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int add(int num1, int num2) throws RemoteException {
Log.i("TAG", "从客户端发来的AIDL请求:num1->" + num1 + "::num2->" + num2);
return num1 + num2;
}
};
}
(5)结果
11-17 17:20:05.805 14109-14120/com.seniorlibs.binder:remote I/TAG: 从客户端发来的AIDL请求:num1->1::num2->2
11-17 17:20:05.805 13969-13969/com.seniorlibs.binder I/TAG: 从服务端调用成功的结果:3
2 Android中的IPC方式
2.1 使用Bundle
Bundle,用Intent传值的引用对象,我们都知道Activity、Service、Receiver三大组件都是可以通过intent的Bundle传值的,那是因为Bundle实现了Parcelable接口,所以他可以在不同的进程间传输。
2.2 使用文件共享
(1)文件共享是一种不错的进程间通讯的方式,两个进程通过读/写同一个文件来交换数据,比如A进程把数据写入文件,B再去读取。Android中,两个线程同时进行写的操作也是不允许的,尽管这可能出问题,通过文件交换数据很好使用,除了可以交换一些文本信息外,我们还可以序列化UI和对象到文件里,引用的时候再恢复,下面就来展示下这个功能:
(2)onResume序列化一个对象到sd卡
private void persistToFile() {
new Thread(new Runnable() {
@Override
public void run() {
User user = new User("1", "jek", false);
File dir = new File(PATH);
if (!dir.exists()) {
dir.mkdir();
}
File cachedFile = new File(PATH);
ObjectOutputStream objectOutputStream = null;
try {
objectOutputStream = new ObjectOutputStream(new FileOutputStream(cachedFile));
objectOutputStream.writeObject(user);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
(3)在第二个进程的Activity去恢复
private void recoverFromFile() {
new Thread(new Runnable() {
@Override
public void run() {
User user = null;
File cachedFile = new File(MainActivity.PATH);
if(cachedFile.exists()){
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(new FileInputStream(cachedFile));
user = (User) objectInputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}).start();
}
(4)小结
我们要尽量避免并发写这种情况的发生或者考虑使用线程同步来限制多个线程的写操作。通过上面的分析,我们可以知道,文件共享方式适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读/写的问题。
(5)SharedPreferences是个特例
SharedPreferences是Android中提供的轻量级存储方案,它通过键值对的方式来存储数据,在底层实现上它采用XML文件来存储键值对,每个应用的SharedPreferences文件都可以在当前包所在的data目录下查看到,一般来说,它的目录位于/data/data/package name/shared_prefs目录下,其中package name表示的是当前应用的包名。从本质上来说,SharedPreferences也属于文件的一种,但是由于系统对它的读/写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存,因此在多进程模式下,系统对它的读/写就变得不可靠,当面对高并发的读/写访问Sharedpreferences有很大几率会丢失数据,因此,不建议在进程间通信中使SharedPreferences。
2.3 使用Messenger
(1)Messenger是一种轻量级的IPC方案,它的底层实现是AIDL。
(2)具体的实现方式可以参考
Android开发艺术探索——第二章:IPC机制(中)
书本:Android开发艺术探索 P65
2.4 使用AIDL-综合的案例
Messenger是以串行的方式处理客户端发来的消息,如果大量的消息同时发送到服务端,服务端仍然只能一个个处理,如果有大量的并发请求,那么用Messenger就不太合适了。同时,Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形用Messenger就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用。
2.4.1 清单文件AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.seniorlibs.binder">
<!-- C2、服务端声明的权限 -->
<permission
android:name="com.seniorlibs.binder.permission.ACCESS_BOOK_SERVICE"
android:protectionLevel="normal" />
<!-- C2、客户端声明的权限 -->
<uses-permission android:name="com.seniorlibs.binder.permission.ACCESS_BOOK_SERVICE" />
<activity
android:name="com.seniorlibs.binder.aidl.BookManagerActivity"
android:label="@string/title_activity_book_manager"
android:screenOrientation="portrait"/>
<service
android:name="com.seniorlibs.binder.aidl.BookManagerService"
android:process=":remote" />
</application>
</manifest>
2.4.2 服务端的AIDL文件接口的创建
在AlDL文件中,并不是所有的数据类型都是可以使用的,那么到底AIDL文件哪些数据类型呢?如下所示
基本数据类型(int、long、char、boolean、double等);
Sting和CharSequence:
List:只支持ArrayList,里面每个元素都必须能够被AIDL支持;
Map:只支持HashMap,里面的每个元素都必须被AIDL支持,包括key和value:
Parcelable:所有实现了Parcelable接口的对象;
AIDL.所有的AIDL接口本身也可以在AIDL.文件中使用
(1)Book.aidl
package com.seniorlibs.binder.aidl;
// 注意:
// 1、必须与实体类同名
// 2、如果AIDL文件中用到了自定义的Parcelable对象,那必须新增一个和他同名的AIDL文件,并在其中声明它为Parcelable类型
// 3、服务端和客户端要保持AIDL包结构一致,客户端需要反序列化服务端中和AIDL接口相关的所有类,如果类的完整路径不一样的话,就无法成功反序列化
parcelable Book;
(2)IBookManager.aidl
package com.seniorlibs.binder.aidl;
// 注意:1、尽管Book类和IBookManager位于相同的包中,但是在为IBookManager仍然要导入Book类,这就是AIDL的特殊之处
import com.seniorlibs.binder.aidl.Book;
import com.seniorlibs.binder.aidl.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
// in、out或者inout,in表示输入型参数,out表示输出型参数,inout表示输入输出型参数
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
(3)IOnNewBookArrivedListener.aidl
package com.seniorlibs.binder.aidl;
import com.seniorlibs.binder.aidl.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}
2.4.3 客户端的实现
package com.seniorlibs.binder.aidl;
/**
* 客户端
*/
public class BookManagerActivity extends Activity implements View.OnClickListener {
private static final String TAG = "TAG-BookManagerActivity";
private static final int MSG_NEW_BOOK_ARRIVED = 1;
private static final int MSG_GET_BOOK_RESULT = 2;
private IBookManager mRemoteBookManager;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_NEW_BOOK_ARRIVED) {
Log.d(TAG, "接收到监听器的回调方法结果:" + msg.obj);
} else if (msg.what == MSG_GET_BOOK_RESULT) {
List<Book> list = (List<Book>) msg.obj;
Log.i(TAG, "查询书籍列表内容:" + list.getClass().getCanonicalName() + " + book list:" + list.toString());
} else {
super.handleMessage(msg);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
findViewById(R.id.button1).setOnClickListener(this);
findViewById(R.id.button2).setOnClickListener(this);
findViewById(R.id.button3).setOnClickListener(this);
// A1、绑定服务
rebindService();
}
/**
* A2、服务回调方法
*/
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// A5、绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型对象mRemoteBookManager,接着就可以调用AIDL中的方法了
mRemoteBookManager = IBookManager.Stub.asInterface(service);
try {
// E3、在客户端绑定远程服务成功之后,给Binder设置一个死亡代理
mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName className) {
mRemoteBookManager = null;
// E1、解除绑定Binder方法二:在onServiceDisconnected()中重新绑定远程Service
rebindService();
}
};
/**
* A6、客户端调用远程服务的方法,AIDL中mRemoteBookManager的方法了,以下分别是:getBookList()和addBook()
*
* 注意:被调用远程服务的方法运行在服务端的Binder线程池中,同时客户端线程会被挂起,如果服务端方法执行比较耗时,就会导致客户端线程长时间地阻塞,而如果这个客户端线程是UI线程的话,就会导致客户端ANR。
* 所以,需要指定AIDL的方法在子线程中执行,如果要访问UI需要使用Handler切换到UI线程去执行。
* @param v
*/
@Override
public void onClick(final View v) {
new Thread(new Runnable() {
@Override
public void run() {
if (mRemoteBookManager == null || mHandler == null) {
return;
}
if (v.getId() == R.id.button1) {
try {
List<Book> list = mRemoteBookManager.getBookList();
// 切换到UI线程去执行:获取书籍列表内容
mHandler.obtainMessage(MSG_GET_BOOK_RESULT, list).sendToTarget();
} catch (RemoteException e) {
e.printStackTrace();
}
} else if (v.getId() == R.id.button2) {
try {
Book newBook = new Book(3, "Android进阶");
Log.i(TAG, "添加书籍内容:" + newBook);
mRemoteBookManager.addBook(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
} else if (v.getId() == R.id.button3) {
try {
// B3、注册服务端回调客户端的IOnNewBookArrivedListener监听器
mRemoteBookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}).start();
}
/**
* B5、添加IOnNewBookArrivedListener.Stub中registerListener()监听器的回调方法,服务端调用此方法后直接回调
*/
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book newBook) {
// B5、此方法是在(客户端的)Binder线程池中执行,因此要访问UI需要使用Handler切换到UI线程去执行
mHandler.obtainMessage(MSG_NEW_BOOK_ARRIVED, newBook).sendToTarget();
}
};
/**
* E1、解除绑定Binder方法一:给Binder设置Deathleciient监听
* Deathleciient是一个接口,其内部只有一个方法binderDied,当Binder死亡的时候,系统就会回调binderDied方法
* binderDied方法在客户端的Binder线程池中被回调,所以在binderDied方法不能访问UI
*/
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// E2、当Binder死亡时,我们就会收到通知,这个时候我们就可以重新绑定远程Service从而恢复连接
Log.d(TAG, "已经解除绑定Binder的name:" + Thread.currentThread().getName());
if (mRemoteBookManager == null) {
return;
}
// E2.1、解除绑定Binder
mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mRemoteBookManager = null;
// E2.2、重新绑定远程Service
rebindService();
}
};
/**
* 重新绑定远程Service
*/
private void rebindService() {
Intent intent = new Intent(BookManagerActivity.this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
// D3、解除注册监听器,isBinderAlive判断Binder是否死亡
if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) {
try {
Log.i(TAG, "解除注册监听器:" + mOnNewBookArrivedListener);
mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
// A7、解绑服务,回收资源
unbindService(mConnection);
super.onDestroy();
}
}
2.4.4 远程服务端的实现
package com.seniorlibs.binder.aidl;
/**
* 服务端
*/
public class BookManagerService extends Service {
private static final String TAG = "TAG-BookManagerService";
/**
* C1、加入权限验证功能,验证通过后才允许链接
*/
private static final String PERMISSION_CHECK = "com.seniorlibs.binder.permission.ACCESS_BOOK_SERVICE";
private AtomicBoolean mIsServiceDestory;
/**
* AIDL方法是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问的情形,所以我们要在AIDL方法中处理线程同步
* 直接使用CopyonWriteArayList来进行自动的线程同步
*/
private CopyOnWriteArrayList<Book> mBookList;
// private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList;
/**
* D1、RemoteCallbackList是系统专门用来删除listener的接口,RemoteCallbackList是一个泛型,支持管理任意的AIDL接口
* 当客户端解注册的时候,我们只要遍历服务端所有的listener,找到那个和解注册listener具有相同Binder对象的服务端listener并把它删掉
*/
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList ;
@Override
public void onCreate() {
super.onCreate();
initData();
}
private void initData() {
mIsServiceDestory = new AtomicBoolean(false);
mBookList = new CopyOnWriteArrayList<>();
// mListenerList = new CopyOnWriteArrayList<IOnNewBookArrivedListener>();
mListenerList = new RemoteCallbackList<>();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
// B1、服务端开启线程
new Thread(new ServiceWorker()).start();
}
/**
* A4、返回服务端的Binder对象
*
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
// C3、权限验证方法一:没有此权限,onBind()直接返回null
int check = checkCallingOrSelfPermission(PERMISSION_CHECK);
Log.d(TAG, "权限验证方法一:onbind check=" + check);
if (check == PackageManager.PERMISSION_DENIED) {
return null;
}
return mBinder;
}
/**
* A3、服务端实现AIDL接口方法
*
* 注意:由于客户端的onServiceConnected和onServiceDisconnected方法都运行在UI线程中,所以不可以在它们里面直接调用服务端的耗时方法,
* 另外,由于服务端的方法本身就运行在服务端的Binder线程池中,所以服务端方法本身就可以执行大量耗时操作,这时切记不要在服务端方法中开线程执行异步任务。
*/
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() {
SystemClock.sleep(5000);
return mBookList;
}
@Override
public void addBook(Book book) {
mBookList.add(book);
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
// C4、权限验证方法二的方式1:没有此权限onTransact()直接返回false
int check = checkCallingOrSelfPermission(PERMISSION_CHECK);
Log.d(TAG, "check=" + check);
if (check == PackageManager.PERMISSION_DENIED) {
return false;
}
// C4、权限验证方法二的方式2:采用UID和PID来验证包名,没有此权限onTransact()直接返回false
String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
if (packageName != null && !packageName.startsWith("com.seniorlibs.binder")) {
return false;
}
return super.onTransact(code, data, reply, flags);
}
// @Override
// public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
// if(!mListenerList.contains(listener)){
// mListenerList.add(listener);
// } else {
// Log.i(TAG,"already exists");
// }
// Log.i(TAG,"registerListener size:" + mListenerList.size());
// }
//
// @Override
// public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
// if(mListenerList.contains(listener)){
// mListenerList.remove(listener);
// Log.i(TAG,"remove listener");
// } else {
// Log.i(TAG,"can not remove listener");
// }
// Log.i(TAG,"unregisterListener size:" + mListenerList.size());
// }
@Override
public void registerListener(IOnNewBookArrivedListener listener) {
// D2、注册监听器IOnNewBookArrivedListener
mListenerList.register(listener);
final int n = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.d(TAG, "registerListener, current size:" + n);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) {
// D2、解注册监听器IOnNewBookArrivedListener
boolean success = mListenerList.unregister(listener);
Log.d(TAG, success ? "unregister success." : "not found, can not unregister.");
// D4、RemoteCallbackList并不是List,不能像操作List操作它,必须beginBroadcast()和finishBroadcast()配对使用
final int n = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.d(TAG, "unregisterListener, current size:" + n);
}
};
/**
* B2、开启线程,每隔20s向感兴趣的用户提醒
*/
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestory.get()) {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
setNewBookArrived(new Book(bookId, "新书:" + bookId));
}
}
}
private void setNewBookArrived(Book book) {
mBookList.add(book);
final int n = mListenerList.beginBroadcast();
for (int i = 0; i < n; i++) {
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
try {
// B4、当有新书的时候,服务端会回调客户端的IOnNewBookArrivedListener对象中的onNewBookArrived方法
listener.onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
}
@Override
public void onDestroy() {
mIsServiceDestory.set(true);
super.onDestroy();
}
}
2.4.5 结果
2.4.6 问题分析
2.4.6.1 服务端的AIDL方法需要做线程同步的原因?
AIDL方法是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问 AIDL方法的情形,所以我们要在AIDL方法中处理线程同步。
2.4.6.2 为什么使用CopyOnWriteArrayList做线程同步?
AIDL中能够使用的List只有ArrayList,但是我们这里却使用了CopyOnWriteArrayList,注意它不是继承ArrayList,为什么能够正常工作呢?这是因为AIDL中所支持的是抽象的List,而List只是一个接口,因此虽然服务端返回的是CopyOnWriteArrayList,但是在Binder中会按照List的规范去访问数据并最终形成一个新的ArrayList传递给客户端。所以,我们在服务端采用CopyOnWriteArrayList是完全可以的。和此类似的还有ConcurrentHashMap。
2.4.6.3 为什么不能使用CopyOnWriteArrayList添加和解注册监听器?
服务端由于无法找到要解除的listener而宣告解注册失败,虽然在注册和解注册过程中使用的是同一个客户端对象,但是通过Binder传递到服务端后会产生两个全新的对象。别忘了对象是不能跨进程直接传输的,对象的跨进程传输本质上都是反序列化的过程,这就是为什么AIDL中的自定义对象都必须要实现Parcelable接口的原因。
2.4.6.4 怎么做才能实现添加和解注册监听器呢?
(1)介绍:RemoteCallbackList是系统专门用来删除listener的接口,RemoteCallbackList是一个泛型,支持管理任意的AIDL接口,因为它的工作原理很简单,在它的内部有一个Map结构专门用来保存所有的AIDL回调,这个Map的key是IBinder类型,value是Callback类型,如下所示。
ArrayMap<IBinder,Callback> mCallbacks = new ArrayMap<IBinder,Callback>();
(2)原理:虽然说多次跨进程传输客户端的同一个对象会在服务端生成不同的对象,但是这些新生成的对象都有一个共同点,那就是他们底层的Binder对象是同一个,当客户端解注册的时候,只要遍历服务端所有的listener,找到那个和解注册listener具有相同Binder对象的服务端listener并把它删掉。
(3)其他功能:
①RemoteCallbackList实现了当客户端终止后,它能够自动移除客户端的listener;
②RemoteCallbackList内部自动实现了线程同步的功能,不需要做额外的线程工作。
(4)例子:RemoteCallbackList并不是List,不能像操作List操作它,必须beginBroadcast()和finishBroadcast()配对使用。如下所示:
@Override
public void registerListener(IOnNewBookArrivedListener listener) {
// D2、注册监听器IOnNewBookArrivedListener
mListenerList.register(listener);
final int N = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) {
// D2、解注册监听器IOnNewBookArrivedListener
boolean success = mListenerList.unregister(listener);
// D4、RemoteCallbackList并不是List,不能像操作List操作它,必须beginBroadcast()和finishBroadcast()配对使用
final int N = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
}
2.4.6.5 Binder如何中断重连服务?
(1)原因:Binder运行在服务端进程,如果服务端进程由于某种原因异常终止,这个时候我们到服务端的Binder连接断裂(称之为Binder死亡),会导致我们的远程调用失败。
(2)解除绑定Binder方法一:如以上代码中的E,给Binder设置Deathleciient监听,当Binder死亡时,我们就会收到binderDied方法的回调,这时就可以重新绑定远程Service从而恢复连接。
(3)解除绑定Binder方法二:如以上代码中的E,在onServiceDisconnected()中重新绑定远程Service。
(4)区别:onServiceDisconnected()方法是在客户端的UI线程中被回调;而binderDied方法在客户端的Binder线程池中被回调,所以在binderDied方法不能访问UI。
2.4.6.6 为什么指定AIDL的方法在子线程中执行?
被客户端调用的远程服务AIDL方法运行在服务端的Binder线程池中,同时客户端线程会被挂起,如果服务端方法执行比较耗时,就会导致客户端线程长时间地阻塞,而如果这个客户端线程是UI线程的话,就会导致客户端ANR。所以,需要指定AIDL的方法在子线程中执行,如果要访问UI需要使用Handler切换到UI线程去执行。
2.4.6.7 如何在AIDL中使用权限验证功能?
(1)原因:默认情况下,我们的远程服务任何人都可以连接,但这不是我们愿意看到的,所以我们必须给服务加入权限验证功能,权限验证失败则无法调用服务中ing的方法。
(2)在onBind中通过自定义权限来验证,如以上代码中的C
①首先要自定义一个权限
private static final String PERMISSION_CHECK = "com.seniorlibs.binder.permission.ACCESS_BOOK_SERVICE";
②然后在Service中的onBind方法中校验,参考如以上代码中的C
(3)在onTransact方法中进行权限校验,如以上代码中的C
(4)学习链接:Android多进程之Binder的意外死亡及权限校验
2.5 使用ContentProvider
(1)ContentProvider基础
ContentProvider是Android中提供的专门用来不同应用之间数据共享的方式,从这一点来看,他天生就是适合进程间通信,和Messenger一样,ContentProvider的底层实现同样也是Binder。
ContentProvider并且实现它的六个方法:onCreate、query、update、 insert和getType,这六个抽象方法都很好理解,onCreate代表ContentProvider的创建,一般我们要做一些初始化工作;getIype用来返回一个Uri请求的MIME类型(媒体类型,比如图片),这个媒体类型还是比较复杂的,如果我们的应用不关注这些选项,可以直接在这个方法中返回null或者/,剩下的四个方法对应于CRUD操作,即实现对数据表的增删查改功能,除了Binder的工作原理,我们知道这六个方法均运行在ContentProvider的进程中,除了onCreate由系统回调并并运行在主线程中,其他五个方法均由外界回调并且运行在Binder线程池中,这一点我们再接下来的例子中可以看到。
(2)具体的实现方式可以参考
Android开发艺术探索——第二章:IPC机制(下)
书本:Android开发艺术探索 P91
Android:关于ContentProvider的知识都在这里了!
2.6 使用Socket
(1)Socket的基础
通过Socket来实现进程通信,Socket也叫做套接字,是网络通信中的概念,他分为流式套接字和用户数据报套接字两种,分别是应用于网络的传输控制层中的Tcp和UDP协议,TCP面向的连接协议,提供稳定的双向通讯功能,TCP连接的建立需要经过“三次握手”才能完成,为了提供稳定的数据传输功能,其本身提供了超时重传机制,因此具有很高的稳定性:而UDP是无连接的,提供不稳定的单向通信功能,当然UDP也可以实现双向通信功能。在性能上,UDP具有更好的效率,其缺点是不保证数据一定能够正确传输,尤其是在网络拥塞的情况下。
(2)具体的实现方式可以参考
Android开发艺术探索——第二章:IPC机制(下)
书本:Android开发艺术探索 P103
2.7 Binder连接池
(1)为什么需要Binder连接池?
随着AIDL数量的增加,我们不能无限制地增Service,Service是四大组件之一,本生是一种系统资源。而且太多的Serice会使得我们的应用看起来很重量级,因为正在运行的Service可以在应用详情页看到。
Binder连接池工作机制是这样的;每个业务模块创建自己的AIDL接口并实现此接口,这个时候不同业务模块之间是不能有耦合的,所有实现细节我们要单独开来,然后向服务端提供自己的唯一标识和其对应的Binder对象;对于服务端来说,只需要一个Service就可以了,服务端提供一个queryBinder接口,这个接口能够根据业务模块的特征来返回相应的Binder对象给它们,不同的业务模块拿到所需的Binder对象后就可以进行远程方法调用了。
由此可见,Binder连接池的主要作用就是将每个业务模块的Binder请求统一转发到远程Service中去执行,从而避免了重复创建Service的过程。
(2)图例
(3)具体的实现方式可以参考
Android开发艺术探索——第二章:IPC机制(下)
书本:Android开发艺术探索 P112
2.8 选用合适的IPC方式
4 参考文章
书本:Android开发艺术探索 P91
Android:关于ContentProvider的知识都在这里了!