Exploration of Android Development Art--IPC Mechanism in Android

Keywords: Android Attribute socket Java

Multiprocess in Android

Why use multi-process

Multiprocess can avoid the method limitation of 65535, and because Android system has memory size limitation for each individual App application, it can increase the available memory through a multi-process approach of App.

Open multi-process

Opening multiple processes in Android is simple, but there are many things to consider when multiple processes are opened, including communication between processes, usage scenarios for creating and destroying processes, and unexpected errors after opening multiple processes.

<!--Main process-->
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

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

        <!--Open multiple processes, pass process Property to specify the process name-->
        <activity
            android:name=".Main2Activity"
            android:process=":remote">
        </activity>

The drawbacks of multi-process initiation

  1. First, the singleton pattern and static members fail completely after using multiple processes

  2. Thread Synchronization Failure

  3. Shared Preference is less reliable because concurrent reading and writing of XML files can be problematic

  4. Application s are created many times

Multiprocess Communication in Android

Android provides a variety of interprocess communication methods:

  • Using Bundle

  • Using shared files

  • Using Messenger

  • Use ContentProvider

  • Using Socket

  • Using Binder (AIDL)

Using Bundle

Because Bundle implements the Parcelable interface, it can transfer some data that can be wrapped between different processes, but there is no interaction, that is to say, it can only transfer the incoming data in one way when starting another process.

Using file sharing

Two files can exchange data by reading and writing the same shared file, which can achieve interactivity. However, because multiple processes in Android can not synchronize the reading and writing of files, this cross-process communication will lead to reading and writing errors.

Using Messenger

Messenger is a lightweight IPC mechanism implemented by Android. It can transmit some lightweight data Message objects. The actual underlying implementation is Binder. It only encapsulates AIDL to make it easier to use. It also handles message objects serially, so there is no concurrency problem.

Server-side code

public class MessengerService extends Service {

    private static final String TAG = "MessengerService";
    //Declare Messenger on the Service Side
    private final Messenger mMessenger = new Messenger(new MessageHandler());


    /**
     * Construct a Handler for processing messages on the Service side
     */
    private static class MessageHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Contast.MSG_FROM_CLIENT:
                    Log.i(TAG, "handleMessage: "+msg.getData().getString(Contast.MSG_CONTENT));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //Return Binder from it
        return mMessenger.getBinder();
    }
}

Client Code

public class Main2Activity extends AppCompatActivity {
    private Messenger mMessenger;

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMessenger = new Messenger(service);
            //Construct Message for Sending
            Message message = Message.obtain(null, Contast.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString(Contast.MSG_CONTENT,"This is the message from the client.");
            message.setData(data);

            try {
                mMessenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mMessenger = null;
        }
    };



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);


    }

    public void serviceStart(View view){
        //Start the Server Service
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        //Unbind
        unbindService(mServiceConnection);
        super.onDestroy();
    }
}

Use ContentProvider

ContentProvider is originally suitable for inter-process communication, and its underlying implementation is also the Binder mechanism. ContentProvider mainly defines some excuses for external applications to access this application, defines access interfaces through ContentProvider, and accesses data through ContentResolver.

Custom Implementation ContentProvider

/**
 * Customized Content Provider to Realize Addition, Deletion and Change Checking Method
 */
public class PipContentProvider extends ContentProvider {


    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

Register a custom ContentProvider

<provider
    <!--Declare that you want to visit theContentProviderRequired permissions-->
    android:permission="com.wei.piechart.PROVIDER"
    <!--ContentProvider Unique ID-->
    android:authorities="com.wei.piechart.PipContentProvider"
    <!--Declaring to be registered ContentProvider-->
    android:name=".PipContentProvider"
    android:process=":provider"/> 

Accessing the specified ContentProvider through ContentResolver and Uri in the program

//Get ContentResolver
ContentResolver contentResolver = getContentResolver();
//Specify the object to be accessed through Uri
Uri uri = Uri.parse("content:com.wei.piechart.PipContentProvider");
//Similar to the SQL database, query operations can be performed, and the results are placed in the Cursor result set.
Cursor cursor = contentResolver.query(uri, null, null, null, null);

Using Socket

Socket socket book is a way of network communication, but it can also be used for two processes in the local machine by using port number.
Socket is divided into Service Socket and Socket, one for the server, one for the client, and Bluetooth through the Socket interaction principle is the same.

Using Binder (AIDL)

To be precise, AIDL is not an IPC mechanism, but an AIDL (Android Interface Definition Language) Android Interface Definition Language, through which we can easily create an IPC model for communication through Binder. In fact, Binder is Android's most mainstream way of inter-process communication. It is also the underlying implementation principle of some mechanisms in front of us. AIDL just gives us a convenient way to implement Binder mechanism.

Read this article about the Binder mechanism in Android Detailed explanation of Android Binder mechanism

Usage restriction

Type restriction

First of all, not all data types can be used in AIDL files. The following types can be used in AIDL files:

  • Basic data type (int long... ).

  • String and CharSequence

  • List: Only ArrayList is supported, and the elements in the List must be able to be supported by AIDL

  • Map: Only HashMap is supported, and the elements in the Map must be able to be supported by AIDL

  • Parcelable: All objects that implement the Parcelable interface

  • AIDL: All AIDL interfaces themselves can also be used in AIDL files

Type declaration restrictions

In addition to basic types, all types need to be directed, input type parameters are represented by in, output type parameters are represented by out, and input and output type parameters are represented by in out.

Explicit import s are required for custom arcelable and AIDL types used

Technological process

Define AIDL interface

Book.aidl

// Book.aidl
package com.wei.piechart;

// Declare any non-default types here with import statements
parcelable Book;

IBookManager.aidl

// IBookManager.aidl
package com.wei.piechart;

// Declare any non-default types here with import statements
import com.wei.piechart.Book;

interface IBookManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    List<Book> getBookList();
    void addBook(in Book book);
}

Book.java

public class Book implements Parcelable{
    int id;
    String name;

    protected Book(Parcel in) {
        id = in.readInt();
        name = 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(id);
        dest.writeString(name);
    }
}

Generate IBookManager.java

After defining the interface and rebuilding the project, AS automatically generates the IBookManager.java class. This is the main function of AIDL. By defining the required fields and methods in the Aidl file, the class that implements Binder can be quickly generated.

Implementing Server

public class BookManagerService extends Service{
    private static final String TAG = "BookManagerService";

    //A list of books on the Service side, supporting multiple processes
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    //Binder for returning to the client
    private Binder mBinder = new IBookManager.Stub(){
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            if(!mBookList.contains(book)){
                mBookList.add(book);
            }
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //Return the Binder created by the server to the client
        return mBinder;
    }

}

Implementing Client

//Declare Service Connection
    private ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //Converting the Binder returned from the server into interface usage on the client side
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                bookManager.addBook(new Book(1, "First book"));
                List<Book> books = bookManager.getBookList();
                Log.i(TAG, "onServiceConnected: "+books.toString());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };


public void serviceStart(View view) {
        //Start the Server Service
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}

Posted by seidel on Thu, 21 Mar 2019 14:30:54 -0700