Android multi-process series

  • Several basic problems of Android multi-process communication
  • Android multiprocess manual Binder class
  • Android multiprocess Binder unbinding listener issues
  • Accidental death and permissions verification for Android Multiprocess Binder
  • Android Multiprocess Messenger

What is the Binder

  • Binder is an Android class that implements the IBinder interface
  • Binder is a cross-process communication method in Android from an IPC perspective
  • Binder can also be understood as a virtual physical device. The device driver is /dev/binder, which is not available in Linux
  • From the Android Framework perspective, Binder is the bridge between ServiceManger’s mangers (ActivityManager, WindowManager, etc.) and corresponding ManagerService
  • From the Android application layer, Binder is the communication medium between the client and the server. When bindService is returned, the Binder object on the server can be used to invoke the service on the server

The use of Binder

Generate the Binder class
  • Binder generation can be done in one of two ways: through AIDL files. We wrote Binder by hand
Generate binders from AIDL files
  • First you need to prepare a Parcelable object
public class Book implements Parcelable {

    public int bookId;
    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    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) {
            returnnew Book[size]; }}; protected Book(Parcelin) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    @Override
    public int describeContents() {
        return0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(bookId); dest.writeString(bookName); }}Copy the code
  • Then write an AIDL file

// Book.aidl
package com.xxq2dream.aidl;

parcelable Book;

Copy the code
// IBookManager.aidl package com.xxq2dream.aidl; // Declare any non-default types here with import statements // The Parcelable object import com.xxq2dream.aidl.Book must be displayed; // All parameters except the basic type must be marked with direction,inInterface IBookManager {List<Book> getBookList(); IBookManager {List<Book> getBookList(); void addBook(in Book book);
}
Copy the code

  • Binder classes can be generated using the Make Project command after the file is written

Simulates client and server interprocess communication
  • Start by writing a Service class and setting the Android: Process property to start in a different process
public class BookManagerService extends Service {
    private static final String TAG = "BookManagerService";

    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private Binder mBinder = new BookManagerImpl(){
        @Override
        public List<Book> getBookList() throws RemoteException {
            Log.e(TAG, "getBookList-->"+ System.currentTimeMillis());
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            Log.e(TAG, "addBook-->"); mBookList.add(book); }}; @Nullable @Override public IBinder onBind(Intent intent) { Log.e(TAG,"onBind-->"+ System.currentTimeMillis());
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate-->"+ System.currentTimeMillis());
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "IOS")); }}Copy the code
  • Registration Service
//AndroidManifest.xml
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <service
        android:name="com.xxq2dream.service.BookManagerService"
        android:process=":remote" />
</application>
Copy the code

  • The client simply binds to the server using the bindService method in the activity and invokes the server’s methods with the returned Binder
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.e(TAG, "ServiceConnection-->"+ System.currentTimeMillis()); / / the server comes back Binder for the client need an AIDL interface types of objects, namely we IBookManager above IBookManager bookManager = BookManagerImpl. AsInterface (iBinder);  List<Book> List = bookManager.getBookList(); Log.e(TAG,"query book list, list type:" + list.getClass().getCanonicalName());
                Log.e(TAG, "query book list:" + list.toString());
                Book newBook = new Book(3, "Android advanced");
                bookManager.addBook(newBook);
                Log.e(TAG, "add book:" + newBook);
                List<Book> newList = bookManager.getBookList();
                Log.e(TAG, "query book list:" + newList.toString());

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.e(TAG, "binder died"); }}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); Intent intent = new Intent(this, BookManagerService.class); // Bind the service, calling back onServiceConnected methodbindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {// unbindService unbindService(mConnection); super.onDestroy(); }}Copy the code
  • Through the above steps we have implemented a simple example of interprocess communication

Call the process
  • From the printed log, we can analyze the process of calling each method in the above example. Here we analyze the process of calling the server to get the book list
  • After the client calls the bindService method, BookManagerService is created, Binder is created by calling the Binder class constructor
  • The onBind method of BookManagerService then returns the Binder created to the client
  • The client’s onServiceConnected method is called, and Binder’s asInterface method is called to get an object of type AIDL interface, bookManager
  • The getBookList method that calls bookManager actually calls the corresponding getBookList method of the Proxy class in Binder
  • Binder’s Transact method is called in the Proxy class’s getBookList method to initiate a remote procedure call request. The onTransact method on the server side is called when the current thread is suspended
  • The onTransact method on the server side is called, and the code finds the specific method to call, in this case TRANSACTION_getBookList
  • Finally, the getBookList method corresponding to the mBinder object in BookManagerService is called to return the book list, mBookList, in the Parcel variable Reply
  • GetBookList method through the result of the Proxy class = reply. CreateTypedArrayList (Book. CREATOR); Get the data returned by the server
  • The client onServiceConnected method receives data and the call ends
Something to watch out for
  • When a client invokes a remote request, the client’s current thread is suspended until the server process returns data, so remote requests cannot be made in the UI thread
  • The onServiceConnected and onServiceDisconnected methods on the client side both run in the UI thread and cannot be called directly from there
  • Binder methods on the server side run in Binder thread pools, so Binder methods should be implemented synchronously regardless of whether they are time consuming
  • As mentioned above, the Binder methods on the server need to handle thread synchronization. In the example above, CopyOnWriteArrayList supports concurrent reads and writes and handles thread synchronization automatically
  • The only List available in AIDL is ArrayList, but AIDL supports abstract lists. So while the server returns a CopyOnWriteArrayList, Binder accesses the data according to List specifications and eventually forms a new ArrayList to pass to the client

conclusion

  • The above is only a simple application of Binder. There are still many problems needing attention in the use of Binder
  • What about Binder’s accidental death?
  • How do I register to listen for callbacks so that the server notifies the client that registered the callbacks as soon as a new message arrives? How to deregister?
  • How to perform permission verification

Welcome to follow my wechat official number, and learn and grow together with me!Copy the code