Android development art exploration learning notes Chapter 2 IPC

Upload the local study notes of previous work recently
Here are the first three chapters of Android art development exploration


The so-called IPC is the abbreviation of inter process communication, which means data interaction between processes. Common schemes include pipeline, named pipeline, shared memory, message queue and semaphore. The common solutions in android are binder and socket.

1. Multi process mode of Android

You can turn on the multi process mode by specifying android:process for the four components. As shown below, you can set by ": xxx". At this time, a process name named "package name +: remote" will be automatically created. You can also directly enter the user-defined process name. If this value is not set, it will run in the process named package name by default.

In addition, through: the created process belongs to the private process of the current application, but not through: the created process belongs to the global process. Other applications can run in the same process with it through ShareUID.

android:process=":remote"

But it brings a problem. Because each process is created, the system will allocate a separate virtual machine for it. Different virtual machines have different memory space. This results in that when two processes access the same object, two copies of the object will be created, which will lead to the failure of sharing data.

public class User{
  public static int age=1;
}

For example, the existing static member variable age=1. If you add age+1 in a process, according to the normal logic, the static member variable belongs to the common class, and the ages in all users are the same. However, in another process, age is still equal to 1, which indicates that there are two copies of age at the same time, which are independent and do not interfere with each other.

This multi process mode usually causes the following problems:

(1) Static members and singleton modes fail completely

This has been described above.

(2) The thread synchronization mechanism has completely failed

This is because there are different memory spaces. Therefore, thread synchronization cannot be guaranteed whether it is a lock object or a lock global class, because the lock is not one thing. The class in the process here is locked, but the class independence of the process there is not locked.

(3) Decreased reliability of SharePreference

This is because SharePreference does not allow two processes to read and write at the same time. Obviously, concurrent file reading and writing is certainly prone to problems.

(4) Application will be created multiple times

It is also well understood that the components of different processes belong to the corresponding virtual machine and application respectively. When a new process is created, a new application will be created at the same time.

In order to solve these problems, some IPC methods are provided, which can share data between different processes.

2. Introduction to basic concepts of IPC

Serialization is to convert data into a transportable data form for transmission. Generally speaking, the data leaving memory needs to be serialized before transmission. In java, it is transmitted into byte stream.

2.1 Serializable

Serializable is a serialized interface provided by java and an empty interface. It is very simple to use.

a. Implement this interface

b. Just declare a serialVersionUID (in fact, it's OK not to add it. Everything is normal during serialization, but there may be problems during deserialization) The code is complete as shown below.

public class User implements Serializable {
  private static final long serialVersionUID=123456789L;
  public  int age;
  public String name;
}

Next, you only need to serialize and deserialize using ObjectOutputStream and ObjectInputStream.

//serialize
User user=new User();
ObjectOutputStream outputStream=new ObjectOutputStream(new FileOutputStream("file.txt"));
outputStream.writeObject(user);
outputStream.close();
//Deserialization
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("file.txt"));
User newUser= (User) inputStream.readObject();
inputStream.close();

As mentioned above, if the serialVersionUID is not added, problems may occur. The principle is as follows: when serializing, the serialVersionUID of this class will be written to file or other intermediaries, and then when deserializing, the original serialVersionUID (user) and the existing serialVersionUID (newUser) will be compared to see if they are the same. If they are different, an error will be reported.

When we do not declare the serialVersionUID manually, we will automatically generate its hash value according to the results of the current class. In this way, once the structure of the class changes, such as increasing or decreasing fields, the serialVersionUID will change. At this time, there will be problems in deserialization. If the serialVersionUID is set manually, the serialVersionUID will always be the serialVersionUID originally set, and will not change, so there will be no error, so the data can be recovered to the greatest extent. In addition, if the class name or the type of member variable changes, serialization will still fail.

2.2Parcelabe

In addition to Serializable, Android also provides a parcel. As long as this interface is implemented, it can also be serialized. We all know that data can be transferred through intent and bundle, and they actually implement this interface.

public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
    //ellipsis
}
public class Intent implements Parcelable, Cloneable {
    //ellipsis
}

In fact, the usage is also written in the source code.

/**
 *Source code example
 * <pre>
 * public class MyParcelable implements Parcelable {
 *     private int mData;
 *
 *     public int describeContents() {
 *         return 0;
 *     }
 *
 *     public void writeToParcel(Parcel out, int flags) {
 *         out.writeInt(mData);
 *     }
 *
 *     public static final Parcelable.Creator<MyParcelable> CREATOR
 *             = new Parcelable.Creator<MyParcelable>() {
 *         public MyParcelable createFromParcel(Parcel in) {
 *             return new MyParcelable(in);
 *         }
 *
 *         public MyParcelable[] newArray(int size) {
 *             return new MyParcelable[size];
 *         }
 *     };
 *     
 *     private MyParcelable(Parcel in) {
 *         mData = in.readInt();
 *     }
 * }</pre>
 */
public interface Parcelable {
    //...
}

These methods are described below

(1)public int describeContents()

    /**
     * Describe the kinds of special objects contained in this Parcelable
     * instance's marshaled representation. For example, if the object will
     * include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
     * the return value of this method must include the
     * {@link #CONTENTS_FILE_DESCRIPTOR} bit.
     *  
     * @return a bitmask indicating the set of special object types marshaled
     * by this Parcelable object instance.
     */
    public @ContentsFlags int describeContents();

I can't understand it. It's about describing the type of this object. In most cases, just return 0. as

  @Override
    public int describeContents() {
        return 0;
    }

(2)public void writeToParcel()

/**
     * Flatten this object in to a Parcel.
     * 
     * @param dest The Parcel in which the object should be written.
     * @param flags Additional flags about how the object should be written.
     * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
     */
    public void writeToParcel(Parcel dest, @WriteFlags int flags);

Convert the object to be converted into the parcel type. The first parameter is the parcel for data saving, and the second parameter is the flag bit, which represents whether the object needs to be used as the return value, which is generally 0. The implementation method is as follows: write each member variable of the object into the serialized structure respectively.

   @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeString(music_name);//It seems that you should write in order, otherwise you will make an error dest writeString(music_author);         dest. writeInt(play_time);    }

(3) Static Parcelable Creator interface has two methods.

  • createFromParcel(Parcel in)
 /**         * Create a new instance of the Parcelable class, instantiating it         * from the given Parcel whose data had previously been written by         * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.         *          * @param source The Parcel to read the object's data from.         * @return Returns a new instance of the Parcelable class.         */        public T createFromParcel(Parcel source);

Create a music object and instantiate the music object through the parcel class that has been assigned a value by writeToParce().

  • newArray(int size)
/**         * Create a new array of the Parcelable class.         *          * @param size Size of the array.         * @return Returns an array of the Parcelable class, with every entry         * initialized to null.         */        public T[] newArray(int size);

Returns an array of music type with size. as

   @Override        public Music createFromParcel(Parcel in) {            return new Music(in);        }        @Override        public Music[] newArray(int size) {            return new Music[size];        }       

**(4) Private construction method**

 				private Music(Parcel in) { 						music_name=in.readString(); 						music_author=in.readString(); 						play_time=in.readInt();        }              

Note: 1 the serialization and deserialization of local variables must be in the same order, otherwise an error will occur

2 if the member variable is another serializable object, you need to pass the context class loader of the current thread, otherwise an error that the class cannot be found will be reported.

As shown below.

				Author author=in.readParcelable(Thread.currentThread().getContextClassLoader());

Q&A

1. What is the difference between parcel and Serializable? What should be used in what scenario?

Serializable is a serialization interface in java. It is simple to use but expensive. Both serialization and deserialization require a lot of io operations. Parcelable is a serialization method in Android, so it is more suitable for Android platform. The disadvantage is that it is more complex to use than the former, but it is more efficient. Parcelable is mainly used for memory serialization. Serializable is recommended for serialization to storage devices and network transmission.

3.AIDL

AIDL is one of the IPC methods in Android. AIDL is the abbreviation of Android Interface definition language, which can easily realize the interaction between client and server. Because there are a lot of contents, here is only a brief introduction for the time being. A separate learning note on AIDL will be written later.

3.1 use

(1) Create aidl file

There are three in total. Note the creation of Book When using Aidl, you may be prompted that Book already exists. You can choose a name and then rename it Book.

Book entity class

package com.example.myapplication.aidl;import android.os.Parcel;import android.os.Parcelable;public class Book implements Parcelable {  private int bookId;  private int price;  private String bookName;  public Book(int bookId, String bookName,int price) {    this.bookId = bookId;    this.bookName = bookName;    this.price=price;  }  private Book(Parcel in){    bookId=in.readInt();    price=in.readInt();    bookName=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(bookId);    dest.writeInt(price);    dest.writeString(bookName);  }  public int getBookId() {    return bookId;  }  public void setBookId(int bookId) {    this.bookId = bookId;  }  public String getBookName() {    return bookName;  }  public void setBookName(String bookName) {    this.bookName = bookName;  }  public int getPrice() {    return price;  }  public void setPrice(int price) {    this.price = price;  }  @Override public String toString() {    return "Book{" +        "bookId=" + bookId +        ", price=" + price +        ", bookName='" + bookName + '\'' +        '}';  }}

Not all data types in AIDL files are supported, only the following types are supported.

  • Basic data type (except short)
  • String and CharSequence
  • ArrayList
  • HashMap
  • Parcelable
  • AIDL
// IBookManager.aidlpackage com.example.myapplication.aidl;// Declare any non-default types here with import statementsimport com.example.myapplication.aidl.Book;interface IBookManager {  List<Book> getBookList();  void addBook(in Book book);}
// Book.aidlpackage com.example.myapplication.aidl;// Declare any non-default types here with import statementsparcelable Book;

(2) Create a server-side service for remote binding

package com.example.myapplication.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;

public class AIDLService extends Service {
  private List<Book> list=new ArrayList<>();

  public AIDLService() {
  }

  @Nullable @Override public IBinder onBind(Intent intent) {
    return new MyBinder();
  }

  private final class MyBinder extends IBookManager.Stub {
    @Override public List<Book> getBookList() throws RemoteException {
      return list;
    }

    @Override public void addBook(Book book) throws RemoteException {
      if(book!=null){
        list.add(book);
      }
    }
  }
}

(3) Create client

Re create a project, and then copy the whole aidl file from the server to the client. Note that the location should be the same.

After that, you need to create the same package name as the server-side Book class to store the Book class. As shown in the figure

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-lgmxlkns-1627456913968) (/ users / bjhl / library / Application Support / typera user images / image-20200810160502220. PNG)]

Finally, bind the remote service.

package com.example.myclient;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.example.myapplication.aidl.Book;
import com.example.myapplication.aidl.IBookManager;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {
  private static final String TAG="MainActivity1";
  private IBookManager iBookManager;
  private boolean isConnected=false;
  private ServiceConnection sc=new ServiceConnection() {
    @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
      isConnected=true;
      iBookManager=IBookManager.Stub.asInterface(iBinder);
      Log.d(TAG, "Connection succeeded");
    }

    @Override public void onServiceDisconnected(ComponentName componentName) {
      isConnected=false;
      Log.d(TAG, "connection failed");
    }
  };
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Intent intent=new Intent();
    intent.setPackage("com.example.myapplication");
    intent.setAction("com.example.aidl");

    Button button = findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View view) {
        if(isConnected){
          Book book=new Book(101,"Book 1",10);
          try {
            iBookManager.addBook(book);
            Log.d(TAG, iBookManager.getBookList().toString());
          } catch (RemoteException e) {
            e.printStackTrace();
          }
        }

      }
    });
    Log.d(TAG,bindService(intent,sc, Context.BIND_AUTO_CREATE)+"");
  }

  @Override protected void onDestroy() {
    super.onDestroy();
    unbindService(sc);
  }
}

Final output

2020-08-10 15:58:50.472 8552-8552/com.example.myclient D/MainActivity1: true
2020-08-10 15:58:50.509 8552-8552/com.example.myclient D/MainActivity1: Connection succeeded
2020-08-10 15:58:56.498 8552-8552/com.example.myclient D/MainActivity1: [Book{bookId=101, price=10, bookName='Book 1'}]
2020-08-10 15:59:00.188 8552-8552/com.example.myclient D/MainActivity1: [Book{bookId=101, price=10, bookName='Book 1'}, Book{bookId=101, price=10, bookName='Book 1'}]

3.2IBookManager.java class source code analysis

First post the complete code

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.example.myapplication.aidl;
public interface IBookManager extends android.os.IInterface
{
  /** Default implementation for IBookManager. */
  public static class Default implements com.example.myapplication.aidl.IBookManager
  {
    @Override public java.util.List<com.example.myapplication.aidl.Book> getBookList() throws android.os.RemoteException
    {
      return null;
    }
    @Override public void addBook(com.example.myapplication.aidl.Book book) throws android.os.RemoteException
    {
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements com.example.myapplication.aidl.IBookManager
  {
    private static final java.lang.String DESCRIPTOR = "com.example.myapplication.aidl.IBookManager";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.example.myapplication.aidl.IBookManager interface,
     * generating a proxy if needed.
     */
    public static com.example.myapplication.aidl.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.myapplication.aidl.IBookManager))) {
        return ((com.example.myapplication.aidl.IBookManager)iin);
      }
      return new com.example.myapplication.aidl.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
    {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_getBookList:
        {
          data.enforceInterface(descriptor);
          java.util.List<com.example.myapplication.aidl.Book> _result = this.getBookList();
          reply.writeNoException();
          reply.writeTypedList(_result);
          return true;
        }
        case TRANSACTION_addBook:
        {
          data.enforceInterface(descriptor);
          com.example.myapplication.aidl.Book _arg0;
          if ((0!=data.readInt())) {
            _arg0 = com.example.myapplication.aidl.Book.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          this.addBook(_arg0);
          reply.writeNoException();
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.example.myapplication.aidl.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<com.example.myapplication.aidl.Book> getBookList() throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.util.List<com.example.myapplication.aidl.Book> _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getBookList();
          }
          _reply.readException();
          _result = _reply.createTypedArrayList(com.example.myapplication.aidl.Book.CREATOR);
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public void addBook(com.example.myapplication.aidl.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);
          }
          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();
        }
      }
      public static com.example.myapplication.aidl.IBookManager sDefaultImpl;
    }
    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 static boolean setDefaultImpl(com.example.myapplication.aidl.IBookManager impl) {
      if (Stub.Proxy.sDefaultImpl == null && impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.example.myapplication.aidl.IBookManager getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  public java.util.List<com.example.myapplication.aidl.Book> getBookList() throws android.os.RemoteException;
  public void addBook(com.example.myapplication.aidl.Book book) throws android.os.RemoteException;
}

The Default class does not do analysis, but returns a Default implementation, which does nothing.

public static class Default implements com.example.myapplication.aidl.IBookManager{  @Override public java.util.List<com.example.myapplication.aidl.Book> getBookList() throws android.os.RemoteException  {    return null;  }  @Override public void addBook(com.example.myapplication.aidl.Book book) throws android.os.RemoteException  {  }  @Override  public android.os.IBinder asBinder() {    return null;  }}

Next comes the stub class.

DESCRIPTOR: first, create the unique identifier of Binder, which is generally represented by the current class name of Binder.

private static final java.lang.String DESCRIPTOR = "com.example.myapplication.aidl.IBookManager";

asInterface: it is to use the DESCRIPTOR to convert the binder into an AIDL interface type object. If the client and the server are in the same process, the IBookManager will be returned directly; otherwise, the stub Proxy object.

public static com.example.myapplication.aidl.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.myapplication.aidl.IBookManager))) {
    return ((com.example.myapplication.aidl.IBookManager)iin);
  }
  return new com.example.myapplication.aidl.IBookManager.Stub.Proxy(obj);
}

asBinder: returns the current binder object.

   @Override public android.os.IBinder asBinder()
    {
      return this;
    }

onTransact: this method runs in the server Binder thread pool. If the client and server are in the same process, this method will not be called; In case of different processes, the server determines the target method requested by the client through code, then takes out the parameters (if any) required by the target method from the data, and then executes the target method. After the target method is executed, the return value (if any) is written to the reply. This logic is completed by the stub internal Proxy class Proxy. If this method returns false, the client request will fail, so you can use this method for permission verification.

For example, you can see case transaction_ The corresponding method of getbooklist is List getBookList(). Because there is no input parameter and the return value is List, the target method is called_ result = this. After getbooklist(), the_ Result writes reply as the return value.

case TRANSACTION_ The method corresponding to addbook is void addbook (in Book). The input parameter is Book type and has no return value, so get it first_ Arg0, and then pass it as a parameter to the target method this addBook(_arg0)

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
  java.lang.String descriptor = DESCRIPTOR;
  switch (code)
  {
    case INTERFACE_TRANSACTION:
    {
      reply.writeString(descriptor);
      return true;
    }
    case TRANSACTION_getBookList:
    {
      data.enforceInterface(descriptor);
      java.util.List<com.example.myapplication.aidl.Book> _result = this.getBookList();
      reply.writeNoException();
      reply.writeTypedList(_result);
      return true;
    }
    case TRANSACTION_addBook:
    {
      data.enforceInterface(descriptor);
      com.example.myapplication.aidl.Book _arg0;
      if ((0!=data.readInt())) {
        _arg0 = com.example.myapplication.aidl.Book.CREATOR.createFromParcel(data);
      }
      else {
        _arg0 = null;
      }
      this.addBook(_arg0);
      reply.writeNoException();
      return true;
    }
    default:
    {
      return super.onTransact(code, data, reply, flags);
    }
  }
}

Next is the inner class Proxy.

First, a remote binder is created for the client to use.

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;}

Proxy#getBookList

This method runs the client. Its execution process is as follows: when the client calls remotely, first create a Parcel object containing input parameters_ data, including the return value when the Parcel object_ Reply and return value_ result; Then write the sharer parameter to_ data (if any), then call transact through remote to initiate an RPC request and suspend the current thread. The current thread will not continue to execute until the server onTransact is called and returned_ Get the value from reply and assign it to__ Result, and finally return_ result.

@Override public java.util.List<com.example.myapplication.aidl.Book> getBookList() throws android.os.RemoteException{  android.os.Parcel _data = android.os.Parcel.obtain();  android.os.Parcel _reply = android.os.Parcel.obtain();  java.util.List<com.example.myapplication.aidl.Book> _result;  try {    _data.writeInterfaceToken(DESCRIPTOR);    boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);    if (!_status && getDefaultImpl() != null) {      return getDefaultImpl().getBookList();    }    _reply.readException();    _result = _reply.createTypedArrayList(com.example.myapplication.aidl.Book.CREATOR);  }  finally {    _reply.recycle();    _data.recycle();  }  return _result;}

Proxy#addBook

This method runs on the client side and its execution process is equivalent to getBookList. Different from the previous method, this method does not return a value because it does not return a value_ result, but there are input parameters, so you need to write book to data.

@Override public void addBook(com.example.myapplication.aidl.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);
    }
    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();
  }
}

Two flags are declared.

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

get and set methods implemented by default.

public static boolean setDefaultImpl(com.example.myapplication.aidl.IBookManager impl) {  if (Stub.Proxy.sDefaultImpl == null && impl != null) {    Stub.Proxy.sDefaultImpl = impl;    return true;  }  return false;}public static com.example.myapplication.aidl.IBookManager getDefaultImpl() {  return Stub.Proxy.sDefaultImpl;}

There are two points to note. The first point is that as mentioned above, when the client calls the server onTransact, it will hang until the server ends and returns. Therefore, if the server execution event is too long, the request cannot be initiated in the ui thread. Second, since the binder method runs in the binder thread pool, the binder method, whether time-consuming or not, should be implemented in a synchronous manner, because it already runs in a thread.

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-h7dxkbwd-1627456913982) (/ users / bjhl / library / Application Support / typera user images / image-20200810112257074. PNG)]

3.3 directional Tag

It is divided into in, out and inOut.

in: it can only be input from the client to the server. The modification of data by the server cannot affect the client.

out: it can only be output from the server to the client. Even if the client has input to the method, it is empty. The data modification of the server can affect the client.

inOut: bidirectional.

4. IPC mode in Android

4.1 Bundle

Because Bundle implements the parcelable interface, it can easily transfer data between different processes. The three components (Activity, Service and Receiver) all support the transfer of Bundle data in Intent.

Mode of use

It is very simple. It is stored by key value pairs.

storage

Bundle bundle=new Bundle();
bundle.putString("name","Zhang San");
bundle.putInt("age",18);
bundle.putStringArray("skill",new String[]{"sing","dance","Play basketball"});
Intent intent=new Intent();
intent.putExtras(bundle);

obtain

Bundle bundle=getIntent().getExtras();
if(bundle!=null) {
  String name=bundle.getString("name");
  int age=bundle.getInt("age",10);
}

In this way, data can be passed between processes through bundle s.

4.2 file sharing

Cross process data transmission can be realized by reading and writing to the same file. However, there may be problems with concurrent reading and writing. Therefore, it is suitable for communication between processes that do not require high data synchronization, and the problem of concurrent read and write should be properly handled.

4.3 Messenger

Messenger is a lightweight IPC scheme, and the bottom layer is AIDL. And only one request is processed at a time, so the problem of thread synchronization does not need to be considered on the server, because there is no concurrent access on the server. The usage is a bit similar to handler.

Use steps

1 server process

Create a service on the server side for remote binding. Create a subclass of handler, pass it into messenger, and return the binder of messenger through onBinder method.

public class MessengerService extends Service {
  private static final String TAG="MessengerService";
  private static class MessengerHandler extends Handler{
    @Override public void handleMessage(@NonNull Message msg) {
      switch (msg.what){
        case 1:
          Log.d(TAG,msg.getData().getString("name")+"");
          break;
        default:break;

      }      super.handleMessage(msg);
    }
  }
  private final Messenger messenger=new Messenger(new MessengerHandler());
  @Nullable @Override public IBinder onBind(Intent intent) {
    return messenger.getBinder();
  }
}

In addition, register this service and start another process.

<service
  android:name=".messenger.MessengerService"
  android:process=":remote" />

2 client process

Bind with the remote service through bindService, and transmit data through message through messenger.

public class MessengerActivity extends AppCompatActivity {
  private Messenger messenger;
  private ServiceConnection serviceConnection=new ServiceConnection() {
    @Override public void onServiceConnected(ComponentName name, IBinder service) {
      messenger=new Messenger(service);
      Message message=Message.obtain(null,1);
      Bundle data=new Bundle();
      data.putString("name","Zhang San");
      message.setData(data);
      try {
        messenger.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(this,MessengerService.class);
    bindService(intent,serviceConnection,BIND_AUTO_CREATE);
  }
}

final result

2020-08-11 08:26:29.428 11947-11947/com.example.myapplication D/MessengerService: Zhang San

In addition, if you need the server to return information to the client, such as notification message receipt, you need to contact relpyTo.

First, modify the handleMessage on the server side

@Override public void handleMessage(@NonNull Message msg) {
      switch (msg.what){
        case 1:
          Log.d(TAG,msg.getData().getString("name")+"");
          //Return data in the same way
          Messenger messenger=msg.replyTo;
          Message message=Message.obtain(null,2);
          Bundle data=new Bundle();
          data.putString("reply","Received");
          message.setData(data);
          try {
            messenger.send(message);
          } catch (RemoteException e) {
            e.printStackTrace();
          }
          break;
        default:break;

      }      super.handleMessage(msg);
    }

On the client side, you need to add a handler and a messenger to receive messages

private static class  ClientHandler extends Handler{
  @Override public void handleMessage(@NonNull Message msg) {
    switch (msg.what){
      case 2:
        Log.d(TAG,msg.getData().getString("reply")+"");
        break;
      default:
        super.handleMessage(msg);
    }

  }
}
private Messenger mReplyMessenger=new Messenger(new ClientHandler());

Then modify the above code and enter a messenger for replyTo to receive the returned data from the server.

@Override public void onServiceConnected(ComponentName name, IBinder service) {
  messenger=new Messenger(service);
  Message message=Message.obtain(null,1);
  Bundle data=new Bundle();
  data.putString("name","Zhang San");
  message.setData(data);
  //To assign replyTo
  message.replyTo=mReplyMessenger;
  try {
    messenger.send(message);
  } catch (RemoteException e) {
    e.printStackTrace();
  }
}
2020-08-11 09:09:58.774 12510-12510/com.example.myapplication D/MessengerActivity: Received

4.4 ContentProvider

ContentProvider is a method provided in Android for data sharing between different applications. The bottom layer is also the binder implementation. ContentProvider mainly organizes data in the form of tables and can contain multiple tables. For each table, they have the hierarchy of rows and columns. Rows represent each record and columns represent a field in a record. In addition to tables, other types such as pictures, videos, etc. are supported.

4.5 Socket

Because the back is a little too deep, skip it for the time being and make it up later.

5. Binder connection pool

slightly

6. Select appropriate IPC scheme

slightly

Keywords: Android

Added by franknu on Mon, 10 Jan 2022 23:06:59 +0200