Android uses service to realize IPC communication in two ways

Time:2021-6-9

Using Aidl to realize IPC communication

1、 Code operation — service binding with remote process

The above code communicates with service in the current process. Now let’s implement how to bind services in different processes.

Aidl: Android interface definition language, namely Android interface definition language.

Service needs Aidl to transfer data across processes. The main steps are as follows:

  1. Write Aidl file, as automatically generated Java class to achieve IPC communication agent
  2. Inherit your own Aidl class and implement the methods in it
  3. Return our implementation class in onbind() and expose it to the outside world
  4. Objects that need to communicate with service are bound to service through bindservice and receive data in serviceconnection.

We use code to achieve the following:

1. First, we need to create a new service


public class MyRemoteService extends Service {
 @Nullable
 @Override
 public IBinder onBind(Intent intent) {
  Log.e("MyRemoteService", "MyRemoteService thread id = " + Thread.currentThread().getId());
  return null;
 }
}

2. Declare our service in the manifest file and specify the name of the running process. In this case, you can not only write the remote process name, you can write any process name you want


<service
    android:name=".service.MyRemoteService"
    android:process=":remote" />

3. Create a new Aidl file to transfer data between user processes.

Aidl supports eight basic data types, string type, charsequence, list, map and custom type. List, map and custom type are explained below.

There will be a default implementation method in it. You can delete it. Here we create a new file as follows:

package xxxx;// Package name of Aidl
//Cannot have modifier before interface
interface IProcessInfo {
 //All the communication methods you want can be added here
 int getProcessId();
}

4. Implement our Aidl class


public class IProcessInfoImpl extends IProcessInfo.Stub {
 @Override
 public int getProcessId() throws RemoteException {
  return android.os.Process.myPid();
 }
}

5. Return in onbind() of service


public class MyRemoteService extends Service {
 IProcessInfoImpl mProcessInfo = new IProcessInfoImpl();
 @Nullable
 @Override
 public IBinder onBind(Intent intent) {
  Log.e("MyRemoteService", "MyRemoteService thread id = " + Thread.currentThread().getId());
  return mProcessInfo;
 }
}

6. Binding service

mTvRemoteBind.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      Intent intent = new Intent(MainActivity.this, MyRemoteService.class);
      bindService(intent, mRemoteServiceConnection, BIND_AUTO_CREATE);
    }
  });


mRemoteServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {

      Log.e("MainActivity", "MyRemoteService onServiceConnected");
  //Fetching data through Aidl
      IProcessInfo processInfo = IProcessInfo.Stub.asInterface(service);
      try {
        Log.e("MainActivity", "MyRemoteService process id = " + processInfo.getProcessId());
      } catch (RemoteException e) {
        e.printStackTrace();
      }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
      Log.e("MainActivity", "MyRemoteService onServiceDisconnected");
    }
  };

As long as the binding is successful, the log can be printed as the process ID of the process where myremoteservice is located. In this way, we complete the process of service communication with different processes.

2、 Code operation — call the service of other apps

Compared with calling services under different processes under the same app, calling services defined by other apps is slightly different

1. Due to the need for other apps to access, the implicit call used by bindservice () is not appropriate, and action needs to be defined when the service is defined
We define the following service in app a of the defined thread:

<service android:name=".service.ServerService">
 <intent-filter>
 //Action customization here
   <action android:name="com.jxx.server.service.bind" />
   <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</service>

2. We need to do this in app B that needs bindservice

  • First of all, copy the Aidl file defined in a to B, such as iprocessinfo. Aidl file defined above, including the path, to be copied intact.
  • Service is invoked in B through explicit call.
mTvServerBind.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
    Intent intent = new Intent();
    intent.setAction("com.jxx.server.service.bind");// Action of service
    intent.setPackage("com.jxx.server");// Package name of app a
    bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE);
  }
});

Transfer of custom object in Aidl

The main steps are as follows:

  1. To define a custom object, you need to implement the Parcelable interface
  2. New Aidl file of custom object
  3. Referencing custom objects in Aidl files that pass data
  4. Copy the custom object and Aidl file to the app that needs bindservice, and keep the main path intact

Let’s take a look at the specific code:

1. Define the custom object and implement the Parcelable interface

public class ServerInfo implements Parcelable {

public ServerInfo() {

}

String mPackageName;

public String getPackageName() {
  return mPackageName;
}

public void setPackageName(String packageName) {
  mPackageName = packageName;
}

protected ServerInfo(Parcel in) {
  mPackageName = in.readString();
}

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

  @Override
  public ServerInfo[] newArray(int size) {
    return new ServerInfo[size];
  }
};

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

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

//When using out or inout modification, you need to add this method yourself
public void readFromParcel(Parcel dest) {
  mPackageName = dest.readString();
}
}

2. New Aidl file of custom object

package com.jxx.server.aidl;
//Note that parseable is lowercase
parcelable ServerInfo;

3. Reference custom object

package com.jxx.server.aidl;
//Even under the same package, we need to guide the package here
import com.jxx.server.aidl.ServerInfo;
interface IServerServiceInfo {
 ServerInfo getServerInfo();
 void setServerInfo(inout ServerInfo serverinfo);
}

Note the set method here. Inout is used here. There are three modifiers
-In: client write, server modification will not be notified to client
-Out: the server modifies and synchronizes with the client, but the object obtained by the server may be empty
-Inout: all modifications are synchronized

When you use out and inout, you need to add read from parcel (parcel dest) manually in addition to implementing Parcelable

4. Copy the custom object and Aidl file to the app to be referenced.

5. Citation


mServerServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      IServerServiceInfo serverServiceInfo = IServerServiceInfo.Stub.asInterface(service);
      try {
        ServerInfo serviceInfo = serverServiceInfo.getServerInfo();
        Log.e("MainActivity", "ServerService packageName = " + serviceInfo.getPackageName());
      } catch (RemoteException e) {
        e.printStackTrace();
      }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
      Log.e("MainActivity", "ServerService onServiceDisconnected");
    }
  };

The objects referenced in list and map should also be custom objects that meet the above requirements, or several other data types.

Using messenger to realize IPC communication

The steps are as follows:

  1. Create a new messenger object on the server side to respond to the registration operation on the client side and pass it out in onbind()
  2. In the service connection of the client side, save the messenger object passed from the server side
  3. At the same time, the client side also creates a new messenger object, and registers the messenger delivered by the server side to the server side to maintain communication.
  4. No matter whether the unbindservice () operation is carried out or not, as long as the client keeps the messenger object of the server, it can still communicate with the server.

1、 Server code

public class MessengerService extends Service {

  static final int MSG_REGISTER_CLIENT = 1;
  static final int MSG_UNREGISTER_CLIENT = 2;
  static final int MSG_SET_VALUE = 3;

  //This is for the client to receive parameters
  static final int MSG_CLIENT_SET_VALUE = 4;

  static class ServiceHandler extends Handler {

    private final List<Messenger> mMessengerList = new ArrayList<>();

    @Override
    public void handleMessage(Message msg) {
      switch (msg.what) {
        case MSG_REGISTER_CLIENT:
          mMessengerList.add(msg.replyTo);
          break;
        case MSG_UNREGISTER_CLIENT:
          mMessengerList.remove(msg.replyTo);
          break;
        case MSG_SET_VALUE:
          int value = msg.arg1;
          for (Messenger messenger : mMessengerList) {
            try {
              messenger.send(Message.obtain(null, MSG_CLIENT_SET_VALUE, value, 0));
            } catch (RemoteException e) {
              e.printStackTrace();
            }
          }
          break;
        default:
          super.handleMessage(msg);
      }
    }
  }

  private Messenger mMessenger = new Messenger(new ServiceHandler());

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

2、 Client code

public class MessengerClientActivity extends AppCompatActivity {
 //These types should correspond to the server
  static final int MSG_REGISTER_CLIENT = 1;
  static final int MSG_UNREGISTER_CLIENT = 2;
  static final int MSG_SET_VALUE = 3;
  static final int MSG_CLIENT_SET_VALUE = 4;

  class ClientHandler extends Handler {

    @Override
    public void handleMessage(Message msg) {

      if (msg.what == MSG_CLIENT_SET_VALUE) {
        mTvValue.setText(msg.arg1 + "");
      } else {
        super.handleMessage(msg);
      }
    }
  }

  TextView mTvServerBind;
  TextView mTvServerUnbind;
  TextView mTvValue;
  TextView mTvSend;

  ServiceConnection mServerServiceConnection;
  Messenger mServerMessenger;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_messenger);

    mTvServerBind = findViewById(R.id.tv_server_bind);
    mTvServerUnbind = findViewById(R.id.tv_server_unbind);
    mTvValue = findViewById(R.id.tv_value);
    mTvSend = findViewById(R.id.tv_send);

    mTvServerBind.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        Intent intent = new Intent();
        intent.setAction("jxx.com.server.service.messenger");
        intent.setPackage("jxx.com.server");
        bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE);
      }
    });

    mTvServerUnbind.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        //Even if we unbindservice here, as long as we still keep the mserverminger object,
        //We can continue to communicate with the server
        unbindService(mServerServiceConnection);
      }
    });

    mTvSend.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        if (mServerMessenger != null) {
          try {
            //Test if you can set the data
            Message test = Message.obtain(null, MSG_SET_VALUE, new Random().nextInt(100), 0);
            mServerMessenger.send(test);
          } catch (RemoteException e) {
            e.printStackTrace();
          }
        }
      }
    });

    mServerServiceConnection = new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
        //Server messenger
        mServerMessenger = new Messenger(service);

        //Now start to construct the messenger used by the client to deliver and receive messages
        Messenger clientMessenger = new Messenger(new ClientHandler());

        try {
          //Register the client with the server
          Message register = Message.obtain(null, MSG_REGISTER_CLIENT);
          register.replyTo = clientMessenger;// This is a registration operation. We can see that the object is taken out in the server code above
          mServerMessenger.send(register);

          Toast. Maketext (messengerclientactivity. This, "binding succeeded", toast. Length_ SHORT).show();

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

      @Override
      public void onServiceDisconnected(ComponentName name) {

      }
    };
  }

The above is the whole content of this article, I hope to help you learn, and I hope you can support developer more.

Recommended Today

What is “hybrid cloud”?

In this paper, we define the concept of “hybrid cloud”, explain four different cloud deployment models of hybrid cloud, and deeply analyze the industrial trend of hybrid cloud through a series of data and charts. 01 introduction Hybrid cloud is a computing environment that integrates multiple platforms and data centers. Generally speaking, hybrid cloud is […]