🧭 安卓中的多进程模式
💡 多进程模式: 安卓可以通过android:process属性为四大组件开启多进程模式,若以:开头则属于该应用的子进程,否则是另外一个新进程。
注意:开启多进程后,安卓会为每一个应用开启一个独立虚拟机,或者说每一个进程都会有一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,导致数据不共享。
使用多进程造成的问题:
- 静态成员和单例模式完全失效;
- 线程同步机制共享失败;
- SharedPreferences的可靠性下降(SharedPreferences不支持两个进程同时去进行写操作,若如此会有一定几率导致数据丢失);
- Application会多次创建。
🔭 IPC基础概念介绍
1.序列化方式
💡 主要有Parcelable和Serializable接口,通过实现这两个接口可以实现序列化,总的来说,Parcelable的效率要高一些。
2.Binder
💡 Binder是安卓中的一种跨进程的通信方式,实现了IBinder接口,是安卓系统的通信的基石。
从Framework角度来说,Binder是ServiceManager连接各种Manager及相应ManagerService的桥梁,从Android应用层来说,Binder是客户端及服务端进行通信的媒介。当bindService时,服务端会返回包含服务端业务调用的Binder对象,客户端可以获取服务端说提供的服务或数据,这里的服务包括普通服务和AIDL服务。
以AIDL为例进行分析,新建以下代码文件:
//Book.java
package com.example.artandroidlearn;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
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;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
@NonNull
@Override
public String toString() {
return bookId + ":" + bookName;
}
}
// Book.aidl
package com.example.artandroidlearn;
// Declare any non-default types here with import statements
parcelable Book;
// IBookManager.aidl
package com.example.artandroidlearn;
import com.example.artandroidlearn.Book;
import com.example.artandroidlearn.IonNewBookArrivedListener;
// Declare any non-default types here with import statements
interface IBookManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
List<Book> getBookList();
void addBook(in Book book);
}
新建完上述代码后点击运行或编译,系统会根据IBookManager.aidl生成一个名为IBookManager.java
的binder接口,分析该代码并进行拆分:
IBookManager
继承自android.os.IInterface
接口;- 声明了
IBookManager.aidl
的两个方法,如下所示:
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public java.util.List<com.example.artandroidlearn.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.artandroidlearn.Book book) throws android.os.RemoteException;
- 声明了两个内部类
Default
和Stub
Default
类(该类作用未知,待研究)
public static class Default implements com.example.artandroidlearn.IBookManager
{
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public java.util.List<com.example.artandroidlearn.Book> getBookList() throws android.os.RemoteException
{
return null;
}
@Override public void addBook(com.example.artandroidlearn.Book book) throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
Stub
类,Stub
类里面有Proxy
类
public static abstract class Stub extends android.os.Binder implements com.example.artandroidlearn.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.example.artandroidlearn.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.artandroidlearn.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.artandroidlearn.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.artandroidlearn.IBookManager))) {
return ((com.example.artandroidlearn.IBookManager)iin);
}
return new com.example.artandroidlearn.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(descriptor);
java.util.List<com.example.artandroidlearn.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
com.example.artandroidlearn.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.artandroidlearn.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.artandroidlearn.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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public java.util.List<com.example.artandroidlearn.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<com.example.artandroidlearn.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.example.artandroidlearn.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.example.artandroidlearn.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);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBook(book);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.example.artandroidlearn.IBookManager sDefaultImpl;
}
💡 当客户端和服务端位于同一个进程的时候,不会走Stub
的transact过程,否则,则会调用transact过程,而具体逻辑则由Stub
的内部代理类Proxy
来完成。需要注意的是,跨进程通信主要是Stub
类和其内部代理类Proxy
来完成。
2.1 Stub
类分析
Stub()
:构造器,创建以DESCRIPTOR
为名称的Binder对象
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
DESCRIPTOR
:Binder
的唯一标识,一般用类名表示;另外两个是方法ID
private static final java.lang.String DESCRIPTOR = "com.example.artandroidlearn.IBookManager";
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
asInterface
方法:将服务端的Binder对象转换成客户端所需要的AIDL接口类型的对象,其转换是区分进程的,若客户端和服务端位于同一个进程,则返回服务端Stub
本身,否则直接返回Stub.Proxy
对象。
/**
* Cast an IBinder object into an com.example.artandroidlearn.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.artandroidlearn.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.artandroidlearn.IBookManager))) {
//返回服务端Stub对象
return ((com.example.artandroidlearn.IBookManager)iin);
}
//返回Stub.Proxy对象
return new com.example.artandroidlearn.IBookManager.Stub.Proxy(obj);
}
asBinder()
:返回当前的Binder对象
@Override public android.os.IBinder asBinder()
{
return this;
}
onTransact
:此方法运行在服务端的Binder线程池中,客户端发起跨进程请求时,远程请求通过系统底层封装后,由此方法处理。注意onTransact
方法返回false
代表客户端请求失败
/**
*code:表明请求方法是什么
*data: 目标方法所需要的参数,若目标方法有参数的话,则会从该参数里面获取参数
*reply:目标方法执行完之后,通过写入到该参数里面返回
*flags:Additional operation flags. Either 0 for a normal RPC,
* or FLAG_ONEWAY for a one-way RPC.
**/
@Override public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(descriptor);
java.util.List<com.example.artandroidlearn.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
com.example.artandroidlearn.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.artandroidlearn.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
sDefaultImpl
的set和get方法,这里面*sDefaultImpl
* 方法是IBookManager
的一个对象本身
public static boolean setDefaultImpl(com.example.artandroidlearn.IBookManager impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.artandroidlearn.IBookManager getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
Proxy
:内部策略类:客户端本地的Binder对象,这里面实现了在IBookManager
里面定义的方法,通过调用transact
方法最后调用到了onTransact
方法实现了客户端和服务端的通信。
private static class Proxy implements com.example.artandroidlearn.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<com.example.artandroidlearn.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<com.example.artandroidlearn.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
//若_status为false表明客户端请求失败,调用客户端本地实现的方法
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.example.artandroidlearn.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.example.artandroidlearn.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);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBook(book);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.example.artandroidlearn.IBookManager sDefaultImpl;
}
💡 注意客户端发起请求时,当前线程会被挂起直到服务端进程返回数据,因此如果远程请求是耗时方法则不能通过UI线程发起此远程请求,另外,由于服务端的Binder方法运行在Binder线程池中,因此Binder方法不管是否耗时都应该采取同步的方式去实现。
综上,Binder工作机制及流程
根据系统由aidl生成IBookManager.java
方式,用自己的方式进行改写,如下,改写完的基本和原来一致,但是可以不用通过aidl的方式生成:
package com.example.artandroidlearn;
import android.os.IInterface;
public interface IBookManager1 extends IInterface {
public static final String DESCRIPTOR = "com.example.learnartandroid.IBookManager";
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<com.example.artandroidlearn.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.artandroidlearn.Book book) throws android.os.RemoteException;
}
package com.example.artandroidlearn;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import java.util.List;
public class BookManagerImpl extends Binder implements com.example.artandroidlearn.IBookManager1 {
public BookManagerImpl() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.example.artandroidlearn.IBookManager1 asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin != null && iin instanceof BookManagerImpl) {
return (com.example.artandroidlearn.IBookManager1) obj;
}
return new Proxy(obj);
}
@Override
public List<Book> getBookList() throws RemoteException {
return null;
}
@Override
public void addBook(Book book) throws RemoteException {
}
@Override
public IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
List<com.example.artandroidlearn.Book> result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(descriptor);
com.example.artandroidlearn.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.artandroidlearn.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.artandroidlearn.IBookManager1 {
private IBinder mRemote;
public Proxy(IBinder mRemote) {
this.mRemote = mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public List<Book> getBookList() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
List<Book> result;
try {
data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_getBookList, data, reply, 0);
reply.readException();
result = reply.createTypedArrayList(Book.CREATOR);
} finally {
reply.recycle();
data.recycle();
}
return result;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (book != null) {
data.writeInt(1);
book.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(TRANSACTION_addBook, data, reply, 0);
reply.readException();
} finally {
reply.recycle();
data.recycle();
}
}
@Override
public IBinder asBinder() {
return mRemote;
}
}
}