android一个apk多个进程,Android多进程

前言

好久没有写学习心得了,最近看了Android多进程相关的知识,是时候总结一下了,也方便以后自己温习,我主要围绕以下几点展开:

为何使用ipc

两种序列化的区别

Binder简单了解

实现多进程的方式有哪些

IPC的必要性

如果要用到ipc,那么必须有多进程的存在,为何要使用多进程呢,这里给出两点:

防止oom,考虑增加应用的使用内存,一个应用分得的内存是有限的,我们为了增加应用的内存,将一些模块单独放在进程中去,这样系统就会给这些模块单独分配内存,降低应用oom的概率。

跨应用获取数据,有时候想获取其他应用中的一些数据,比如联系人的信息,这些信息在其他模块,我们肯定不能直接获取到,这就需要ipc了,事实上ContentProvier也是多进程通信的方式,只是一些细节被屏蔽掉了,我们无法感知而已。

序列化

当使用多进程传递数据的时候,为了数据的一致性,我们就必须要将对象序列化和反序列化,序列化不是Android中的新概念,在Java中就有,序列化有两种方式Serializable和Parcelable接口。

1.Serializable接口

java序列化就是将实现了Serializable的对象转换成字符序列形式保存在文件中,在需要的时候再将字符序列转换为对象恢复原来的结构,即反序列化。这样我们既然是字符序列,我们就可以很方便的传输了,比如intent,多进程,甚至网络通信等等。

//序列化操作1--FileOutputStream

ObjectOutputStream oos1 = new ObjectOutputStream(new FileOutputStream("worm.out"));

oos1.writeObject("Worm storage By FileOutputStream ");

oos1.writeObject(w);//必须所有引用的对象都实现序列化(本例终究是Data这个类),否则抛出有java.io.NotSerializableException:这个异常

oos1.close();

//反序列化操作1---FileInputStream

ObjectInputStream ois1 = new ObjectInputStream(new FileInputStream("worm.out"));

String s1 = (String)ois1.readObject();

Worm w1 = (Worm)ois1.readObject();

ois1.close();

System.out.println("反序列化操作1之后");

System.out.println(s1);

System.out.println("w1:"+w1);

复制代码public class Data implements Serializable {

private static final long serialVersionUID = 7247714666080613254L;

public int n;

public Data(int n) {

this.n = n;

}

public String toString(){

return Integer.toString(n);

}

}

复制代码

以上就是对实现了Serializable的对象进行序列化和反序列化,可以看出,序列化和反序列化就是将对象写入文件和对文件进行读取的过程,这也太明了了,就是这么简单,需要注意的是,所有的操作都是针对字节。

序列化操纵之前

w=:a(853):b(119):c(802):d(788):e(199):f(881)

反序列化操作1之后

Worm storage By FileOutputStream

w1::a(853):b(119):c(802):d(788):e(199):f(881)

复制代码

这里说明一下,serialVersionUID,这个玩意我们经常看到,它是干嘛的,既然系统加入这个肯定是有它的作用的啦,这个很重要,我们进行序列化的时候也会写入这个值,当在反序列化的时候我们会对这个值做个比较,如果一致就可以成功反序列化操作,否则就会抛异常。

Parcelable

在Android中,出现了一种新的序列化方法,那就是使用Parcelable,使用它我们就可以很方便在intent,binder传递数据啦,使用方式很简单。

public class FilterEntity implements Parcelable{

private String name;

private String chooseStr;

private boolean hasSelected;

public FilterEntity(String name, String chooseStr, boolean hasSelected) {

this.name = name;

this.chooseStr = chooseStr;

this.hasSelected = hasSelected;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getChooseStr() {

return chooseStr;

}

public void setChooseStr(String chooseStr) {

this.chooseStr = chooseStr;

}

public boolean isHasSelected() {

return hasSelected;

}

public void setHasSelected(boolean hasSelected) {

this.hasSelected = hasSelected;

}

@Override

public String toString() {

return "FilterEntity{" +

"name='" + name + '\'' +

", chooseStr='" + chooseStr + '\'' +

", hasSelected=" + hasSelected +

'}';

}

@Override

public int describeContents() {

return 0;

}

@Override

public void writeToParcel(Parcel dest, int flags) {

dest.writeString(this.name);

dest.writeString(this.chooseStr);

dest.writeByte(this.hasSelected ? (byte) 1 : (byte) 0);

}

public FilterEntity() {

}

protected FilterEntity(Parcel in) {

this.name = in.readString();

this.chooseStr = in.readString();

this.hasSelected = in.readByte() != 0;

}

public static final Creator CREATOR = new Creator() {

@Override

public FilterEntity createFromParcel(Parcel source) {

return new FilterEntity(source);

}

@Override

public FilterEntity[] newArray(int size) {

return new FilterEntity[size];

}

};

}复制代码

两种序列化的比较

Serializable是java的序列化方式,使用起来简单,但是开销比较大,在序列化和反序列化的时候会进行大量的读写操作。

Pacelable是android独有的,所以在Android平台下肯定是首选啦,它使用起来稍微复杂一点,但是非常高效。

Parcelable主要是将对象序列化到内存中,而Serializable主要是序列化到存储设备和网络通信中。

Binder

Binder是一个很复杂的概念,我们这里主要是谈谈它的上层应用使用方式,Binder是进程之间通信的桥梁,这里给一个远程service

public class BookManagerService extends Service {

private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList();

// private CopyOnWriteArrayList mListenList = new CopyOnWriteArrayList();

private RemoteCallbackList mListenList = new RemoteCallbackList<>();

@Nullable

@Override

public IBinder onBind(Intent intent) {

return mBinder;

}

private Binder mBinder = new IBookManager.Stub() {

@Override

public List getBookList() throws RemoteException {

Log.d("wangchao0000", "getBookList: ");

return mBookList;

}

@Override

public void addBook(Book book) throws RemoteException {

mBookList.add(book);

}

@Override

public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

//可以做一些事,比如包验证

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

}

};

@Override

public void onCreate() {

super.onCreate();

Log.d("wangchao0000", "onCreate: ");

// mBookList.add(new Book(1, "开发艺术探索"));

// mBookList.add(new Book(2, "Flutter进阶"));

ExecutorService executorService = Executors.newScheduledThreadPool(1);

((ScheduledExecutorService) executorService).scheduleAtFixedRate(new Runnable() {

@Override

public void run() {

Log.d("wangchao0000", "run: ");

int bookId = mBookList.size() + 1;

notifyNewBookArrived(new Book(bookId, "new book--" + bookId));

}

},0, 5, TimeUnit.SECONDS);

}

}

复制代码

Manifest文件如下:

android:process=":remote"

android:enabled="true"

android:exported="true"

/>

复制代码

这样BookManagerService就运行在远程服务了。接下来就看客户端怎么和远程服务端怎么去通信了,我们新建一个AIDL文件,这个接口文件中去声明几个我们需要的方法。

interface IBookManager {

List getBookList();

void addBook(in Book book);

}

复制代码

系统会根据这个AIDL文件自动生成Binder类,这个类就是我们的核心类,利用它客户端和服务端就可以通信了哈,想起来就是兴奋,我们把这个类梳理一下就ok了。先上代码:

/*

* This file is auto-generated. DO NOT MODIFY.

* Original file: /Users/wangchao/code/TestProject/app/src/main/aidl/com/example/wangchao/testproject/IBookManager.aidl

*/

package com.example.wangchao.testproject;

// Declare any non-default types here with import statements

public interface IBookManager extends android.os.IInterface

{

/** Local-side IPC implementation stub class. */

public static abstract class Stub extends android.os.Binder implements com.example.wangchao.testproject.IBookManager

{

private static final java.lang.String DESCRIPTOR = "com.example.wangchao.testproject.IBookManager";

/** Construct the stub at attach it to the interface. */

public Stub()

{

this.attachInterface(this, DESCRIPTOR);

}

/**

* Cast an IBinder object into an com.example.wangchao.testproject.IBookManager interface,

* generating a proxy if needed.

*/

public static com.example.wangchao.testproject.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.wangchao.testproject.IBookManager))) {

return ((com.example.wangchao.testproject.IBookManager)iin);

}

return new com.example.wangchao.testproject.IBookManager.Stub.Proxy(obj);

}

@Override public android.os.IBinder asBinder()

{

return this;

}

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException

{

switch (code)

{

case INTERFACE_TRANSACTION:

{

reply.writeString(DESCRIPTOR);

return true;

}

case TRANSACTION_getBookList:

{

data.enforceInterface(DESCRIPTOR);

java.util.List _result = this.getBookList();

reply.writeNoException();

reply.writeTypedList(_result);

return true;

}

case TRANSACTION_addBook:

{

data.enforceInterface(DESCRIPTOR);

com.example.wangchao.testproject.data.Book _arg0;

if ((0!=data.readInt())) {

_arg0 = com.example.wangchao.testproject.data.Book.CREATOR.createFromParcel(data);

}

else {

_arg0 = null;

}

this.addBook(_arg0);

reply.writeNoException();

return true;

}

}

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

}

private static class Proxy implements com.example.wangchao.testproject.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 getBookList() throws android.os.RemoteException

{

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

java.util.List _result;

try {

_data.writeInterfaceToken(DESCRIPTOR);

mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);

_reply.readException();

_result = _reply.createTypedArrayList(com.example.wangchao.testproject.data.Book.CREATOR);

}

finally {

_reply.recycle();

_data.recycle();

}

return _result;

}

@Override public void addBook(com.example.wangchao.testproject.data.Book book) throws android.os.RemoteException

{

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

try {

_data.writeInterfaceToken(DESCRIPTOR);

if ((book!=null)) {

_data.writeInt(1);

book.writeToParcel(_data, 0);

}

else {

_data.writeInt(0);

}

mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);

_reply.readException();

}

finally {

_reply.recycle();

_data.recycle();

}

}

}

static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

public java.util.List getBookList() throws android.os.RemoteException;

public void addBook(com.example.wangchao.testproject.data.Book book) throws android.os.RemoteException;

}

复制代码

咋一眼看到这个代码是不是很头大,是的,我也很头痛,系统自动生成的,不过我们来梳理一下:这个类继承类IInterface,一旦继承了这个接口,系统就认为它是AIDL:

/**

* Base class for Binder interfaces. When defining a new interface,

* you must derive it from IInterface.

*/

public interface IInterface

{

/**

* Retrieve the Binder object associated with this interface.

* You must use this instead of a plain cast, so that proxy objects

* can return the correct result.

*/

public IBinder asBinder();

}

复制代码

没啥啊,里面就是只有asBinder一个方法,该方法就是返回binder对象的。

接下来看看:

public java.util.List getBookList() throws android.os.RemoteException;

public void addBook(com.example.wangchao.testproject.data.Book book) throws android.os.RemoteException;

复制代码

仔细一看,这不就是咱们在AIDL文件中定义的那两个方法嘛,这里对它进行了声明,看来在这个类中肯定是要用到了。然后定义了几个整型变量,用来告诉服务端目前客户端是调用的哪一个方法,不然服务端怎么知道呢。

接着看,重点来了,就是那个stub,这个概念我们并不陌生,它时常在service中出没:

private Binder mBinder = new IBookManager.Stub() {

@Override

public List getBookList() throws RemoteException {

Log.d("wangchao0000", "getBookList: ");

return mBookList;

}

@Override

public void addBook(Book book) throws RemoteException {

mBookList.add(book);

}

@Override

public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

//可以做一些事,比如包验证

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

}

};

复制代码

是吧,我没说错把。接下来我们就分析分析了。

它是一个内部类,它就是一个Binder类,它实现了IInterface接口。这个类中有几个重要的方法:

asInterface

onTransact

asBinder

Proxy,它是一个内部类

咱们分别来分析他们的实现逻辑:

asInterface

我们先来看看asInterface方法,用来获取服务端对象:

/**

* Cast an IBinder object into an com.example.wangchao.testproject.IBookManager interface,

* generating a proxy if needed.

*/

public static com.example.wangchao.testproject.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.wangchao.testproject.IBookManager))) {

return ((com.example.wangchao.testproject.IBookManager)iin);

}

return new com.example.wangchao.testproject.IBookManager.Stub.Proxy(obj);

}

复制代码

它是一个静态方法,调用的地方是在客户端,注释写得很清楚,如果客户端和服务端在同一进程中,直接返回service对象,否则返回Proxy。事实上,既然写这个类,那肯定是不在一个进程了,那么proxy就有用武之地了。

Proxy

Proxy是stub的内部类,它实现了AIDL接口,Proxy对象是返回给客户端的,客户端拿到后去调用里面

的方法:

ServiceConnection serviceConnection = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

bookManager = IBookManager.Stub.asInterface(service);

try {

// Log.d("BookMnagerActivity", "query book list: -------" + Arrays.asList(list));

// list.add(new Book(3, "web进阶"));

// Log.d("BookMnagerActivity", "query book list: -------" + Arrays.asList(bookManager.getBookList()));

bookManager.registerListen(onNewBookArriveListen);

} catch (RemoteException e) {

e.printStackTrace();

}

}

@Override

public void onServiceDisconnected(ComponentName name) {

}

};

复制代码

这个bookManager就是proxy的对象,这里可以通过断点进去看。

我们选一个方法去分析吧:

private static class Proxy implements com.example.wangchao.testproject.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 getBookList() throws android.os.RemoteException

{

android.os.Parcel _data = android.os.Parcel.obtain();

android.os.Parcel _reply = android.os.Parcel.obtain();

java.util.List _result;

try {

_data.writeInterfaceToken(DESCRIPTOR);

mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);

_reply.readException();

_result = _reply.createTypedArrayList(com.example.wangchao.testproject.data.Book.CREATOR);

}

finally {

_reply.recycle();

_data.recycle();

}

return _result;

}

}

复制代码

当客户端去调用getBookList方法的时候,它就执行到这里,这里声明了_data, _reply, _result。调用过程如下:

首先把之前定义好的int标识符写到_data里面去,以便服务端去识别。

调用远程服务的transact,这时候客户端进程挂起,直到服务端执行完ontransact方法,返回结果之后才能唤醒。

返回结果后,唤醒,将数据返回给客户端的调用处。

onTransact

前面说了,客户端调用了服务端的transact方法,该方法属于Binder的方法,在Binder线程池中

mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);

复制代码

接下来看看,服务端干了啥。

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException

{

switch (code)

{

case INTERFACE_TRANSACTION:

{

reply.writeString(DESCRIPTOR);

return true;

}

case TRANSACTION_getBookList:

{

data.enforceInterface(DESCRIPTOR);

java.util.List _result = this.getBookList();

reply.writeNoException();

reply.writeTypedList(_result);

return true;

}

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

}

复制代码

该方法中,它会去根据code去判断调用哪个方法,这个code就是之前声明的那个id,哦,nice,原来这么简单:

根据code去查找需要调用的方法

调用服务端的getBookList

将返回的结果写到reply里面去,然后唤醒客户端

proxy和onTransact不就是两个相互应答的过程嘛。

这里需要说明一下,

客户端发起请求后当前线程就会被挂起,直到服务端进程返回结果,如果远程方法执行很耗时的操作,就会造成ANR,所以发起请求的线程不能是UI线程哈。

Binder方法运行在线程池中,这里要使用线程同步。

其实大家经常说的AIDL,有个啥用,它就是个工具而已,利用这个工具系统可以帮我们生成Binder类,但这个AIDL并不是必须的,我们可以自己去写Binder类。

Android中IPC的几种方式

Bundle

文件共享

Messenger

AIDL

ContentPrider

Bundle

相信大家对这个都不陌生,在Activity,Service使用intent传递数据的时候经常会用到,只要这个对象是已经序列化了的,它就可以放到Bundle里去传输,这不就是ipc了嘛。

文件共享

这个很简单,多个进程或者是应该去读写同一个文件,我们把序列化的对象写进去,另外一个进程从这个文件从去读取,然后再反序列化,就可以很顺利的拿到数据了。

Messenger AIDL

上面将binder的时候已经分析过AIDL了,不懂的大家可以专门去学学AIDL的使用,Messenger就是封装了AIDL而已,没啥好讲的。

ContentPrider

这玩意不就是Android四大组建之一吗,对,就是它,它天然带了IPC属性,比如获取联系人模块里面的数据。

好了,也写了不少了,主要理解原理,至于怎么使用不是这片文章的重点。

总结

序列化和反序列化就是将对象转换为字符序列存储到某个文件,之后就是读写文件获取,方便传输而已。

IPC要抓住那个桥梁,就是Binder,binder里面的方法就是服务端定义的方法,我们作为客户端就是想调用服务端的方法来实现一些功能,如果我们拿到了Binder,还愁不能调用服务端的方法吗,所以。。。。。

不要在UI线程中去bind 服务,因为远程进程在干啥,我们是不知道的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值