Interprocess Communication--IPC

Posted by kaimason1 on Wed, 10 Jul 2019 19:56:18 +0200

Preface:

Inter-process Communication (IPC) refers to the communication between processes. Generally speaking, an app has only one process, but may have multiple threads, so we use multithreaded communication, such as handler,AsyncTask.

But in some special cases, we need multiple processes in our app, or we need to communicate across processes when we call a remote service.

1. Setting up multiple processes

The Android multi-process setup step is simple, using only four components plus process attributes in the manifest file

 <service android:name=".MessagerService"
             android:process=":messager">
</service>

(: Messageager's final process name becomes package name +: messager)

Although multi-process setup is simple, there are a series of problems when using it.

(Two processes correspond to different memory regions)

  • 1.Application objects are created many times

  • 2. Static members are not shared

  • 3. Synchronization lock failure

  • 4. Single case mode failure

  • 5. Data transfer objects must be serializable

2. Serializability

The object of interprocess communication is strictly required. In addition to the basic data type, other objects must be serializable if they want to be transferable. Serializable or Parcelable is usually realized by Android.

If you don't need to transfer objects of non-basic data types in process communication, you may not understand serialization, but serialization is the basis of interprocess communication, so it's advisable for friends who don't know to familiarize themselves with it first.

I've introduced serialization before, and I won't repeat it here.

Serialization--Serializable and Parcelable

http://blog.csdn.net/yulyu/ar...

3. Communication

There are many ways to communicate across processes, such as through Intent, through AIDL and Messager, through socket. Here we mainly introduce Binder-based AIDL and Messager.

3.1 Intent

Intent's data transmission is the most commonly used in our daily life. Its principle is actually the encapsulation of Binder, but it can only achieve one-way data transmission, so it can't achieve cross-process communication very well. We will not introduce it here.

3.2 Messager

The bottom layer of Messager is also Binder-based. In fact, it should be said that it encapsulates a layer on the basis of AIDL.

Generally speaking, Android uses Binder mainly through the bind Service. The server (not the background here, refers to one of the processes) mainly runs the Service. The client obtains the relevant Binder through the bindService. Binder acts as a bridge for cross-process communication.

Here we first demonstrate multi-process communication in the same application

3.2.1 Server

First we create a Service.

public class XiayuService extends Service{
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

And configure his process in the manifest file

<service android:name=".XiayuService"
             android:process=":xiayu"
 />

Create a Hander in Service to receive messages

private final static Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        System.out.println("Digua melon,I'm a potato.,I'm a potato., Answer when you hear it.,Answer when you hear it.");

    }
};

Create a Messager in Service and put Handler in it

private final static Messenger mMessenger = new Messenger(mHandler);

Rewrite the onbind method to return the Binder in Messager

public IBinder onBind(Intent intent) {
    return mMessenger.getBinder();
}

3.2.2 Client

Create an object to implement Service Connection

private class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //This method is called when the service is connected
            //TODO 
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
}

Binding services

Intent    intent = new Intent(MainActivity.this, XiayuService.class);

MyServiceConnection  myServiceConnection = new MyServiceConnection();

bindService(intent, myServiceConnection, BIND_AUTO_CREATE);

After binding the service, the onService Connected method of Service Connection is invoked, and the Handler on the server side can receive the message by sending the message through the Messager.

private class MyServiceConnection implements ServiceConnection {

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //Create Messager through Binder
        Messenger messenger = new Messenger(service);
        //Create msg
        Message msg = Message.obtain();

        try {
            //Send messages through Messager
            messenger.send(msg);
            
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
}

In this way, we can get a Messager containing Binder through bindService to communicate, but we only realize client-to-server messaging at present, so how can server-to-client messaging?

First we modify the server-side code, first we modify the Service's Hadler.

(The key code is Messenger messenger = msg.replyTo)

private final static Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        System.out.println("Digua melon,I'm a potato.,I'm a potato., Answer when you hear it.,Answer when you hear it.");
        //Get Messager
        Messenger messenger = msg.replyTo;
        //Create messages
        Message msg_reply = Message.obtain();
        try {
            //Send out
            messenger.send(msg_reply);
            
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
};

Then we add a Handler and Messager to the client to process messages.

private final static Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        System.out.println("Potato,Potato,I am Digua,I have received your message.");
    }
};

private final static Messenger mReplyMessager = new Messenger(mHandler);

Another key point is to send messages from clients to servers by messaging.

(msg.replyTo =mReplyMessager)

private class MyServiceConnection implements ServiceConnection {

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Messenger messenger = new Messenger(service);

        Message msg = Message.obtain();
        //Transfer the client's Messager to the server through msg (key code)
        msg.replyTo =mReplyMessager;
        
        try {

            messenger.send(msg);

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

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
}

In this way, cross-process communication between server and client can be well realized.

If you need to transfer data, you can set data through Bundle, which can also transfer serializable objects through messages, in addition to the basic data types.

Sender:

        Message msg = Message.obtain();

        Bundle bundle = new Bundle();

        //Transfer serialized objects
        //bundle.putParcelable();

        //bundle.putSerializable();

        msg.setData(bundle);

Receiver:

        Bundle data = msg.getData();
        
        //get data
        //data.getSerializable()

        //data.getParcelable()

3.2.3 Disadvantage

Above, we have realized cross-process communication, but there are drawbacks in it. The server processes the client's messages serially and must process them one by one. So when the concurrency is large, it is not suitable to communicate through Messager.

3.2.4 Attention

The above demonstrates inter-process communication within an application. Binding services can be bound by displaying intentions, but if it is inter-process communication across applications, implicit intentions need to be used. One thing to note here is that after 5.0 implicit intentions to open or bind services need setPackage(Service package name), otherwise they will be reported. Wrong

    mIntent = new Intent();

    //Set Package to Service's Package Name
    mIntent.setPackage("com.xiayu.ipcservice");

    mIntent.setAction("myMessager");

3.3 AIDL

As mentioned above, cross-process merging via Meaager is not suitable for high-volume cases, so if there is a large amount of concurrency, what do we use to deal with it? That can be done through AIDL, here is the description of Google.

Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want 
to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you 
should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle 
multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before 
implementing an AIDL.

The main idea is that you can use Messager to handle simple cross-process communication, but high concurrency requires AIDL.

Let's start by demonstrating cross-process communication in the same application.

3.3.1 Server

First we create a Service

public class AIDLService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

Then set up the Service process in the manifest file

    <service android:name=".AIDLService"
             android:process=":xiayu"
    />

Then right-click on the new AIDL file and Android Studio will help you create an AIDL file under the same folder in your Aidl directory.

// IShop.aidl
package com.xiayu.aidldemo;

interface IShop {
       //This method is a way to create aidl's own way to tell you which data types you can use
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

There will be an interface in the AIDL file and a method is declared. That method mainly tells you which data types AIDL supports. So we delete this method, and we declare a method ourselves for later calls.

(Note: Every time you modify an AIDI file, you need to synchronize to take effect, because after each synchronization, Android Studio generates the corresponding java file in the project / build/generated/source/aidl/debug directory.)

interface IShop {
    //Self-declared methods for subsequent calls
    void sell();
}

We create a Binder in Service and return it when onbind

public class AIDLService extends Service{

    private Binder mBinder = new IShop.Stub() {
        @Override
        public void sell() throws RemoteException {
            System.out.println("patron,What do you need??");
        }
    };


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

3.3.2 Client

Create a custom class to implement Service Connection

private class  XiayuConnection implements ServiceConnection{

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
       //This method is called when the binding is successful
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
}

Binding services, when the binding is successful, follow the onService Connected method of Connection and pass Binder over

mIntent = new Intent(this, AIDLService.class);
mXiayuConnection = new XiayuConnection();
bindService(mIntent, mXiayuConnection, BIND_AUTO_CREATE);

In the onService Connected method, the object passed by the server is retrieved through the asInterface and the method of the server is invoked.

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
    //Get the object passed to the server
    IShop iShop = IShop.Stub.asInterface(service);
    try {
        iShop.sell();
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

Now the client can call the sell method for cross-process communication, but at present it can only transfer data of basic data type. What if you want to transfer other data? Let's go on.

3.3.3 Transfer Complex Data through AIDL

First we need to know what data types AIDL supports.

  • 1. Basic data types

  • 2. Implementing the object of Parcelable interface

  • 3.List: Only ArrayList is supported, and the elements in it are supported by AIDL when needed

  • 4.Map: Only HashMap is supported, and both key and value are supported by AIDL.

So we define an object Product to implement Parcelable interface, how to implement Parcelable I do not repeat here, if you do not understand the friends can see the author's previous article.

Serialization--Serializable and Parcelable

http://blog.csdn.net/yulyu/ar...

Product We set up two fields

public class Product implements Parcelable {
    public String name;
    public int    price;
    ...
}

Next we need to create an aidl file with the same file name in the same directory of the aidl folder

(Note that here we are going to create it by new File and enter the file suffix aidl by ourselves. If you create it by new AIDL, he will prompt you to Interface Name must be unique)

Next, we need to enter the package name in the aidl file and declare the variable as a Parcelable type.

(Note that the statement here is in lowercase parcelable)

// Product.aidl
package com.xiayu.aidldemo;

parcelable Product;

Let's go back to IShop.aidl, delete the previous sell method, and create two new methods.

// IShop.aidl
package com.xiayu.aidldemo;

import com.xiayu.aidldemo.Product;

interface IShop {

    Product buy();

    void setProduct(in Product product);

}

Here are three things to note

(1) Although IShop. Aidl is in the same package as Product.aidl, it still needs to import manually.

(2) When declaring the method here, we need to add a tag before the parameter. There are three kinds of tag: in, out and inout. This means the flow direction that the parameter can support:

  • 1.in: This object can go from the client to the server, but as a return value from the server to the client, the data will not be transferred to the past (it will not be null, but the fields are not assigned).

  • 2.out: This object can be used as a return value from the server to the client, but the data from the client to the server will be empty (not null, but the fields are not assigned).

  • 3.inout: From the client to the server, or as a return value from the server to the client

Summarize with a picture:

(Don't set them all to inout, depending on the requirements, because it increases overhead)

(3) The default template for implementing Parcelable only supports in, and if you need to support out or inout, you need to implement the readFromParcel method manually.

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(this.name);
    dest.writeInt(this.price);
}
//Manual implementation of this method
public void readFromParcel(Parcel dest) {
    //Note that the read order here is the same as that in the writeToParcel() method.
    name = dest.readString();
    price = dest.readInt();
}

Now you can communicate in the client through the IShop call method

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
    IShop iShop = IShop.Stub.asInterface(service);
    try {
        //Call methods for communication
        iShop.setProduct(mProduct);
        Product buy = iShop.buy();
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

3.4 Multiprocess Communication (AIDL) between Different Applications

We introduced the inter-process communication in the same application, and then we introduced the inter-process communication between different applications.

3.4.1 Server

First, we need to put Product.java in a folder with the same name in the aidl directory (if we want to provide services to other app s, it's better to put all the objects we need in the aidl directory, so it's easier to copy)

But if you run the program at this time, the compiler will prompt you that you can't find the Product, because Android Studio will go to the Java directory by default. At this time, you need to add a code between the build.gradle file and the android {} so that the java files in the aidl directory can also be identified.

sourceSets {
    main {
        java.srcDirs = ['src/main/java', 'src/main/aidl']
    }
}

Then we add intent-filter to Service so that other applications can bind services with implicit intent, and the server-side modifications are over.

<service android:name=".AIDLService"
         android:process=":xiayu">
    <intent-filter>
        <action android:name="action.xiayu"/>
    </intent-filter>
</service>

3.4.2 Client

We need to create a new application to serve as a client and copy all the files in the aidl directory on the server side. It's important to note that the directory inside can't be changed. It needs to be the same as before.

By clicking Synchronization, Android Studio automatically generates the corresponding java files for us to use.

At this point we need to bind services with implicit intent

(Note: After 5.0 implicit intention to open or bind service to setPackage, otherwise error will be reported)

    mIntent.setAction("action.xiayu");
    mIntent.setPackage("com.xiayu.aidldemo");

Next, as before, create a class to implement Service Connection

private class  XiayuConnection implements ServiceConnection {

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //TODO
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
}

Binding services

bindService(mIntent, mXiayuConnection, BIND_AUTO_CREATE);

Communicate via IBinder in Service Connection's onService Connected

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        IShop iShop = IShop.Stub.asInterface(service);
        try {
            iShop.setProduct(mProduct);
            Product buy = iShop.buy();
            System.out.println("buy=" + buy.price);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

Release resources when unbound

public void unbind(View v) {
    unbindService(mXiayuConnection);
    mXiayuConnection = null;
    mIShop = null;
}

So we can communicate between different applications through the IShop we get.

Finally, there are a few points to be noted when using services (very simple, but some people often overlook these points).

  • 1: startService and stopService need to use the same Intent object

  • 2: BidService and unbindService need to use the same Service Connection object

  • 3: After 5.0, implicitly intend to open or bind service to setPackage (package name)

Popular articles

Topics: Java Android socket Google