Android中的多进程模式
开启多进程模式
我们想要了解的是一个应用的多个进程的情况,Android使用多进程通过在配置四大组件的时候配置android:process属性。代码示例创建出一个最简单的多进程的应用。
<activity
android:name=".FirstActivity"
android:configChanges="orientation|screenSize"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity"
android:process=":remote"/>
<activity android:name=".ThirdActivity"
android:process="com.test.remote"/>
使用adb shell ps|grep test命令查看进程名包含test字段的进程的信息:
进程的名字后缀的命名我们很容易看出默认的进程名为包名,使用:remote的为在包名后面添加,自己使用包含.命名的为自定义的全部名称。
注意:Android系统会为每一个应用分配一个唯一的UID,具有相同UID的应用可以共享数据,通过在Application配置android:shareUserId来指定ID,前提是签名也相同。这种情况不管应用是否在一个进程中,实际上就变成了两个应用不同进程但是可以共享数据。
多进程模式的运行机制
我们知道Android是基于Linux系统的,并且每一个进程启动的时候会分配独立的虚拟机和内存信息。这个时候当启动多进程模式的时候就会产生许多数据无法同步的问题,例如一个静态变量在多进程模式下改变值,这个时候每个进程会拷贝静态变量所属类的信息,独立的存储在自己的运行的进程的内存中。多进程会引发下面一些问题:
- 静态成员和单例模式实效:对类进行内存拷贝一份。
- 线程同步机制实效:都不属于同一个锁了。
- SharedPreferences可靠性下降:SharedPreferences原理是在内存进行一次缓存,然后在对文件进行读写,这个时候可能发生多进程读写缓存后错乱。
- Application多次创建:每个进程会有一个独立的虚拟机,有独立的内存,独立的Application对象。
IPC基础概念
其中序列化的概念主要是通过Serializable和Parcelable接口来实现序列。然后使用Intent和Binder来进行传输。还有一个我一直觉得很难的点Binder。
参考详细解析:Binder源码解析
我们主要掌握使用AIDL来分析Binder的工作机制,掌握常规AIDL的使用。
生成一个简单的AIDL来了解它们的一些原理:
代码创建:
实例bean对象Book.java
package com.lyman.ipc.bean;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Author: lyman
* Email: lymenye@gmail.com
* Date: 2017/3/13
* Description:
*/
public class Book implements Parcelable {
private int mBookId;
private String mBookName;
public Book(int bookId,String bookName){
this.mBookId = bookId;
this.mBookName = bookName;
}
protected Book(Parcel in) {
mBookId = in.readInt();
mBookName = in.readString();
}
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];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mBookId);
dest.writeString(mBookName);
}
}
要引用对象还得声明在aidl中的Book.java对象对应在aidl中Book.aidl:
// Book.aidl
package com.lyman.ipc.bean;
import com.lyman.ipc.bean.Book;
// Declare any non-default types here with import statements
parcelable Book;
最后生成实际操作的IBookManager.aidl,这是我们要系统生成的aidl实现文件:
// IBookManager.aidl
package com.lyman.ipc;
import com.lyman.ipc.bean.Book;
// Declare any non-default types here with import statements
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
注意这里对于要引用的Book.java对象对应的Book.aidl文件的包名要相同,否则找不到。导入对象和导包都要注意否则会出现异常。aidl文件的生成通过AS生成aidl文件。包目录结构为:
其中系统生成的aidl的实现在这:
创建好了上面的Binder的实现以后就可以在不同进程中使用了,我们常见的是在Service中的onBinder中提供调用,供不同的进程来使用。我认为Binder好比一个Android系统底层提供的大仓库,提供数据中转供我们的不同进程中来跨进程调用。
Android中的IPC方式
使用Bundle:注意和上面的Binder做区分,长的像。但是Bundle是我们在Intent中传递数据最常使用的。
使用文件共享:在确定不需要考虑同步操作的时候可以使用这种方式,而且Android中对文件的读写不会像Window加上排斥锁导致别人不能使用这个文件。如果不会同时对一个文件有读写的操作,可以尝试它。同样SharedPreferences也是对XML文件的读写,而且还在Android内存中提供缓存,当多进程的时候很容易导致数据不同步的问题。
使用Messenger:对这个东西开始很陌生,就听过Handler中使用的Message对象,它们两又是长得比较像。Messenger可以认为是我们上面了解到的aidl的Android的工具类实现,因为它是在aidl的基础上面实现的,我们可以直接来使用。实现客户端请求然后服务端回答的功能如下:
服务端:创建Messenger对象,并且创建Handler和这个Messenger对象进行关联;在handle中获取到客户端的消息后,在Message中调用Message.replyTo拿到可以发送消息给客户端的Messenger对象,向其中发送响应。
客户端:首先bind远程的跨进程服务;在onServiceConnected中根据IBinder参数来构建可以向服务端发送消息的Messenger对象发送请求消息;发送的Message中对replyTo进行赋值,这个值为在客户端创建Messager和接受服务器响应消息的Handler来拿到服务端的响应。
这个文字看的很懵圈,直接看看代码:
服务端代码:
package com.lyman.ipc.service;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
/**
* Author: lyman
* Email: lymenye@gmail.com
* Date: 2017/3/14
* Description:
*/
public class MessengerService extends Service{
private static final String TAG = "MessengerService";
public static final int FROM_CLIENT = 0x01;
public static final int FROM_SERVER = 0x02;
private Messenger mMessenger;
private Handler mMessengerHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case FROM_CLIENT:
Log.i(TAG,msg.getData().getString("data"));
Messenger messenger = msg.replyTo;
Message message = Message.obtain();
message.what = FROM_SERVER;
Bundle bundle = new Bundle();
bundle.putString("data","i'm form server reply");
message.setData(bundle);
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
super.handleMessage(msg);
}
};
@Override
public void onCreate() {
super.onCreate();
mMessenger = new Messenger(mMessengerHandler);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
客户端代码:
package com.lyman.ipc;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import com.lyman.ipc.service.MessengerService;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Messenger mServerMessenger;
private Messenger mMyMessenger;
private Handler mMyMessengerHander = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MessengerService.FROM_SERVER:
Log.i(TAG, "handleMessage: "+msg.getData().getString("data"));
break;
}
super.handleMessage(msg);
}
};
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected: success");
mServerMessenger = new Messenger(service);
mMyMessenger = new Messenger(mMyMessengerHander);
sendMessage();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private void sendMessage() {
try {
Message message = new Message();
message.what = MessengerService.FROM_CLIENT;
Bundle bundle = new Bundle();
bundle.putString("data", "i'm form client");
message.setData(bundle);
message.replyTo = mMyMessenger;
mServerMessenger.send(message);
Log.i(TAG, "onServiceConnected: send message success");
} catch (RemoteException e) {
Log.i(TAG, "onServiceConnected: send message failed");
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void bindService(View view) {
if(mServerMessenger != null){
sendMessage();
}else{
bindService(new Intent(this, MessengerService.class),
mServiceConnection, Service.BIND_AUTO_CREATE);
}
}
}
这样就生成了一个客户端和服务端通信的demo,这个我们可以这样理解Messenger:它就像一个可以发送以Message为载体的有序的一个管道,并且从服务端到客户端和从客户端到服务端它们使用不同的Messager管道来进行消息的发送和接收处理。
4.使用aidl:继续上面aidl简单使用的例子,我们来扩展它的跨进程来进行调用。
- 服务端:
package com.lyman.ipc.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import com.lyman.ipc.IBookListenner;
import com.lyman.ipc.IBookManager;
import com.lyman.ipc.bean.Book;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class BookService extends Service {
private static final String TAG = "BookService";
private CopyOnWriteArrayList<Book> mDatas = new CopyOnWriteArrayList<>();
//刚开始使用普通的集合来存储监听器集合
// private List<IBookListenner> mListenners = new ArrayList<>();
private RemoteCallbackList<IBookListenner> mListenners = new RemoteCallbackList<>();
//binder实现
private IBookManager.Stub iBookManager = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mDatas;
}
@Override
public void addBook(Book book) throws RemoteException {
mDatas.add(book);
}
@Override
public void registBookListenner(IBookListenner listenner) throws RemoteException {
Log.i(TAG, "registBookListenner: " + listenner.toString());
// if(!mListenners.contains(listenner)){
// mListenners.add(listenner);
// }
mListenners.register(listenner);
}
@Override
public void unregistBookListenner(IBookListenner listenner) throws RemoteException {
Log.i(TAG, "unregistBookListenner: " + listenner.toString());
// if(mListenners.contains(listenner))
// mListenners.remove(listenner);
mListenners.unregister(listenner);
}
};
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate: ");
new Thread(new Runnable() {
@Override
public void run() {
onBookArrive(new Book(1, "Java"));
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
onBookArrive(new Book(2, "Android"));
}
}).start();
}
private void onBookArrive(Book book) {
mDatas.add(book);
// for(IBookListenner listenner:mListenners){
// try {
// listenner.onBookArrive(book);
// } catch (RemoteException e) {
// e.printStackTrace();
// }
// }
int count = mListenners.beginBroadcast();
for (int i = 0; i < count; i++) {
try {
mListenners.getBroadcastItem(i).onBookArrive(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: ");
return iBookManager;
}
}
服务端的创建多了一个监听器用来监听书本添加进来以后回调注册监听者。
监听的接口由于是aidl文件那么肯定要有aidl的实现接口:
// IBookListenner.aidl
package com.lyman.ipc;
import com.lyman.ipc.bean.Book;
// Declare any non-default types here with import statements
interface IBookListenner {
void onBookArrive(in Book book);
}
接口类的层次结构如下:
这里还引出了一个RemoteCallbackList集合,主要在我们注册监听器的时候底层传递转换为对应的aidl对象,我们在注册的时候使用的对象和反注册的时候的对象不会是同一个,这个集合就是帮助我们做一个对象的转换,让我们可以方便的在客户端使用同一个对象来进行注册与反注册。
- 客户端代码:
package com.lyman.ipc;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.lyman.ipc.bean.Book;
import com.lyman.ipc.service.BookService;
import java.util.List;
/**
* Author: lyman
* Email: lymenye@gmail.com
* Date: 2017/3/15
* Description:
*/
public class BookActivity extends AppCompatActivity {
private static final String TAG = "BookActivity";
private IBookManager iBookManager;
private IBookListenner.Stub mListenner = new IBookListenner.Stub() {
@Override
public void onBookArrive(Book book) throws RemoteException {
Log.i(TAG, "onBookArrive: " + book.toString());
}
};
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iBookManager = IBookManager.Stub.asInterface(service);
try {
iBookManager.registBookListenner(mListenner);
List<Book> list = iBookManager.getBookList();
Log.i(TAG, "onServiceConnected: " + list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book);
bindService(new Intent(this, BookService.class), mServiceConnection,
Service.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if (mListenner != null) {
if (iBookManager != null) {
try {
iBookManager.unregistBookListenner(mListenner);
mListenner = null;
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
unbindService(mServiceConnection);
super.onDestroy();
}
}
客户端的是一个常规的bindService代码与注册注销监听器代码。
这里对aidl的一些自己记得不牢靠的点做了一些记录还是有比较深的印象了。对于aidl实现的底层代码我们没有拿出来了解。可以参考《Android开发艺术探索》一书中对实现原理的讲解,本系列文章也是对其所阅读进行后面的实践思考来做下笔记
参考blog:click me