赞
踩
昨天我们主管说准备把项目拆分一下,现在项目依赖了好几个负责串口通讯Library,准备把这些Library变成独立的APP,通过Android跨进程机制进行数据交互。然后让我写一个跨进程通信的Demo进行测试。
跨进程通信的方式有好几种,我这里用的是AIDL的方式。
首先实现同一应用内跨进程通信,然后在实现APP间通信。因为AIDL是c/s模式,所以我们先创建一个服务端应用。
服务端包名: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,允许其他应用调用。
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方法 }
① 、在项目的根目录下创建一个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文件
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");
上面我们已经完成了服务端的功能,并且实现activity和service的双向通信。现在只需要将activity的功能放到另一个应用内实现就行了。
包名:com.aidl.client
将服务端的AILD文件夹拷贝到客户端,并且包名和服务端一样,保持不变。服务端和客户端AIDL目录如下。
我们知道客户端的包名是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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。