一、多进程是什么?
正常情况下,在Android系统中一个APP启动后只会运行在一个进程中,其进程名为apk的包名,所有的组件都会在这个进程中运行;
但是如果需要将某些组件(如Service,Activity等)运行在单独的进程中,就需要用到android:process属性了。
我们可以给android的组件设置android:process属性来使其运行在指定的进程中。
1、多进程优点:
①使用更多的内存
Android系统对每个应用进程的内存占用是有限制的,而且占用内存越大的进程,通常被系统杀死的可能性越大。让一个组件运行在单独的进程中,可以减少主进程所占用的内存,避免OOM问题,降低被系统杀死的概率。
②实现多模块
比如微信的小程序(一个住进程,一个小程序进程)、支付宝的小程序;
当你启动一个小程序时候,就会启动小程序的进程,不占用主进程的内存,使小程序进程单独出来,速度会快很多。
③子进程奔溃,主进程可以继续工作
④主进程退出,子进程可以继续工作
⑤实现守护进程
通过JNI利用C/C++,调用fork()方法来生成子进程,一般开发者会利用这种方法来做一些daemon(守护进程)进程,来实现防杀保活等效果。
2、多进程引起的问题
①静态成员和单例模式失效
②线程同步机制失效
③SharedPreferences 可靠性降低
④Application 被多次创建
⑤多进程交互麻烦
引起的原因:
Android中,系统会为每个应用或进程分配独立的虚拟机,不同的虚拟机自然占有不同的内存地址空间,
所以同一个类的对象会产生不同的副本,导致共享数据失败,必然也不能实现线程的同步。
二、多进程通信方式
Android中支持的多进程通信方式主要有以下几种,它们之间各有优缺点,可根据使用场景选择选择:
- AIDL:功能强大,支持进程间一对多的实时并发通信,并可实现 RPC (远程过程调用)。
- Messenger:支持一对多的串行实时通信, AIDL 的简化版本。
- Bundle:四大组件的进程通信方式,只能传输 Bundle 支持的数据类型。
- ContentProvider:强大的数据源访问支持,主要支持 CRUD 操作,一对多的进程间数据共享,例如我们的应用访问系统的通讯录数据。
- BroadcastReceiver:即广播
广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。
这就象电台进行广播一样,听众只能被动地收听,而不能主动与电台进行沟通,在应用程序中发送广播比较简单。
只需要调用sendBroadcast方法即可。该方法需要一个Intent对象。通过Intent对象可以发送需要广播的数据
- 文件共享:在非高并发情况下共享简单的数据。
- Socket:通过网络传输数据。
这次介绍日常开发APP中常用的交互方式AIDL和Messenger
如图
1、多进程AIDL通信
AIDL是Android接口定义语言是用于定义服务器和客户端通信接口的一种描述语言,可以使用它定义客户端与服务端进程间通信(IPC)的编程接口,
在 Android 中,进程之间无法共享内存(用户空间),不同进程之间的通信一般使用 AIDL 来处理。
使用简单步骤:
创建一个AIDL文件(扩展名为.aidl);
暴露一个接口给客户端(通过建立一个Service,在onBind()方法中返回一个Stub类的实例);
服务端实现该AIDL文件生成的Java接口(系统会自动生成对应的Java接口);
客户端连接绑定该远程服务。
①创建AIDL文件
创建一个 AIDL 文件,声明服务端要暴露给客户端的接口,然后创建一个 Service 监听客户端的连接请求,并在其中实现 AIDL 文件中的接口
注意,为了方便开发,我们一般把 AIDL 相关的文件放在同一包中,这样当客户端是另一个应用时可方便的把整个包复制到客户端工程中
首先了解下 AIDL 文件支持的几种数据类型:
- 基本数据类型
- String、CharSequence
- ArrayList、HashMap,其内部元素也需要被AIDL支持
- 实现了 Parcelable 接口的对象
- AIDL 类型的接口,非普通接口
Book是实现了Parcelable的类,只定义了name字段,按照规定如果 AIDL 文件用到了自定义Parcelable对象,同时需要提供一个Book.aidl文件
- package com.json;
- parcelable Book;
②ILibraryManager.aidl定义了服务端要暴露给客户端的接口:
- package com.json;
- import com.json.Book;
- interface ILibraryManager{
- // 近期新书查询
- List<Book> getNewBookList();
- // 图书捐赠
- void donateBook(in Book book);
- }
③接下来就是AIDL文件生成的Java接口,在编写服务类前要先编译项目,这样在服务类里使用 AIDL 生成的java类
首先通过ILibraryManager.Stub()创建一个mBinder对象,并实现了ILibraryManager.aidl中定义的接口方法,在onBind()方法中返回创建的mBinder,并在服务onCreate()添加数据。
最后在 AndroidManifest.xml 注册服务:
- <service
- android:name=".ManagerService"
- android:process=":remote">
- </service>
- public class ManagerService extends Service {
- private static final String TAG = "ManagerService";
- // CopyOnWriteArrayList 支持并发读写
- private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
- private Binder mBinder = new ILibraryManager.Stub() {
- @Override
- public List<Book> getNewBookList() throws RemoteException {
- return mBookList;
- }
- @Override
- public void donateBook(Book book) throws RemoteException {
- mBookList.add(book);
- }
- };
- public LibraryManagerService() {
- }
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- mBookList.add(new Book("book0"));
- mBookList.add(new Book("book1"));
- }
- }
④客户端连接绑定该远程服务
先实现ServiceConnection接口,在onServiceConnected()方法中将IBinder对象转换成ILibraryManager对象,通过该对象就能调用ILibraryManager.aidl中声明的方法了。
接下来绑定服务:
- public class AIDLActivity extends AppCompatActivity {
- private static final String TAG = "AIDLActivity";
- ILibraryManager libraryManager=null;
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- libraryManager = ILibraryManager.Stub.asInterface(service);
- try {
- // test1
- List<Book> books = libraryManager.getNewBookList();
- Log.e(TAG, "books:" + books.toString());
- // test2
- libraryManager.donateBook(new Book("book" + books.size()));
- List<Book> books2 = libraryManager.getNewBookList();
- Log.e(TAG, "books:" + books2.toString());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- };
- void bindAidlData(){
- if(null==libraryManager){
- return;
- }
- try {
- // test1
- List<Book> books = libraryManager.getNewBookList();
- Log.e(TAG, "books:" + books.toString());
- // test2
- libraryManager.donateBook(new Book("book" + books.size()));
- List<Book> books2 = libraryManager.getNewBookList();
- Log.e(TAG, "books:" + books2.toString());
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_aidl);
- bindNewService();
- }
- private void bindNewService() {
- Intent intent = new Intent(AIDLActivity.this, LibraryManagerService.class);
- bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
- }
- @Override
- protected void onDestroy() {
- unbindService(mServiceConnection);
- super.onDestroy();
- }
- }
2、aidl的通知功能
首先创建一个服务端通知客户端的 IOnNewBookArrivedListener.aidl 接口:
- package com.json;
- import com.json.Book;
- interface IOnNewBookArrivedListener {
- void onNewBookArrived(in Book book);
- }
服务端要先注册后才能收到通知,同时也可以取消注册,所以要给之前的ILibraryManager.aidl添加连个方法了:
- interface ILibraryManager{
- ......
- // 注册通知
- void register(IOnNewBookArrivedListener listener);
- // 取消注册
- void unregister(IOnNewBookArrivedListener listener);
- }
修改ManagerService:
- public class ManagerService extends Service {
- ......
- ......
- ......
- // 系统提供的专门用于保存、删除跨进程 listener 的类
- private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
- // AtomicBoolean 支持并发读写
- private AtomicBoolean mIsServiceDestroy = new AtomicBoolean(false);
- private Binder mBinder = new ILibraryManager.Stub() {
- ......
- @Override
- public void register(IOnNewBookArrivedListener listener) throws RemoteException {
- mListenerList.register(listener);
- Log.e(TAG, "register success");
- }
- @Override
- public void unregister(IOnNewBookArrivedListener listener) throws RemoteException {
- mListenerList.unregister(listener);
- Log.e(TAG, "unregister success");
- }
- };
- .......
- @Override
- public void onCreate() {
- super.onCreate();
- ......
- new Thread(new Runnable() {
- @Override
- public void run() {
- // 如果服务还没终止
- while (!mIsServiceDestroy.get()) {
- try {
- Thread.sleep(10 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- Book book = new Book("book" + mBookList.size());
- mBookList.add(book);
- bookArrivedNotify(book);
- }
- }
- }).start();
- }
- private void bookArrivedNotify(Book book) {
- int n = mListenerList.beginBroadcast();
- for (int i = 0; i < n; i++) {
- IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
- if (listener != null) {
- try {
- listener.onNewBookArrived(book);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
- mListenerList.finishBroadcast();
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- mIsServiceDestroy.set(true);
- }
- }
这里用到了RemoteCallbackList类,它是系统提供的专门用于删除跨进程 listener 的类,用普通的集合难以保证客户端注册的 listener 和服务端存储的 listener 是同一个,会取消注册失败
客户端代码修改
- public class AIDLActivity extends AppCompatActivity {
- ......
- private ILibraryManager mLibraryManager;
- private Handler mHandler = new Handler(new Handler.Callback() {
- @Override
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_NEW_BOOK_ARRIVED:
- Log.e(TAG, "new book:" + msg.obj);
- break;
- }
- return true;
- }
- });
- private IOnNewBookArrivedListener listener = new IOnNewBookArrivedListener.Stub() {
- @Override
- public void onNewBookArrived(Book book) throws RemoteException {
- // 由于 onNewBookArrived 方法在子线程被调用,所以通过Handler切换到UI线程,方便UI操作
- mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
- }
- };
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service);
- mLibraryManager = libraryManager;
- try {
- ......
- // 注册通知
- libraryManager.register(listener);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- ......
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_aidl);
- bindNewService();
- }
- @Override
- protected void onDestroy() {
- unbindService(mServiceConnection);
- if (mLibraryManager != null && mLibraryManager.asBinder().isBinderAlive()) {
- try {
- // 取消注册
- mLibraryManager.unregister(listener);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- super.onDestroy();
- }
- }
3、Messenger通讯
Messenger 是一种轻量级的多进程通信方式,它是在 AIDL 的基础上封装而成的,可以看做是 AIDL 的简化版,支持一对多的串行实时通信,
一次只处理一个请求,不存在并发的问题。和 AIDL 的使用类似,但要简单的多,同样需要实现服务端和客户端。
首先来看服务端
- public class MessengerService extends Service {
- private static final String TAG = "MessengerService";
- // 将Messenger和Handler关联起来
- private Messenger mServiceMessenger = new Messenger(new MessengerHandler());
- public MessengerService() {
- }
- @Override
- public IBinder onBind(Intent intent) {
- return mServiceMessenger.getBinder();
- }
- private static class MessengerHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case MessengerActivity.MESSAGE_FROM_CLIENT:
- // 打印接收到的客户端消息
- Log.e(TAG, "receive message from client:" + msg.getData().getString("msg"));
- // 给客户端回复一条消息
- Messenger clientMessenger = msg.replyTo;
- Message message = Message.obtain();
- message.what = MessengerActivity.MESSAGE_FROM_SERVICE;
- Bundle bundle = new Bundle();
- bundle.putString("msg", "I am fine,thank you!");
- message.setData(bundle);
- try {
- clientMessenger.send(message);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- break;
- }
- }
- }
- }
看客户端的实现
- public class MessengerActivity extends AppCompatActivity {
- private static final String TAG = "MessengerActivity";
- public static final int MESSAGE_FROM_CLIENT = 1;
- public static final int MESSAGE_FROM_SERVICE = 2;
- private Messenger mServiceMessenger;
- private Messenger mClientMessenger = new Messenger(new MessengerHandler());
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mServiceMessenger = new Messenger(service);
- Message message = Message.obtain();
- message.what = MESSAGE_FROM_CLIENT;
- Bundle bundle = new Bundle();
- bundle.putString("msg", "how are you?");
- message.setData(bundle);
- // 传递服务端回复客户端时需要使用的Messenger
- message.replyTo = mClientMessenger;
- try {
- mServiceMessenger.send(message);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_messenger);
- Intent intent = new Intent(MessengerActivity.this, MessengerService.class);
- bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
- }
- @Override
- protected void onDestroy() {
- unbindService(mServiceConnection);
- super.onDestroy();
- }
- private static class MessengerHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case MessengerActivity.MESSAGE_FROM_SERVICE:
- Log.e(TAG, "receive message from service:" + msg.getData().getString("msg"));
- break;
- }
- }
- }
- }
在onServiceConnected()中,将服务端的Binder转换成服务端的Messenger对象,然后发送消息,由于服务端还需要给客服端回复消息,所以需要在客户端创建一个Messenger对象附加在消息上发送给服务端使用
多进程总结:
1、多进程app可以在系统中申请多份内存,但应合理使用,建议把一些高消耗但不常用的模块放到独立的进程,不使用的进程可及时手动关闭;
2、多进程占用内存多,耗电量会增加,这个要注意;
3、每个进程在创建的时候,都会执行Application的onCreate进行初始化,如果这时候没有针对不同进程处理,onCreate的初始化业务会被多次执行,这是没有必要的而且多次初始化容易引起问题,所以需根据不同进程初始化相应的业务。
本文转载自微信公众号「 Android开发编程」,可以通过以下二维码关注。转载本文请联系 Android开发编程公众号。