Android探索:六种IPC方式(上)——Bundle、文件共享、Messenger
-
- Android探索:六种IPC方式(上)——Bundle、文件共享、Messenger
- 引言
- 1、Bundle
- 2、文件共享
- 3、Messenger
- 两个APP如何通讯
引言
我们了解完Android IPC基础,接下来学习Android中的六种IPC方式:Bundle、文件共享、AIDL、Messenger、ContentProvider、Socket。
注意:本文代码均为kotlin语言
1、Bundle
Android四大组件中的Activity、Service、Receiver都是支持在Intent中传递Bundle数据的,例如在开发中,Activity界面跳转我们经常会使用intent.putExtra(name,value) 来设置数据,从源码来看其实是把值赋给了一个Bundle对象。Bundle像是一个Map集合,但它只能存储序列化的对象,比如基本数据类型、实现了Parcel able或Serializable接口的对象等。组件之间通过这种通讯方式最为简单。
2、文件共享
文件共享就是两个进程间通过读/写同一个文件来交换数据。注意这两个进程 操作的对象不会是同一个,只是内容一样。使用这种方式有个缺点,如果进程的读写并发操作的话,可能会导致数据不同步甚至丢失数据。我们知道SharedPreferences是Android提供的一种轻量级存储方案,也是数据文件操作的一种,不建议具有并发性的进程使用此类方式。
3、Messenger
在Message对象中放入要传递的对象,通过Messenger(信使)就可以在进程间传递Message对象了。其底层实现就是AIDL,由于它一次处理一个请求,因此在服务端不存在并发执行的情形。使用方法也比较简单,下面来看一个简单的例子:客户端把消息传递给服务端。
/*** 服务端*/
class MessengerService : Service() {/*** 2、将Handler与Messenger绑定*/private val mMessenger = Messenger(MessengerHandler())override fun onBind(intent: Intent?): IBinder {// 3、返回Messenger对象中的Binderreturn mMessenger.binder}/*** 1、定义一个Handler内部类用来处理收到的信息*/inner class MessengerHandler : Handler(){override fun handleMessage(msg: Message?) {if (msg!!.what == 0x123){Log.e("--------","来自客户端:${msg.data["msg"]}")}super.handleMessage(msg)}}
}
注册service
<service android:name=".messenger.MessengerService"android:process=":remote"/>
/*** 客户端*/
class MessengerActivity : Activity(){/*** 1、定义ServiceConnection 链接回掉*/private val mConnection = object : ServiceConnection{override fun onServiceDisconnected(name: ComponentName?) {Log.e("------","服务已断开")}override fun onServiceConnected(name: ComponentName?, service: IBinder?) {// 2、将Binder 封装给Messenger对象val service = Messenger(service)val msg = Message.obtain(null,0x123)val data = Bundle()data.putString("msg","你好,我是客户端")msg.data = datatry {// 3、向服务端发送信息service.send(msg)}catch (e: RemoteException){e.printStackTrace()}}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 4、绑定服务val intent = Intent(this,MessengerService::class.java)bindService(intent,mConnection, Context.BIND_AUTO_CREATE)}override fun onDestroy() {unbindService(mConnection)super.onDestroy()}
}
上面的例子演示了服务端如何接受客户端的消息,但是有时候我们还需要服务端向客户端回复结果,如何实现呢,下面我们接着修改上面的例子。先看看 服务端的修改:
override fun handleMessage(msg: Message?) {if (msg!!.what == 0x123){
// 0x123是消息标识,代表来自客户端的消息,可随意指定Log.e("--------","来自客户端:${msg.data["msg"]}")// 收到消息后,立即回应客户端val client = msg.replyToval replyMsg = Message.obtain(null,0x124)// 0x124是消息标识,代表来自服务端的消息,可随意指定val bundle = Bundle()bundle.putString("reply","哈哈,我收到你的消息了")replyMsg.data = bundletry {client.send(replyMsg)// 向客户端发送消息} catch (e: RemoteException) {e.printStackTrace()}return}super.handleMessage(msg)}
然后,客户端准备一个接收消息的Messenger和Handler,并将Messenger发送给服务端
private val mGetServiceMessenger = Messenger(MessengerHandler())...省略部分代码
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {// 2、将Binder 封装给Messenger对象val service = Messenger(service)val msg = Message.obtain(null,0x123)val data = Bundle()data.putString("msg","你好,我是客户端")msg.data = data// 注意这一句,为了可以接收到服务端的回应,需要把Messenger对象传递给服务端msg.replyTo = mGetServiceMessengertry {// 3、向服务端发送信息service.send(msg)}catch (e: RemoteException){e.printStackTrace()}}
...inner class MessengerHandler : Handler(){override fun handleMessage(msg: Message?) {if (msg!!.what == 0x124){// 0x124代表来自服务端Log.e("------","来自服务端:${msg.data["reply"]}")return}super.handleMessage(msg)}}
运行程序,可以看到Log
05-12 02:32:44.145 4357-4357/com.sange.ipc:remote E/--------: 来自客户端:你好,我是客户端
05-12 02:32:44.172 4320-4320/com.sange.ipc E/------: 来自服务端:哈哈,我收到你的消息了
Messenger的使用比较简单,下图是上述例子的工作原理
两个APP如何通讯
上述例子同一APP中的进程间的通讯,其实 和两个APP通讯原理是一样的,这一点要明白,下面讲下两个APP如何通讯
新建一个APP module,名字叫Service。
将上个例子中的MessengerService文件拷贝进来。
注册 AndroidManifest.xml
<service android:name="com.sange.service.MessengerService"android:enabled="true"android:exported="true"><!--android:exported属性表示是否可被外部的App绑定,true为允许,false为不允许--><intent-filter><action android:name="com.sange.service.MessengerService"/></intent-filter></service>
在客户端APP 绑定service
// 4、绑定服务val intent = Intent()// 注意一定要写包名,然后是action名字intent.setClassName("com.sange.service","com.sange.service.MessengerService")bindService(intent,mConnection, Context.BIND_AUTO_CREATE)
不写包名,会报以下类似错误
05-12 03:18:24.358 6752-6752/com.sange.ipc E/AndroidRuntime: FATAL EXCEPTION: mainProcess: com.sange.ipc, PID: 6752java.lang.RuntimeException: Unable to start activity ComponentInfo{
com.sange.ipc/com.sange.ipc.messenger.MessengerActivity}: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=com.sange.service.MessengerService }at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)at android.app.ActivityThread.-wrap11(Unknown Source:0)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)at android.os.Handler.dispatchMessage(Handler.java:106)at android.os.Looper.loop(Looper.java:164)at android.app.ActivityThread.main(ActivityThread.java:6494)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
由于篇幅较长,另外三种 下篇分析。