Android multi-process communication - in-depth understanding

Android multi-process communication - in-depth understanding

[[405775]]

1. What is multi-process?

Normally, in the Android system, after an app is started, it will only run in one process, whose process name is the package name of apk, and all components will run in this process;

However, if you need to run certain components (such as Service, Activity, etc.) in a separate process, you need to use the android:process attribute.

We can set the android:process attribute for the Android component to make it run in the specified process.

1. Advantages of multi-process:

①Use more memory

The Android system has a limit on the memory usage of each application process, and the larger the memory usage of a process, the more likely it is to be killed by the system. Running a component in a separate process can reduce the memory used by the main process, avoid OOM problems, and reduce the probability of being killed by the system.

②Realize multiple modules

For example, WeChat’s mini-programs (one main process and one mini-program process) and Alipay’s mini-programs;

When you start a mini program, the mini program process will be started, which will not occupy the memory of the main process, making the mini program process separate and much faster.

③The child process crashes, and the main process can continue to work

④The main process exits and the child process can continue to work

⑤Implementing the daemon process

Use C/C++ through JNI to call the fork() method to generate a child process. Generally, developers will use this method to create some daemon processes to achieve anti-killing and keep-alive effects.

2. Problems caused by multiple processes

①Static members and singleton mode are invalid

②Thread synchronization mechanism fails

③SharedPreferences reliability is reduced

④Application is created multiple times

⑤Multi-process interaction is troublesome

Causes:

In Android, the system allocates an independent virtual machine for each application or process. Different virtual machines naturally occupy different memory address spaces.

Therefore, objects of the same class will produce different copies, resulting in failure of shared data and inevitable failure to achieve thread synchronization.

2. Multi-process communication mode

There are mainly the following multi-process communication methods supported in Android. Each of them has its own advantages and disadvantages. You can choose according to the usage scenario:

  • AIDL: Powerful, supports one-to-many real-time concurrent communication between processes, and can implement RPC (remote procedure call).
  • Messenger: supports one-to-many serial real-time communication, a simplified version of AIDL.
  • Bundle: The process communication method of the four major components can only transmit data types supported by Bundle.
  • ContentProvider: Powerful data source access support, mainly supporting CRUD operations and one-to-many inter-process data sharing, such as our application accessing the system's address book data.
  • BroadcastReceiver: Broadcast

Broadcasting is a passive cross-process communication method. When a program sends a broadcast to the system, other applications can only passively receive the broadcast data.

This is just like a radio station broadcasting. Listeners can only listen passively but cannot actively communicate with the radio station. It is relatively simple to send broadcasts in the application.

Just call the sendBroadcast method. This method requires an Intent object. The Intent object can be used to send the data to be broadcasted.

  • File sharing: Sharing simple data in non-high-concurrency situations.
  • Socket: Transmits data over the network.

This time we will introduce AIDL and Messenger, two commonly used interaction methods in daily APP development.

As shown

1. Multi-process AIDL communication

AIDL is the Android interface definition language, which is a description language used to define the communication interface between the server and the client. It can be used to define the programming interface of the inter-process communication (IPC) between the client and the server.

In Android, processes cannot share memory (user space), and communication between different processes is generally handled using AIDL.

Simple steps to use:

Create an AIDL file (with the extension .aidl);

Expose an interface to the client (by establishing a Service and returning an instance of the Stub class in the onBind() method);

The server implements the Java interface generated by the AIDL file (the system will automatically generate the corresponding Java interface);

The client connection is bound to the remote service.

①Create AIDL file

Create an AIDL file, declare the interface that the server wants to expose to the client, then create a Service to listen to the client's connection request and implement the interface in the AIDL file

Note that for the convenience of development, we generally put AIDL related files in the same package, so that when the client is another application, the entire package can be easily copied to the client project.

First, let's take a look at the data types supported by AIDL files:

  • Basic Data Types
  • String, CharSequence
  • ArrayList, HashMap, and their internal elements also need to be supported by AIDL
  • An object that implements the Parcelable interface
  • AIDL type interface, not a normal interface

Book is a class that implements Parcelable. It only defines the name field. According to the regulations, if the AIDL file uses a custom Parcelable object, a Book.aidl file must also be provided.

  1. package com.json;
  2.  
  3.  
  4. parcelable Book;

②ILibraryManager.aidl defines the interface that the server will expose to the client:

  1. package com.json;
  2.  
  3. import com.json.Book;
  4.  
  5. interface ILibraryManager{
  6. // Recent new book search
  7. List<Book> getNewBookList();
  8. // Book Donation
  9. void donateBook( in Book book);
  10. }

③The next step is the Java interface generated by the AIDL file. Before writing the service class, you must compile the project first so that the java class generated by AIDL can be used in the service class.

First, create an mBinder object through ILibraryManager.Stub(), implement the interface method defined in ILibraryManager.aidl, return the created mBinder in the onBind() method, and add data in the service onCreate().

Finally, register the service in AndroidManifest.xml:

  1. <service
  2. android: name = ".ManagerService"  
  3. android:process= ":remote" >
  4. </service>

  1. public class ManagerService extends Service {
  2.  
  3. private static final String TAG = "ManagerService" ;
  4.  
  5. // CopyOnWriteArrayList supports concurrent reading and writing
  6. private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
  7.  
  8. private Binder mBinder = new ILibraryManager.Stub() {
  9.  
  10. @Override
  11. public List<Book> getNewBookList() throws RemoteException {
  12. return mBookList;
  13. }
  14.  
  15. @Override
  16. public void donateBook(Book book) throws RemoteException {
  17. mBookList.add (book);
  18. }
  19. };
  20.  
  21. public LibraryManagerService() {
  22. }
  23.  
  24. @Override
  25. public IBinder onBind(Intent intent) {
  26. return mBinder;
  27. }
  28.  
  29. @Override
  30. public void onCreate() {
  31. super.onCreate();
  32. mBookList.add (new Book( "book0" ));
  33. mBookList.add (new Book( "book1" ));
  34. }
  35. }

④The client connects and binds the remote service

First implement the ServiceConnection interface, and convert the IBinder object into an ILibraryManager object in the onServiceConnected() method. Through this object, you can call the methods declared in ILibraryManager.aidl.

Next, bind the service:

  1. public class AIDLActivity extends AppCompatActivity {
  2. private static final String TAG = "AIDLActivity" ;
  3. ILibraryManager libraryManager= null ;
  4. private ServiceConnection mServiceConnection = new ServiceConnection() {
  5. @Override
  6. public void onServiceConnected(ComponentName name , IBinder service) {
  7. libraryManager = ILibraryManager.Stub.asInterface(service);
  8. try {
  9. //test1
  10. List<Book> books = libraryManager.getNewBookList();
  11. Log.e(TAG, "books:" + books.toString());
  12. //test2
  13. libraryManager.donateBook(new Book( "book" + books. size ()));
  14. List<Book> books2 = libraryManager.getNewBookList();
  15. Log.e(TAG, "books:" + books2.toString());
  16. } catch (RemoteException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20.  
  21. @Override
  22. public void onServiceDisconnected(ComponentName name ) {
  23.  
  24. }
  25. };
  26.  
  27. void bindAidlData(){
  28. if( null ==libraryManager){
  29. return ;
  30. }
  31. try {
  32. //test1
  33. List<Book> books = libraryManager.getNewBookList();
  34. Log.e(TAG, "books:" + books.toString());
  35. //test2
  36. libraryManager.donateBook(new Book( "book" + books. size ()));
  37. List<Book> books2 = libraryManager.getNewBookList();
  38. Log.e(TAG, "books:" + books2.toString());
  39. } catch (RemoteException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. @Override
  44. protected void onCreate(Bundle savedInstanceState) {
  45. super.onCreate(savedInstanceState);
  46. setContentView(R.layout.activity_aidl);
  47. bindNewService();
  48. }
  49.  
  50. private void bindNewService() {
  51. Intent intent = new Intent(AIDLActivity.this, LibraryManagerService.class);
  52. bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
  53. }
  54.  
  55. @Override
  56. protected void onDestroy() {
  57. unbindService(mServiceConnection);
  58. super.onDestroy();
  59. }
  60. }

2. Notification function of aidl

First, create an IOnNewBookArrivedListener.aidl interface for the server to notify the client:

  1. package com.json;
  2.  
  3.  
  4. import com.json.Book;
  5.  
  6.  
  7. interface IOnNewBookArrivedListener {
  8. void onNewBookArrived( in Book book);
  9. }

The server must register before it can receive notifications, and it can also unregister, so we need to add two methods to the previous ILibraryManager.aidl:

  1. interface ILibraryManager{
  2. ......
  3. //Register notification
  4. void register(IOnNewBookArrivedListener listener);
  5. // Unregister
  6. void unregister(IOnNewBookArrivedListener listener);
  7. }

Modify ManagerService:

  1. public class ManagerService extends Service {
  2. ......
  3. ......
  4. ......
  5. // Class provided by the system specifically for saving and deleting cross-process listeners
  6. private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
  7. // AtomicBoolean supports concurrent reading and writing
  8. private AtomicBoolean mIsServiceDestroy = new AtomicBoolean( false );
  9.  
  10. private Binder mBinder = new ILibraryManager.Stub() {
  11. ......
  12. @Override
  13. public void register(IOnNewBookArrivedListener listener) throws RemoteException {
  14. mListenerList.register(listener);
  15. Log.e(TAG, "register success" );
  16. }
  17.  
  18. @Override
  19. public void unregister(IOnNewBookArrivedListener listener) throws RemoteException {
  20. mListenerList.unregister(listener);
  21. Log.e(TAG, "unregister success" );
  22. }
  23. };
  24.  
  25. .......
  26.  
  27. @Override
  28. public void onCreate() {
  29. super.onCreate();
  30. ......
  31.        
  32. new Thread(new Runnable() {
  33. @Override
  34. public void run() {
  35. // If the service has not terminated
  36. while (!mIsServiceDestroy.get()) {
  37. try {
  38. Thread.sleep(10 * 1000);
  39. } catch (InterruptedException e) {
  40. e.printStackTrace();
  41. }
  42.  
  43. Book book = new Book( "book" + mBookList. size ());
  44. mBookList.add (book);
  45. bookArrivedNotify(book);
  46. }
  47. }
  48. }).start();
  49. }
  50.      
  51. private void bookArrivedNotify(Book book) {
  52. int n = mListenerList.beginBroadcast();
  53. for ( int i = 0; i < n; i++) {
  54. IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
  55. if (listener != null ) {
  56. try {
  57. listener.onNewBookArrived(book);
  58. } catch (RemoteException e) {
  59. e.printStackTrace();
  60. }
  61. }
  62. }
  63. mListenerList.finishBroadcast();
  64. }
  65.  
  66. @Override
  67. public void onDestroy() {
  68. super.onDestroy();
  69. mIsServiceDestroy.set ( true ) ;
  70. }
  71. }

The RemoteCallbackList class is used here. It is a class provided by the system specifically for deleting cross-process listeners. It is difficult to ensure that the listener registered by the client and the listener stored by the server are the same using ordinary collections, which will cause the registration to fail.

Client code modification

  1. public class AIDLActivity extends AppCompatActivity {
  2. ......
  3. private ILibraryManager mLibraryManager;
  4.  
  5. private Handler mHandler = new Handler(new Handler.Callback() {
  6. @Override
  7. public boolean handleMessage(Message msg) {
  8. switch (msg.what) {
  9. case MESSAGE_NEW_BOOK_ARRIVED:
  10. Log.e(TAG, "new book:" + msg.obj);
  11. break;
  12. }
  13. return   true ;
  14. }
  15. });
  16.  
  17. private IOnNewBookArrivedListener listener = new IOnNewBookArrivedListener.Stub() {
  18. @Override
  19. public void onNewBookArrived(Book book) throws RemoteException {
  20. // Since the onNewBookArrived method is called in the child thread, the Handler is used to switch to the UI thread to facilitate UI operations
  21. mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
  22. }
  23. };
  24.  
  25. private ServiceConnection mServiceConnection = new ServiceConnection() {
  26. @Override
  27. public void onServiceConnected(ComponentName name , IBinder service) {
  28. ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service);
  29. mLibraryManager = libraryManager;
  30. try {
  31. ......
  32. //Register notification
  33. libraryManager.register(listener);
  34. } catch (RemoteException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. ......
  39. };
  40.  
  41. @Override
  42. protected void onCreate(Bundle savedInstanceState) {
  43. super.onCreate(savedInstanceState);
  44. setContentView(R.layout.activity_aidl);
  45. bindNewService();
  46. }
  47.  
  48. @Override
  49. protected void onDestroy() {
  50. unbindService(mServiceConnection);
  51. if (mLibraryManager != null && mLibraryManager.asBinder().isBinderAlive()) {
  52. try {
  53. // Unregister
  54. mLibraryManager.unregister(listener);
  55. } catch (RemoteException e) {
  56. e.printStackTrace();
  57. }
  58. }
  59. super.onDestroy();
  60. }
  61. }

3. Messenger communication

Messenger is a lightweight multi-process communication method. It is encapsulated based on AIDL and can be regarded as a simplified version of AIDL. It supports one-to-many serial real-time communication.

Only one request is processed at a time, and there is no concurrency problem. Similar to the use of AIDL, but much simpler, and also requires the implementation of the server and client.

First, let's look at the server

  1. public class MessengerService extends Service {
  2. private static final String TAG = "MessengerService" ;
  3. // Associate the Messenger with the Handler
  4. private Messenger mServiceMessenger = new Messenger(new MessengerHandler());
  5.  
  6. public MessengerService() {
  7. }
  8.  
  9. @Override
  10. public IBinder onBind(Intent intent) {
  11. return mServiceMessenger.getBinder();
  12. }
  13.  
  14. private static class MessengerHandler extends Handler {
  15. @Override
  16. public void handleMessage(Message msg) {
  17. super.handleMessage(msg);
  18. switch (msg.what) {
  19. case MessengerActivity.MESSAGE_FROM_CLIENT:
  20. // Print the received client message
  21. Log.e(TAG, "receive message from client:" + msg.getData().getString( "msg" ));
  22. // Reply a message to the client
  23. Messenger clientMessenger = msg.replyTo;
  24. Message message = Message.obtain();
  25. message.what = MessengerActivity.MESSAGE_FROM_SERVICE;
  26. Bundle bundle = new Bundle();
  27. bundle.putString( "msg" , "I am fine, thank you!" );
  28. message.setData(bundle);
  29. try {
  30. clientMessenger.send(message);
  31. } catch (RemoteException e) {
  32. e.printStackTrace();
  33. }
  34. break;
  35. }
  36. }
  37. }
  38. }

See the client implementation

  1. public class MessengerActivity extends AppCompatActivity {
  2. private static final String TAG = "MessengerActivity" ;
  3.  
  4. public   static final int MESSAGE_FROM_CLIENT = 1;
  5. public   static final int MESSAGE_FROM_SERVICE = 2;
  6.  
  7. private Messenger mServiceMessenger;
  8.  
  9. private Messenger mClientMessenger = new Messenger(new MessengerHandler());
  10.  
  11. private ServiceConnection mServiceConnection = new ServiceConnection() {
  12. @Override
  13. public void onServiceConnected(ComponentName name , IBinder service) {
  14. mServiceMessenger = new Messenger(service);
  15. Message message = Message.obtain();
  16. message.what = MESSAGE_FROM_CLIENT;
  17. Bundle bundle = new Bundle();
  18. bundle.putString( "msg" , "how are you?" );
  19. message.setData(bundle);
  20. // Pass the Messenger that the server needs to use when replying to the client
  21. message.replyTo = mClientMessenger;
  22. try {
  23. mServiceMessenger.send(message);
  24. } catch (RemoteException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28.  
  29. @Override
  30. public void onServiceDisconnected(ComponentName name ) {
  31.  
  32. }
  33. };
  34.  
  35. @Override
  36. protected void onCreate(Bundle savedInstanceState) {
  37. super.onCreate(savedInstanceState);
  38. setContentView(R.layout.activity_messenger);
  39.  
  40. Intent intent = new Intent(MessengerActivity.this, MessengerService.class);
  41. bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
  42. }
  43.  
  44. @Override
  45. protected void onDestroy() {
  46. unbindService(mServiceConnection);
  47. super.onDestroy();
  48. }
  49.  
  50. private static class MessengerHandler extends Handler {
  51. @Override
  52. public void handleMessage(Message msg) {
  53. super.handleMessage(msg);
  54. switch (msg.what) {
  55. case MessengerActivity.MESSAGE_FROM_SERVICE:
  56. Log.e(TAG, "receive message from service:" + msg.getData().getString( "msg" ));
  57. break;
  58. }
  59. }
  60. }
  61. }

In onServiceConnected(), convert the server-side Binder into a server-side Messenger object, and then send a message. Since the server needs to reply to the client, a Messenger object needs to be created on the client side and attached to the message and sent to the server for use.

Multi-process summary:

1. Multi-process apps can apply for multiple copies of memory in the system, but they should be used reasonably. It is recommended to put some high-consumption but infrequently used modules into independent processes, and unused processes can be manually closed in time;

2. Multiple processes occupy more memory and increase power consumption, so please pay attention to this;

3. When each process is created, the Application's onCreate will be executed for initialization. If there is no processing for different processes at this time, the onCreate initialization business will be executed multiple times, which is unnecessary and multiple initializations are prone to cause problems, so the corresponding business needs to be initialized according to different processes.

This article is reprinted from the WeChat public account "Android Development Programming". You can follow it through the following QR code. To reprint this article, please contact the Android Development Programming public account.

<<:  QQ's new emoticon "Da Cai Wang" dominates the screen: no matter how big the phone screen is, it will never be blurred

>>:  What is the value of B-side designers? Let’s take a look at the director-level analysis (Part 1)

Recommend

A new app marketing plan

How to promote a new App? Nowadays, App promotion...

Why hasn’t there been a second “WeChat” in Europe and the United States?

Recently, it was reported that Ted Livingston, th...

What would happen to us if we could no longer feel pain?

In life, we are bound to get hurt sometimes. Expe...

How to make a good online event promotion plan?

In marketing psychology, herd mentality, greed fo...

Investigation into suspected Apple ID leak

[[246116]] A large-scale theft of Apple IDs among...

Tesla's global Internet outage paralyzed its cars, netizens: nothing new

Tesla’s Battery Day had just ended when tragedy s...

The four basic ways to attract users: mining, support, output and retention

Background: It has been four years since Changba ...