Yesterday, our supervisor said that we were going to split the project. Now the project relies on several libraries responsible for serial communication, and we plan to turn these libraries into independent apps for data interaction through the Cross-process mechanism of Android. Then let me write a Demo of cross-process communication to test it.

There are several ways to communicate across processes, and I’m using the AIDL approach here.

1. Service and Activity communication within the same APP

First, inter-process communication within the same application is realized, and then inter-app communication is realized. Since AIDL is c/ S mode, we will create a server application first.

1. Create a server APP and then create a Service.

The server package name is com.aidl. Service

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="1000">
                <action android:name="com.aidl.service.MyService"></action>
            </intent-filter>
        </service>
Copy the code

You can also create it manually. The enabled and exported properties should be set to true to allow other applications to call.

2. Create the message object that sends the message

AIDL does not support passing normal Java objects, but does support Parcelable objects, so our message object will implement Parcelable.

public class Msg implements Parcelable {

    private String msg;
    private long time;
    public Msg(String msg){
        this.msg = msg;
    }
    public Msg(String msg, long time) {
        this.msg = msg;
        this.time = time;
    }
    protected Msg(Parcel in) {
        msg = in.readString();
    }
    public static final Creator<Msg> CREATOR = new Creator<Msg>() {
        @Override
        public Msg createFromParcel(Parcel in) {
            return new Msg(in);
        }
        @Override
        public Msg[] newArray(int size) {
            return newMsg[size]; }};@Override
    public int describeContents(a) {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(msg);
    }
  	//set, get method
}
Copy the code
3. Create AIDL file

Create a Msg AIDL file in the root directory of the project. The package name is the same as the project package name, and msg. aidl is declared as parcelable, and the msG. aidl path is the same as the msG. Java path.

package com.aidl.service;
import com.aidl.service.Msg;

interface IReceiveMsgListener {
   void onReceive(in Msg msg);
}
Copy the code

Import the full path to msg.aidl import com.aidel.service.msg. Msg in onReceive() uses the in input flag.

Create imsgManager.aidl interface for message management

package com.aidl.service;
import com.aidl.service.IReceiveMsgListener;
import com.aidl.service.Msg;

interface IMsgManager {
   void sendMsg(in Msg msg);
   void registerReceiveListener(IReceiveMsgListener receiveListener);
   void unregisterReceiveListener(IReceiveMsgListener receiveListener);
}
Copy the code

Methods for sending messages, registering and unlistening messages are provided in imsgManager.aidl. Also import the full path of msg. aidl and iReceivemsgListener. aidl.

At this point the AIDL file has been written. Finally, you need to Make Project, and the compiler generates the corresponding Binder files

4. Write MyService service code
public class MyService extends Service {
    AIDL does not support normal interface callbacks. Use RemoteCallbackList to implement interface callbacks
    private RemoteCallbackList<IReceiveMsgListener> mReceiveListener = new RemoteCallbackList<IReceiveMsgListener>();

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

    class MyBinder extends IMsgManager.Stub {
        
        // Send a message
        public void sendMsg(Msg msg) {
            receiveMsg(msg);
        }
        
        / / register
        @Override
        public void registerReceiveListener(IReceiveMsgListener receiveListener) throws RemoteException {
            mReceiveListener.register(receiveListener);
        }
        
        // Unregister
        @Override
        public void unregisterReceiveListener(IReceiveMsgListener receiveListener) throws RemoteException {
            boolean success = mReceiveListener.unregister(receiveListener);
            if (success){
                Log.d("tag"."=== Deregistration successful");
            }else {
                Log.d("tag"."=== Deregistration failed"); }}@Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            return super.onTransact(code, data, reply, flags); }}// Received message processing
    public void receiveMsg(Msg msg) {
        // The notification Callback loop begins, returning N as the number of mReceiveListener callbacks implemented
        final int N = mReceiveListener.beginBroadcast();
        msg.setMsg("This is server, I received:"+msg.getMsg());
        for (int i = 0; i < N; i++){
            IReceiveMsgListener listener = mReceiveListener.getBroadcastItem(i);
            if(listener ! =null) {try {
                    listener.onReceive(msg);
                } catch(RemoteException e) { e.printStackTrace(); }}}// Notification notification Callback loop endsmReceiveListener.finishBroadcast(); }}Copy the code

Binder mechanisms are used to register, unregister, and send services.

The Activity code:

public class MainActivity extends AppCompatActivity {

    MyService.MyBinder binder = null;
    ServiceConnection mConnection;
    private ListView mListView;
    private EditText mEditText;
    private List<Msg> mMsgs = new ArrayList<>();
    private ListAdapter mAdapter;
    private IMsgManager mIMsgManager;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg); mAdapter.notifyDataSetChanged(); }};@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.listview);
        mEditText = (EditText) findViewById(R.id.edit_text);
        mAdapter = new ListAdapter(this, mMsgs);
        mListView.setAdapter(mAdapter);

        mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                binder = (MyService.MyBinder) iBinder;
                IMsgManager msgManager = IMsgManager.Stub.asInterface(iBinder);
                mIMsgManager = msgManager;
                try {
                    mIMsgManager.asBinder().linkToDeath(mDeathRecipient, 0);
                    mIMsgManager.registerReceiveListener(mReceiveMsgListener);
                } catch(RemoteException e) { e.printStackTrace(); }}@Override
            public void onServiceDisconnected(ComponentName componentName) {}};// Note that an Activity can communicate with an Intent only if it is the same process as a Service
        Intent intent = new Intent(MainActivity.this, MyService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);// Start the service

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (TextUtils.isEmpty(mEditText.getText().toString())) {
                    Toast.makeText(MainActivity.this."Message empty", Toast.LENGTH_SHORT).show();
                    return;
                }
                binder.sendMsg(newMsg(mEditText.getText().toString().trim())); }}); findViewById(R.id.btn_exit).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MainActivity.this.finish(); }}); }private IReceiveMsgListener mReceiveMsgListener = new IReceiveMsgListener.Stub() {

        @Override
        public void onReceive(Msg msg) throws RemoteException {
            msg.setTime(System.currentTimeMillis());
            mMsgs.add(msg);
            mHandler.sendEmptyMessage(1); }};private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        // The interface that receives the callback when the process hosting the IBinder disappears
        @Override
        public void binderDied(a) {
            if (null == mIMsgManager) {
                return;
            }
            mIMsgManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mIMsgManager = null;
            // Start from scratch}};@Override
    protected void onDestroy(a) {
        // Unregister
        if (null! = mIMsgManager && mIMsgManager.asBinder().isBinderAlive()) {try {
                mIMsgManager.unregisterReceiveListener(mReceiveMsgListener);
            } catch(RemoteException e) { e.printStackTrace(); }}// Unbind the service
        unbindService(mConnection);
        super.onDestroy(); }}Copy the code

The operation screenshot is as follows:

android:process=":remote"
Copy the code

To start, use action:

 Intent intent = new Intent();
 intent.setAction("com.aidl.service.MyService");
Copy the code

2. Communication between two or more Apps

Above, we have completed the function of the server side and implemented the two-way communication between the activity and the service. Now you just need to put the activity function into another application to implement it.

1. Create a client application.

Package name: com. Aidl. Client

2. Copy the AIDL file from the server to the client

Copy the AILD folder of the server to the client and keep the same package name as the server. The server and client AIDL directories are as follows.

3, copy the msg.java object

We know that the package name of the client is com.aidl. Client and the msg.aidl path is com.aidl. Service, so we will create msg.java in com.aidl.

public class MainActivity extends AppCompatActivity {

    private IMsgManager myBinder;/ / define AIDL
    private ListView mListView;
    EditText mEditText;
    private List<Msg> mMsgs = new ArrayList<>();
    private ListAdapter mAdapter;
    private IMsgManager mIMsgManager;
    private Msg mMsg;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    mAdapter.notifyDataSetChanged();
                    mListView.smoothScrollToPosition(mMsgs.size() - 1); }}}; ServiceConnection mConnection =new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            myBinder = IMsgManager.Stub.asInterface(iBinder);
            IMsgManager msgManager = IMsgManager.Stub.asInterface(iBinder);
            mIMsgManager = msgManager;
            try {
                // Link to the death agent and receive a callback when IBinder dies
                mIMsgManager.asBinder().linkToDeath(mDeathRecipient, 0);
                // Register a message listener
                mIMsgManager.registerReceiveListener(mReceiveMsgListener);
            } catch(RemoteException e) { e.printStackTrace(); }}@Override
        public void onServiceDisconnected(ComponentName name) {}};@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.listview);
        mEditText = (EditText) findViewById(R.id.edit_text);
        mSendCountTv = (TextView) findViewById(R.id.send_count_tv);
        mReceiveCountTv = (TextView) findViewById(R.id.receive_count_tv);
     	mMsg = new Msg("");
        mAdapter = new ListAdapter(this, mMsgs);
        mListView.setAdapter(mAdapter);
   
        Intent intent = new Intent();
        // Cross-process communication needs to be started using action
        intent.setAction("com.aidl.service.MyService");
        // after android5.0, if the servicer is not in the same App package, you need to set the package name of the application where the service resides
        intent.setPackage("com.aidl.service");
        //开启Service
        bindService(intent, mConnection, BIND_AUTO_CREATE);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    String msg = mEditText.getText().toString().trim();
                    if (TextUtils.isEmpty(msg)) {
                        Toast.makeText(MainActivity.this."Message cannot be empty.", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    mMsg.setMsg(msg);
                    // Pass messages to service through binder
                    myBinder.sendMsg(mMsg);
                } catch(RemoteException e) { e.printStackTrace(); }}}); }// Message callback listener
    private IReceiveMsgListener mReceiveMsgListener = new IReceiveMsgListener.Stub() {
		// Received the server message
        @Override
        public void onReceive(Msg msg) throws RemoteException {
            msg.setTime(System.currentTimeMillis());
            if (mMsgs.size() > 100) {
                mMsgs.clear();
            }
            mMsgs.add(msg);
            mHandler.sendEmptyMessage(1); }};private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        /** * The interface that receives the callback when the process hosting IBinder disappears */
        @Override
        public void binderDied(a) {
            if (null == mIMsgManager) {
                return;
            }
            mIMsgManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mIMsgManager = null;
            // Rebind the remote service here}};@Override
    protected void onDestroy(a) {
   		/ / unbundling
        super.onDestroy(); }}Copy the code

Client operation screenshot:

The full project has been uploaded to GitHub: github.com/Zhengyi66/A…