Android多进程
为什么要使用多进程
-
系统为App每一个进程分配的内存是有限的,超过系统分配的内存上限就会出现OOM。
-
当加载比较大的图片预览时
-
后台播放音乐
开启多进程模式
- 单个应用使用多进程只有一种方法,那就是给四大组件在AndroiMenifest中指定
android:process
属性。另外还有就是可以通过JNI的native层去fork一个新的进程。 - 如果没有特别的指定运行的进程,那么运行的进程名字为自己的包名
- 若设置
android:process=":remote"
,”:”的含义是指要在当前的进程名前加上当前的包名,则进程名为<package>:remote
,并且以”:”开头的进程属于当前应用的私有进程。 - 若设置为别的形式,例如
com.jiangker.process
,则这种进程为全局进程,其他应用可以通过ShareUID的方式可以和它跑到同一个进程中。Android系统会为每个应用分配一个唯一的UID,具有相同UID的应用才能共享数据。
多进程带来的问题
1 静态成员和单例模式完全失效
因为Android系统给每一个应用都分配了一个独立的虚拟机,所以内存空间是不相同的,所以不能通过内存来共享数据。
2 线程同步机制完全失效
线程中的同步机制作用于的是同一个对象,因为不是在一个进程中了,对象也不相同了,所以不能使用
3 SharedPreferences的可靠性下降
SharedPreferences不支持多个进程去同时读写xml文件,否则会导致一定几率的数据丢失。
4 Application会多次创建
多进程的通信方式
Linux经典进程间通信方式
- 共享内存(Shared Memory)
- 管道(Pipe)
- UNIX Domain Socket
Android中常用进程通信方式
- Intent 、Bundle :要求传递数据能被序列化,实现 Parcelable、Serializable ,适用于四大组件通信。
- 文件共享 :适用于交换简单的数据实时性不高的场景。
- AIDL:AIDL 接口实质上是系统提供给我们可以方便实现 BInder 的工具。
- Messenger:基于 AIDL 实现,服务端串行处理,主要用于传递消息,适用于低并发一对多通信。
- ContentProvider:基于 Binder 实现,适用于一对多进程间数据共享。(通讯录 短信 等)
- Socket:TCP、UDP,适用于网络数据交换
Binder的通信过程
- server在SM容器中注册、
- Client若要调用Server的add方法,就需要先获取server对象,但是SM不会把真正的server对象返回给Client,而是把Server的一个代理对象Proxy返回给Client。
- Client调用Proxy的add方法,ServiceManager会帮它去调用Server的add方法,并把结果返回给Client
AIDL跨进程通信
1 默认支持的数据类型
- Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。
- String类型和CharSequence类型。
- List类型:只支持ArrayList,里面的每个元素都必须要被AIDL支持;
- Map类型:只支持HashMap,里面的每个元素都必须要被AIDL支持,包括key和value;
- Parcelable:所有实现了Parcelable接口的对象;
- AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
其中自定义的Parcelable对象和AIDL对象必须要显示的import进来
2 in out inout的区别
AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中in表示数据只能由客户端流向服务端,out表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的参数为空的对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。
3 使用示例
3.1 AIDL文件的定义
aidl文件路径下
1
2
3
4
//Book.aidl
package com.jiangker.testitem.lib;
parcelable Book;
对应java文件路径下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//Book.java
package com.jiangker.testitem.lib
import android.os.Parcel
import android.os.Parcelable
class Book(
var name: String?,
var price: Int
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readInt()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name)
parcel.writeInt(price)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Book> {
override fun createFromParcel(parcel: Parcel): Book {
return Book(parcel)
}
override fun newArray(size: Int): Array<Book?> {
return arrayOfNulls(size)
}
}
}
AIDL接口定义
1
2
3
4
5
6
7
8
9
//IBookManager.aidl
package com.jiangker.testitem.lib;
import com.jiangker.testitem.lib.Book;
interface IBookManager{
List<Book> getBookList();
void addBook(in Book book);
}
3.2 Server端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MainService : Service() {
private val bookLists = ArrayList<Book>()
private val manager = object : IBookManager.Stub() {
override fun addBook(book: Book) {
bookLists.add(book)
}
override fun getBookList(): MutableList<Book> {
return bookLists
}
}
override fun onBind(intent: Intent) = manager
}
3.3 Client端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MainActivity : AppCompatActivity() {
private var bookService: IBookManager? = null
private val connection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
bookService = IBookManager.Stub.asInterface(service)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
//绑定服务
bindService(Intent(this,MainService::class.java),connection,Context.BIND_AUTO_CREATE)
binding.apply {
btnInsert.setOnClickListener
val book = Book("jiangker ${SystemClock.uptimeMillis()}", 528)
bookService?.addBook(book)
Log.i(TAG, "addBook $book - ${book.hashCode()}")
}
btnQuery.setOnClickListener {
bookService?.bookList?.forEachIndexed {index,book ->
Log.i(TAG, "list index: $index : - : $book - ${book.hashCode()}")
}
}
}
}
}
对应的生成的Java文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
public interface IBookManager extends IInterface
{
/** server端继承的抽象类*/
public static abstract class Stub extends Binder implements IBookManager
{
//binder的唯一标识
private static final String DESCRIPTOR = "com.jiangker.testitem.lib.IBookManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* 判断IBinder对象是否和自己处于同一进程,如果是就直接使用,否则把服务端的IBinder包装为一个客户端所需的AIDL接口的Proxy代理对象
*/
public static IBookManager asInterface(IBinder obj) {
if ((obj==null)) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof IBookManager))) {
return ((IBookManager)iin);
}
return new IBookManager.Stub.Proxy(obj);
}
//返回当前Binder对象
@Override
public IBinder asBinder() {
return this;
}
/**
* 接收客户端传递过来的数据。运行在服务端中的Binder线程池中,当客户端发起请求时,远程请求会通过系统底层封装后交由此方法来处理
*/
@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<Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
Book _arg0;
if ((0!=data.readInt())) {
_arg0 = Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_registerListener:
{
data.enforceInterface(descriptor);
IOnNewBookArrivedListener _arg0;
_arg0 = IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
this.registerListener(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unRegisterListener:
{
data.enforceInterface(descriptor);
IOnNewBookArrivedListener _arg0;
_arg0 = IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
this.unRegisterListener(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements IBookManager
{
private IBinder mRemote;
Proxy(IBinder remote)
{
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
//运行在客户端,会把该方法的信息参数打包发给server端,等待方法返回
@Override
public List<Book> getBookList() throws RemoteException {
//把要传递的数据写入_data
Parcel _data = Parcel.obtain();
//把要接收的函数返回值写入_reply
Parcel _reply = Parcel.obtain();
List<Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//最后使用transact方法,就可以把数据传递给Binder的server端了
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_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);
}
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();
}
}
@Override public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException
{
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
boolean _status = mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().registerListener(listener);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException
{
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
boolean _status = mRemote.transact(Stub.TRANSACTION_unRegisterListener, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().unRegisterListener(listener);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static IBookManager sDefaultImpl;
}
static final int TRANSACTION_getBookList = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_registerListener = (IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_unRegisterListener = (IBinder.FIRST_CALL_TRANSACTION + 3);
public static boolean setDefaultImpl(IBookManager impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static IBookManager getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public List<Book> getBookList() throws RemoteException;
public void addBook(Book book) throws RemoteException;
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException;
public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException;
}
- Stub继承至binder,需要由提供服务的一方实现,最后返回binder对象给SM
- Proxy是接口的代理,当Client通过asInterface(IBinder)去获取接口对象时,在同一进程会直接返回实际对象,若不在同一进程就会返回服务端Proxy的代理对象,Proxy是组合模式,会持有远程Binder的引用。
- 当调用server的方法时,会把数据进行序列化,然后调用远程binder的transact方法来把数据写入,如何阻塞等待server的结果返回。
- 当方法进行调用时,服务端的onTransact方法会被执行,然后根据code去解析参数然后再调用具体的方法实现,最后再把返回结果写入返回。
- binder的线程都为子线程。binder的调用可以在主线程执行,会阻塞等待结果返回。
死亡代理
binder提供两个方法,通过linkToDeath就可以给Binder设置一个死亡代理,在Binder死亡时通知我们,这个触发是在客户端的Binder线程池里面的,不可以进行UI操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//定义死亡代理
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (bookServer == null) return;
bookServer.asBinder().unlinkToDeath(mDeathRecipient,0);
bookServer = null;
// binderServer();
}
};
//设置绑定
private ServiceConnection connection = new ServiceConnection{
@Override
public void onServiceDisConnected(ComponentName name){}
@Override
public void onServiceConnected(ComponentName name,IBinder service){
service.linkToDeath(mDeathRecipient,0)
}
}
也可以在onServiceDisConnected中进行重连操作,这个是主线程中触发的
权限校验
在服务端定义权限
1
2
3
4
5
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sourcecode">
<permission android:name="com.jinagker.permission.Test"
android:protectionLevel="normal"/>
</manifest>
可以在两个地方进行拦截
- onBind方法处(使用Context类的
checkCallingOrSelfPermission(String permission)
方法)1 2 3 4 5 6
override fun onBind(intent: Intent): IBinder? { val result = checkCallingOrSelfPermission("com.jinagker.permission.Test") if (result == PackageManager.PERMISSION_DENIED) return null return messageManager.binder }
使用,需要申明权限
1
<uses-permission android:name="com.jinagker.permission.Test"/>
-
在服务端的onTransact中进行验证(重写服务端的onTransact方法,校验失败返回false)
1 2 3 4 5 6 7 8
val server = object : ISaveSp.Stub() { override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean { val result = checkCallingOrSelfPermission("com.jinagker.permission.Test") if (result == PackageManager.PERMISSION_DENIED) return false return super.onTransact(code, data, reply, flags) } }
- 直接在服务端属性处增加
android:poermission
,没有申明的onServiceConnected
不会有返回
Messenger方式
Messenger其实也是对Binder的一种封装,结合handler的方式,使用message来携带消息。
1
2
3
4
5
6
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private val messenger = Messenger(MessengerHandler())
override fun onBind(intent: Intent): IBinder {
return messenger.binder
}
class MessengerHandler : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MessengerActivity.CLIENT_MSG -> {
// replyTo也是一个messenger对象
val client = msg.replyTo
val message = Message.obtain(null, SERVER_MSG)
val bundle = Bundle()
bundle.putParcelable("bitmap", Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888))
message.data = bundle
client.send(message)
}
else -> super.handleMessage(msg)
}
}
}
跨进程传递大图片
- 对于如果图片本为本地图片的,可以直接传递文件路径。
- 若是下载到内存中的图片,写入储存再传递路径,再在另外的进程读取,这样的方式性能很低。
- 使用Socket、管道等方式需要两次拷贝,从用户空间到内核空间,再从内核空间到用户空间。
- 使用binder
若使用intent传递数据,数据量比较大时,会抛出异常
1
2
3
val intent = Intent(this, PoolService::class.java)
intent.putExtra("bitmap",Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888))
bindService(intent, connection, Context.BIND_AUTO_CREATE)
正常情况推荐使用intent的putBinder方式来传递,然后通过binder中的bitmap方法传递数据
1
2
3
4
5
6
7
package cn.jiangker.android.ipc.pool;
import android.graphics.Bitmap;
interface BitmapTranslate{
void translate(in Bitmap origin);
}
大致原因为intent方式传递时,系统会禁止掉文件引用符的传递方式,强制走序列化和反序列化流程。而使用binder传递时,若文件大于16kb时,会开辟一个匿名共享内存,无需拷贝,提高传递效率。
参考
代码地址
https://gitlab.jiangker.cn/jiangker/androidipc