Android使用Service实现IPC通信的2种方式
借助aidl实现ipc通信
一、代码实操---与远端进程的service绑定
上面的代码都是在当前进程内跟service通信,现在我们来实现一下,不同进程内service如何绑定。
aidl:android interface definition language,即android接口定义语言。
service跨进程传递数据需要借助aidl,主要步骤是这样的:
- 编写aidl文件,as自动生成的java类实现ipc通信的代理
- 继承自己的aidl类,实现里面的方法
- 在onbind()中返回我们的实现类,暴露给外界
- 需要跟service通信的对象通过bindservice与service绑定,并在serviceconnection接收数据。
我们通过代码来实现一下:
1、首先我们需要新建一个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、在manifest文件中声明我们的service同时指定运行的进程名,这里并是不只能写remote进程名,你想要进程名都可以
<service android:name=".service.myremoteservice" android:process=":remote" />
3、新建一个aidl文件用户进程间传递数据。
aidl支持的类型:八大基本数据类型、string类型、charsequence、list、map、自定义类型。list、map、自定义类型放到下文讲解。
里面会有一个默认的实现方法,删除即可,这里我们新建的文件如下:
package xxxx;//aidl所在的包名 //interface之前不能有修饰符 interface iprocessinfo { //你想要的通信用的方法都可以在这里添加 int getprocessid(); }
4、实现我们的aidl类
public class iprocessinfoimpl extends iprocessinfo.stub { @override public int getprocessid() throws remoteexception { return android.os.process.mypid(); } }
5、在service的onbind()中返回
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、绑定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"); // 通过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"); } };
只要绑定成功就能在有log打印成myremoteservice所在进程的进程id。这样我们就完成了跟不同进程的service通信的过程。
二、代码实操---调用其他app的service
跟调同app下不同进程下的service相比,调用其他的app定义的service有一些细微的差别
1、由于需要其他app访问,所以之前的bindservice()使用的隐式调用不在合适,需要在service定义时定义action
我们在定义的线程的app a 中定义如下service:
<service android:name=".service.serverservice"> <intent-filter> //这里的action自定义 <action android:name="com.jxx.server.service.bind" /> <category android:name="android.intent.category.default" /> </intent-filter> </service>
2、我们在需要bindservice的app b 中需要做这些处理
- 首先要将a中定义的aidl文件复制到b中,比如我们在上面定义的iprocessinfo.aidl这个文件,包括路径在内需要原封不动的复制过来。
- 在b中调用service通过显式调用
mtvserverbind.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { intent intent = new intent(); intent.setaction("com.jxx.server.service.bind");//service的action intent.setpackage("com.jxx.server");//app a的包名 bindservice(intent, mserverserviceconnection, bind_auto_create); } });
aidl中自定义对象的传递
主要步骤如下:
- 定义自定对象,需要实现parcelable接口
- 新建自定义对象的aidl文件
- 在传递数据的aidl文件中引用自定义对象
- 将自定义对象以及aidl文件拷贝到需要bindservice的app中,主要路径也要原封不动
我们来看一下具体的代码:
1、定义自定义对象,并实现parcelable接口
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); } //使用out或者inout修饰时需要自己添加这个方法 public void readfromparcel(parcel dest) { mpackagename = dest.readstring(); } }
2、新建自定义对象的aidl文件
package com.jxx.server.aidl; //注意parcelable 是小写的 parcelable serverinfo;
3、引用自定义对象
package com.jxx.server.aidl; //就算在同一包下,这里也要导包 import com.jxx.server.aidl.serverinfo; interface iserverserviceinfo { serverinfo getserverinfo(); void setserverinfo(inout serverinfo serverinfo); }
注意这里的set方法,这里用了inout,一共有3种修饰符
- in:客户端写入,服务端的修改不会通知到客户端
- out:服务端修改同步到客户端,但是服务端获取到的对象可能为空
- inout:修改都收同步的
当使用out和inout时,除了要实现parcelable外还要手动添加readfromparcel(parcel dest)
4、拷贝自定义对象以及aidl文件到在要引用的app中即可。
5、引用
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"); } };
list、map中引用的对象也应该是符合上面要求的自定义对象,或者其他的几种数据类型。
使用messenger实现ipc通信
步骤是这样的:
- 在server端新建一个messenger对象,用于响应client端的注册操作,并在onbind()中传递出去
- 在client端的serviceconnection中,将server端传递过来的messenger对象进行保存
- 同时client端也新建一个messenger对象,通过server传递过来的messenger注册到server端,保持通信用。
- 不管是否进行unbindservice()操作,只要client保有server端的messenger对象,仍然能和server端进行通信。
一、server端代码
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; //这个是给client端接收参数用的 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(); } }
二、client端代码
public class messengerclientactivity extends appcompatactivity { //这些类型要和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) { //就算这里我们unbindservice,只要我们还保留有mservermessenger对象, //我们就能继续与server通信 unbindservice(mserverserviceconnection); } }); mtvsend.setonclicklistener(new view.onclicklistener() { @override public void onclick(view v) { if (mservermessenger != null) { try { //测试一下能否设置数据 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) { //服务端的messenger mservermessenger = new messenger(service); //现在开始构client用来传递和接收消息的messenger messenger clientmessenger = new messenger(new clienthandler()); try { //将client注册到server端 message register = message.obtain(null, msg_register_client); register.replyto = clientmessenger;//这是注册的操作,我们可以在上面的server代码看到这个对象被取出 mservermessenger.send(register); toast.maketext(messengerclientactivity.this, "绑定成功", toast.length_short).show(); } catch (remoteexception e) { e.printstacktrace(); } } @override public void onservicedisconnected(componentname name) { } }; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
Android中实现异步任务机制的AsyncTask方式的使用讲解
-
Android实现音乐播放进度条传递信息的两种方式(在service和activity中)
-
Android使用Service实现IPC通信的2种方式
-
使用Service组件实现简单的音乐播放器功能 --Android基础
-
android开发AIDL跨进程通信:AIDL中RemoteCallbackList的使用及权限验证方式详解
-
Android service的两种使用方式bindService和startService使用方式
-
使用jquery中封装的$.Ajax()进行异步通信并使用json方式实现数据传输
-
使用Messenger实现Service的双向通信
-
Android实现音乐播放进度条传递信息的两种方式(在service和activity中)
-
Android中实现异步任务机制的AsyncTask方式的使用讲解