There are several ways to communicate across processes in Android, such as
  • Intent
  • Messenger
  • AIDL (Android Interface Definition Language)
  • ContentProvider
  • Socket
Take AIDL as an example, when using AIDL to achieve Android cross-process communication, there are usually three steps:
  1. Define the AIDL interface file that returns binder to the client in the onBind method of the Service

  2. The client is bound to the server, retrieving the binder in the callback function onServiceConnected

  3. The Stub’s asInterface method translates to the interface we defined, and then invokes the server-side logic.

This is a typical CS (client-server) architecture. Let’s use AIDL to implement cross-process communication. First, let’s define a problem:

If xiao Wang is the boss of a chain supermarket, what he cares about most is the current scale of his supermarket and the circumstance of the turnover of his supermarket. He is the end that needs to be served, so wang is defined as the client. For the boss’s needs, we need to provide two services. One is to query the number of chain supermarkets and the turnover of supermarkets.

Now that the requirement is there, let’s implement it:

The client defines a BossActivity that displays the current size and turnover of the supermarket chain. The server defines two services for decoupling, OrderService (to query turnover) and StoreService (to query supermarket size)

Now follow the three steps above, and we’ll implement them one by one

1. Define the AIDL

  • defineIOrderService.aidlIncludes services to check turnover
package qiwoo.android.sync.binder; Interface IOrderService {// Get the turnover int getOrderAmount(); }Copy the code
  • defineIStoreService.aidlIncluding a service to check the size of the supermarket, which is actually the number of stores
package qiwoo.android.sync.binder; import qiwoo.android.sync.binder.Store; List<Store> getStores(); }Copy the code

2. Implement the interface in Service and return it as binder

  • OrderServiceThe concrete implementation is as follows
public class OrderService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
    }

    IOrderService.Stub mOrderService = new IOrderService.Stub{@override public int getOrderAmount() throws RemoteException {//return100; }}; Nullable @override public IBinder onBind(Intent Intent) {// Return OrderService binderreturnmOrderService; }}Copy the code
  • StoreServiceThe concrete implementation is as follows
public class StoreService extends Service {

    private List<Store> stores;

    @Override
    public void onCreate() { super.onCreate(); Store1 = new Store(1,"qiwoo"."123"."beijing");
        Store store2 = new Store(2, "mobile"."123"."beijing");

        stores = new ArrayList<>();
        stores.add(store1);
        stores.add(store2);
    }

    IStoreService.Stub mStoreService = new IStoreService.Stub() {

        @Override
        public List<Store> getStores() throws RemoteException {
            returnstores; }}; Nullable @override public IBinder onBind(Intent Intent) {// Return StoreService binderreturnmStoreService; }}Copy the code

In 3.BossActivityIn thebindService

  • Binding service
Intent orderIntent = new Intent();
orderIntent.setClass(this, OrderService.class);
bindService(orderIntent, mOrderServiceConnection, Context.BIND_AUTO_CREATE);

Intent storeIntent = new Intent();
storeIntent.setClass(this, StoreService.class);
bindService(storeIntent, mStoreServiceConnection, Context.BIND_AUTO_CREATE);

Copy the code
  • The incoming ServiceConnection
private ServiceConnection mOrderServiceConnection = new ServiceConnection() {@override public void onServiceConnected(connected) {// We got the binder mOrderService = IOrderService. Stub. AsInterface (service); } @Override public void onServiceDisconnected(ComponentName name) { } };Copy the code
  • Invoking a remote service
Try {/ / get the binders in onServiceConnected int amount = mOrderService. GetOrderAmount (); Toast.makeText(BossActivity.this,"Congratulations boss turnover is:" + amount + "$", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
    e.printStackTrace();
}

Copy the code

So far, so good. We implemented cross-process communication with AIDL and tested it as expected.



Great. The owner is rich, so we can add chicken legs again at night. But is there a problem? To call a service from another process in a BossActivity, you must wait for the ServiceConnection passed in bindService to get its onServiceConnected callback, which is called with the Binder asynchronously. Sometimes we don’t want to do this. Is there a way to get binder directly through a GET method?

The answer is yes. Now let’s go back to the beginning of this article and look at some common ways to implement cross-process communication. There is a ContentProvider and then it will be our main character. We’ll use it to get binder synchronously on the client. How do we do that? Same three steps.

  • Define AIDL interface files and implementation classes

  • Define a ContentProvider that returns the specific service binder depending on the query parameters

  • Query the ContentProvider to get the Cursor, convert it to the interface we define via the Stub’s asInterface method, and invoke the server-side logic.

1. Define AIDL and implementation classes

AIDL is exactly the same as above

  • OrderServiceImplimplementation
public class OrderServiceImpl extends IOrderService.Stub {

    @Override
    public int getOrderAmount() throws RemoteException {
        return100; }}Copy the code
  • StoreServiceImplimplementation
public class StoreServiceImpl extends IStoreService.Stub {

    private List<Store> stores;

    public StoreServiceImpl() {

        Store store1 = new Store(1, "qiwoo"."123"."beijing");
        Store store2 = new Store(2, "mobile"."123"."beijing");

        stores = new ArrayList<>();
        stores.add(store1);
        stores.add(store2);
    }

    @Override
    public List<Store> getStores() throws RemoteException {
        returnstores; }}Copy the code

Definition 2.BinderProviderThe main code is as follows:

public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {

    IBinder binder;

    if (selectionArgs[0].equals(SERVICE_ORDER)) {
        binder = new OrderServiceImpl();

        Log.d(TAG, "Query OrderServiceImpl");

    } else if (selectionArgs[0].equals(SERVICE_STORE)) {
        binder = new StoreServiceImpl();

        Log.d(TAG, "Query StoreServiceImpl");

    } else {
        return null;
    }

    BinderCursor cursor = new BinderCursor(new String[]{"service"}, binder);

    return cursor;
}

Copy the code

SQLiteDatabase’s Query method returns a Cursor directly, which is an interface and cannot be instantiated directly. So we need to find a way to instantiate a Cursor, and we’re using MatrixCursor. So once we have the Cursor we can return different binders depending on the query parameters and put them back into the Cursor. So let’s look at BinderCursor

public class BinderCursor extends MatrixCursor {

    static final String KEY_BINDER = "binder";

    Bundle mBinderExtra = new Bundle();

    public static class BinderParcelable implements Parcelable {

        public IBinder mBinder;

        public static final Creator<BinderParcelable> CREATOR = new Creator<BinderParcelable>() {
            @Override
            public BinderParcelable createFromParcel(Parcel source) {
                return new BinderParcelable(source);
            }

            @Override
            public BinderParcelable[] newArray(int size) {
                returnnew BinderParcelable[size]; }}; BinderParcelable(IBinder binder) { mBinder = binder; } BinderParcelable(Parcelsource) {
            mBinder = source.readStrongBinder();
        }

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

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeStrongBinder(mBinder);
        }
    }

    public BinderCursor(String[] columnNames, IBinder binder) {
        super(columnNames);

        if(binder ! = null) { Parcelable value = new BinderParcelable(binder); mBinderExtra.putParcelable(KEY_BINDER, value); } } @Override public BundlegetExtras() {
        returnmBinderExtra; }}Copy the code

As you can see, it inherits from the MatrixCursor and then wraps the Binder with a Bundle so that you can return a New Object of the MatrixCursor.

3. The queryContentProviderTo obtaincursorInvoke server-side logic.

final ContentResolver resolver = MainActivity.this.getContentResolver();

final Cursor cu = resolver.query(CONTENT_URI, null, null, new String[]{SERVICE_ORDER}, null);
if (cu == null) {
    return;
}

IBinder binder = getBinder(cu);
try {
    IOrderService orderService = IOrderService.Stub.asInterface(binder);
    int amount = orderService.getOrderAmount();

    Toast.makeText(MainActivity.this, "Congratulations boss turnover is:" + amount + "$", Toast.LENGTH_SHORT).show();
    
} catch (RemoteException e) {
    e.printStackTrace();
}

cu.close();


Copy the code

Here, we pass in the query parameters SERVICE_ORDER and SERVICE_STORE to obtain the sales and supermarket size data. If there are many services, we can encapsulate this part of the code and write a management class that returns different services based on different parameters.

The binder is now synchronized, and no longer needs to wait for a callback. The printed result is the same as above and will not be displayed.

In this paper, the demo address: github.com/77Y/SyncBinder

Pay attention to wechat public number, the latest technology dry goods real-time push