昨天我们主管说准备把项目拆分一下,现在项目依赖了好几个负责串口通讯Library,准备把这些Library变成独立的APP,通过Android跨进程机制进行数据交互。然后让我写一个跨进程通信的Demo进行测试。
跨进程通信的方式有好几种,我这里用的是AIDL的方式。
一、同一个APP内Service和Activity通信
首先实现同一应用内跨进程通信,然后在实现APP间通信。因为AIDL是c/s模式,所以我们先创建一个服务端应用。
1、创建一个服务端的APP,然后在创建一个Service服务。
服务端包名:com.aidl.service
自动生成MyService类和manifest注册文件。
<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>
当然你也可以手动创建。这里的enabled和exported属性要设置为true,允许其他应用调用。
2、创建传送消息的消息对象
AIDL是不支持传递普通的Java对象的,不过支持Parcelable对象,所以我们的消息对象要实现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 new Msg[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(msg);
}
//set,get方法
}
3、创建AIDL文件
① 、在项目的根目录下创建一个Msg的AIDL文件。包名和项目包名一致,并且Msg.aidl声明为parcelable类型,Msg.aidl路径和Msg.java路径一致。
②、创建一个收到消息的监听接口IReceiveMsgListener.aidl
package com.aidl.service;
import com.aidl.service.Msg;
interface IReceiveMsgListener {
void onReceive(in Msg msg);
}
导入Msg.aidl的完整路径import com.aidl.service.Msg。 onReceive()中Msg使用 in 输入标记。
③、创建消息管理的接口IMsgManager.aidl
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);
}
IMsgManager.aidl中提供了发送消息的方法、注册和解除消息监听的方法。同样要导入Msg.aidl 和IReceiveMsgListener.aidl的完整路径。
到这里AIDL文件编写完成。最后需要Make Project,编译器生成对应的Binder文件
4、编写MyService服务代码
public class MyService extends Service {
//AIDL不支持正常的接口回调,使用RemoteCallbackList实现接口回调
private RemoteCallbackList<IReceiveMsgListener> mReceiveListener = new RemoteCallbackList<IReceiveMsgListener>();
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
class MyBinder extends IMsgManager.Stub {
//发送消息
public void sendMsg(Msg msg) {
receiveMsg(msg);
}
//注册
@Override
public void registerReceiveListener(IReceiveMsgListener receiveListener) throws RemoteException {
mReceiveListener.register(receiveListener);
}
//解除注册
@Override
public void unregisterReceiveListener(IReceiveMsgListener receiveListener) throws RemoteException {
boolean success = mReceiveListener.unregister(receiveListener);
if (success){
Log.d("tag","=== 解除注册成功");
}else {
Log.d("tag","=== 解除注册失败 ");
}
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return super.onTransact(code, data, reply, flags);
}
}
//收到消息处理
public void receiveMsg(Msg msg) {
//通知Callback循环开始,返回N为实现mReceiveListener回调的个数
final int N = mReceiveListener.beginBroadcast();
msg.setMsg("我是服务器,我收到了:"+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();
}
}
}
//通知通知Callback循环结束
mReceiveListener.finishBroadcast();
}
}
Service中通过Binder机制实现注册,解除注册和发送的方法。
Activity代码:
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) {
}
};
//注意Activity和Service是同一进程才能使用Intent通信
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, mConnection, BIND_AUTO_CREATE);//开启服务
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (TextUtils.isEmpty(mEditText.getText().toString())) {
Toast.makeText(MainActivity.this, "消息为空", Toast.LENGTH_SHORT).show();
return;
}
binder.sendMsg(new Msg(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() {
//当承载IBinder的进程消失时接收回调的接口
@Override
public void binderDied() {
if (null == mIMsgManager) {
return;
}
mIMsgManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mIMsgManager = null;
//断线重来逻辑
}
};
@Override
protected void onDestroy() {
//解除注册
if (null != mIMsgManager && mIMsgManager.asBinder().isBinderAlive()) {
try {
mIMsgManager.unregisterReceiveListener(mReceiveMsgListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
//解除绑定服务
unbindService(mConnection);
super.onDestroy();
}
}
运行截图如下:
如果想要实现同一应用内跨进程通信需要修改Service的进程:
android:process=":remote"
需要使用action进行启动:
Intent intent = new Intent();
intent.setAction("com.aidl.service.MyService");
二、两个或多个APP之间通信
上面我们已经完成了服务端的功能,并且实现activity和service的双向通信。现在只需要将activity的功能放到另一个应用内实现就行了。
1、创建客户端应用。
包名:com.aidl.client
2、将服务端的AIDL文件拷贝到客户端
将服务端的AILD文件夹拷贝到客户端,并且包名和服务端一样,保持不变。服务端和客户端AIDL目录如下。
3、拷贝Msg.Java对象
我们知道客户端的包名是com.aidl.client,而Msg.aidl路径是com.aidl.service,所以我们要在com.aidl.service目录下创建Msg.java。
编写Activity代码:
public class MainActivity extends AppCompatActivity {
private IMsgManager myBinder;//定义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 {
//链接到死亡代理,当IBinder死亡时收到回调
mIMsgManager.asBinder().linkToDeath(mDeathRecipient, 0);
//注册消息监听
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();
//跨进程通信需要使用action启动
intent.setAction("com.aidl.service.MyService");
//android5.0之后,如果servicer不在同一个App的包中,需要设置service所在程序的包名
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, "消息不能为空", Toast.LENGTH_SHORT).show();
return;
}
mMsg.setMsg(msg);
//通过binder将消息传递到service
myBinder.sendMsg(mMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
//消息回调监听
private IReceiveMsgListener mReceiveMsgListener = new IReceiveMsgListener.Stub() {
//收到服务端消息
@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() {
/**
* 当承载IBinder的进程消失时接收回调的接口
*/
@Override
public void binderDied() {
if (null == mIMsgManager) {
return;
}
mIMsgManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mIMsgManager = null;
//在这里重新绑定远程服务
}
};
@Override
protected void onDestroy() {
//解绑
super.onDestroy();
}
}
客户端运行截图:
服务端截图
因为我们服务端Activity也实现了IReceiveMsgListener 的接口,所以服务端Activity也能收到回调截图如下:
参考:部分参考Android开发艺术探索。
完整项目已经上传到GitHub上去了:https://github.com/Zhengyi66/AIDL