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…