【朝花夕拾】一篇文章搞懂Android跨进程通信
前言
只要是面试中高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点。Android系统的运行由大量相互独立的进程相互协助来完成的,所以Android进程间通信问题,是做好Android开发高级工程师必须要跨过的一道坎。如果您还对这方面的知识还做不到如数家珍,那就和我一起来攻克它吧!
本文主要包含了如下内容:
View Code 为了下文分析流程及生命周期,在其中各个方法中都添加了Log。
同时,在Server端的AndroidManifest.xml文件中添加该Service的注册信息。
1 <service 2 android:name=".AidlService" 3 android:exported="true"> 4 <intent-filter> 5 <action android:name="com.songwei.aidl" /> 6 </intent-filter> 7 </service>
这里有几点需要注意:
(1)exported属性值,如果有“intent-filter”,则默认值为true,否则为false。所以这里其实可以去掉,因为有“intent-filter”,其默认值就是true。
(2)由于笔者在后面启动该service的时候用的action的方式,所以这里就有了“intent-filter”里面的action。如果用其他方式启动,这个service的注册信息就需要相应的改动了,有一定开发经验的读者应该都知道,就不展开讲了,主要是怕读者容易忽略这里,所以特别提醒一下。
4、编译Sever端和Client端App,生成IDemoService.java文件。
当编译的时候,AS会自动为我们生成IDemoService.java文件,如图6.1和图6.2中④处所示。当你打开该文件的时候,是不是看到了如下场景?
View Code 惊不惊喜?意不意外?这下是不是有种似曾相识的赶脚?这就是一个很普通的java中的接口文件而已,结构也非常简单。
View Code 代码中对一些关键和容易忽略的地方做了注释,可以结合起来进行理解。
6、运行
运行的时候,需要先启动Service端进程,才能在Client端中点击“绑定”的时候绑定成功。完成一次“绑定”和“解绑”,得到的log如下所示:
1 01-08 15:29:43.109 13532-13532/com.songwei.aidldemoserver I/aidlDemo: server:[onCreate] 2 01-08 15:29:43.110 13532-13532/com.songwei.aidldemoserver I/aidlDemo: server:[onBind] 3 01-08 15:29:43.113 13299-13299/com.songwei.aidldemoclient I/aidlDemo: client:[onServiceConnected]componentName=ComponentInfo{com.songwei.aidldemoserver/com.songwei.aidldemoserver.AidlService} 4 01-08 15:29:43.114 13532-13547/com.songwei.aidldemoserver I/aidlDemo: server:[setName] 5 01-08 15:29:43.114 13532-13546/com.songwei.aidldemoserver I/aidlDemo: server:[getName] 6 01-08 15:29:43.114 13299-13299/com.songwei.aidldemoclient I/aidlDemo: client:[onServiceConnected]myName=Andy Song 7 01-08 15:36:07.570 13299-13299/com.songwei.aidldemoclient I/aidlDemo: client:[onServiceDisconnected]
可以结合前面的ClientActivity.java和AidlService.java代码中的添加的log,来理解一下这个流程。当然,最好是能够按照上面的步骤,亲自动手实现一遍,比看10遍更有效果。
本节对介绍了AIDL最基本的知识,出于项目性质的原因,可能有些Android开发人员工作了好几年都不一定需要完整写一个AIDL实现两个App通信的功能。笔者就是这样,在前几年的工作中,虽然很早就知道AIDL这个东西,但确实是项目中就没有写过这个功能,直到最近两年。所以即便是读者您有不少开发经验了,也可能和笔者一样是个AIDL的初级开发者,这也是我花这么长的篇幅写这个基础内容的原因。
另外,有需要源码可以下载这个demo,网盘地址:https://pan.baidu.com/s/1CyE_8-T9TDQLVQ1TDAEX2A 提取码:4auk 。
七、AIDL的深入使用和理解
前面一节讲了AIDL最进本的知识,这一节中将会结合更复杂的场景,更深入地介绍AIDL。(这一节的内容会持续补充完整)
1、Client端是如何实现调用Server端方法的
2、AIDL支持的数据类型
3、AIDL数据类序列化问题
4、AIDL回调的使用
当Server端某个操作执行完后,需要通知Client端自己完成了任务,这个时候回调就带来了很大的便利,和在同一个App中使用回调效果一样。例如在上一节的例子中,Server完成了setName()这个操作(耗时的异步操作更能体现回调的作用)后,要通知Client端自己完成了任务,可以进行下一步的操作了,就是这样一个场景。现在在前面AIDL例子基础上,对回调的使用步骤进行说明。
(1)在Server端IDemoService.aidl同一目录中添加一个新的.aidl接口文件,我这里命名为IDemoCallback.aidl,内容如下:
1 // IDemoCallback.aidl 2 package com.songwei.aidldemoserver; 3 4 // Declare any non-default types here with import statements 5 6 interface IDemoCallback { 7 void testCallback(String msg); 8 }
(2)在IDemoService.aidl中添加注册/反注册两个方法
1 // IDemoService.aidl 2 package com.songwei.aidldemoserver; 3 4 // Declare any non-default types here with import statements 5 import com.songwei.aidldemoserver.IDemoCallback; 6 7 interface IDemoService { 8 9 void setName(String name); 10 11 String getName(); 12 13 void registerCallback(IDemoCallback cb); 14 15 void unregisterCallback(IDemoCallback cb); 16 }
第13行和第15行为新增的方法。将这两个.aidl文件同步到Client端,使C/S两端的aidl文件完全一样,均为
最好将两个app都编译一遍,这样后面有些地方可以用代码补全,而不用手动书写。当然在AidlService.java中实现接口的时候肯定会报错的,把新增的方法补上就可以了。
(3)在AidlService.java中添加如下加粗部分的代码,
1 private RemoteCallbackList<IDemoCallback> mCallbacks = new RemoteCallbackList<>(); 2 3 private void callback(String msg) { 4 int N = mCallbacks.beginBroadcast(); 5 for (int i = 0; i < N; i++) { 6 try { 7 mCallbacks.getBroadcastItem(i).testCallback(msg); 8 } catch (RemoteException e) { 9 e.printStackTrace(); 10 } 11 } 12 mCallbacks.finishBroadcast(); 13 } 14 15 class MyBinder extends IDemoService.Stub { 16 private String mName = ""; 17 18 public void setName(String name) throws RemoteException { 19 Log.i(TAG, "server:[setName]"); 20 mName = name; 21 callback("'Andy song' is setted"); 22 } 23 24 @Override 25 public String getName() throws RemoteException { 26 Log.i(TAG, "server:[getName]"); 27 return mName; 28 } 29 30 @Override 31 public void registerCallback(IDemoCallback cb) throws RemoteException { 32 Log.i(TAG,"server:[registerCallback]"); 33 if(cb != null){ 34 mCallbacks.register(cb); 35 } 36 } 37 38 @Override 39 public void unregisterCallback(IDemoCallback cb) throws RemoteException { 40 Log.i(TAG,"server:[unregisterCallback]"); 41 if(cb != null){ 42 mCallbacks.unregister(cb); 43 } 44 } 45 46 }
RemoteCallbackList是系统提供的一个用于存储回调对象的列表,其对象mCallbacks用于存储注册的IDemoCallback对象。通过第3行的callback()方法中的内容,我们可以推测它是采用一种类似于Broadcast的方式来实现回调的。
当setName()方法执行完毕后,callback("'Andy song' is setted");就会把回调信息反馈给Client中注册该回调的地方了。
(4)在ClientActivity.java中注册回调并处理相关逻辑
1 ...... 2 public void onServiceConnected(ComponentName componentName, IBinder binder) { 3 //Log.i(TAG, "client:[onServiceConnected]componentName=" + componentName); 4 mIsBinded = true; 5 //得到一个远程Service中的Binder代理,而不是该Binder实例 6 mDemoService = IDemoService.Stub.asInterface(binder); 7 try { 8 mDemoService.registerCallback(mDemoCallback); 9 //远程控制设置name值 10 mDemoService.setName("Andy Song"); 11 //远程获取设置的name值 12 String myName = mDemoService.getName(); 13 Log.i(TAG, "client:[onServiceConnected]myName=" + myName); 14 15 } catch (RemoteException e) { 16 e.printStackTrace(); 17 } 18 ...... 19 private IDemoCallback mDemoCallback = new IDemoCallback.Stub() { 20 @Override 21 public void testCallback(String msg) throws RemoteException { 22 Log.i(TAG, "client:[testCallback] msg=" + msg); 23 } 24 }; 25 ...... 26 private void unRegisterCallback() { 27 try { 28 mDemoService.unregisterCallback(mDemoCallback); 29 } catch (RemoteException e) { 30 e.printStackTrace(); 31 } 32 } 33 ......
在解绑定的地方调用unRegisterCallback()反注册回调即可,这样就完成了代码整个代码的编写。这里需要注意注册回调的时机,一定要在setName()执行前注册,否则Client端收不到回调信息。
(5)运行C/S端,然后“绑定”/“解绑”,就会看到如下log信息:
1 01-11 14:11:44.714 5903-5903/com.songwei.aidldemoserver I/aidlDemo: server:[onCreate] 2 01-11 14:11:44.715 5903-5903/com.songwei.aidldemoserver I/aidlDemo: server:[onBind] 3 01-11 14:11:44.720 5903-5916/com.songwei.aidldemoserver I/aidlDemo: server:[registerCallback] 4 01-11 14:11:44.720 5903-5916/com.songwei.aidldemoserver I/aidlDemo: server:[setName] 5 01-11 14:11:44.721 6572-6572/com.songwei.aidldemoclient I/aidlDemo: client:[testCallback] msg='Andy song' is setted 6 01-11 14:11:44.722 5903-5916/com.songwei.aidldemoserver I/aidlDemo: server:[getName] 7 01-11 14:11:44.722 6572-6572/com.songwei.aidldemoclient I/aidlDemo: client:[onServiceConnected]myName=Andy Song 8 01-11 14:11:58.589 5903-5916/com.songwei.aidldemoserver I/aidlDemo: server:[unregisterCallback] 9 01-11 14:11:58.616 5903-5903/com.songwei.aidldemoserver I/aidlDemo: server:[onUnbind] 10 01-11 14:11:58.617 5903-5903/com.songwei.aidldemoserver I/aidlDemo: server:[onDestroy]
第5行就是回调信息,表示回调成功。
源码地址链接: https://pan.baidu.com/s/1eI8chrxYTGqSaIaeMZEOYg 提取码: 84cd
结语
本文主要是笔者用来整理跨进程通信的知识点,以及复习近两年才真正会用的AIDL,所以详略上是前半部分略,后半部分详,完全是按照笔者对知识点掌握程度来行文的。读者在阅读中,如果有些部分因为写得太简略而看得不过瘾,只能自己去查更详细的资料了;如果有些部分因为写得太详细而嫌啰嗦,完全可以跳着看;如果有些知识点因为笔者经验和水平问题写得有误或者阐述欠妥,请不吝赐教,万分感激!!!
- 一篇文章读懂Android组件之间数据传递方法 IPC进程通信方法AIDL介绍
- 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制
- 关于Android 跨进程通信的文章?
- 【朝花夕拾】Android性能篇之(七)Android跨进程通信总结
- 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制【转】
- 【朝花夕拾】性能优化篇之(七)Android跨进程通信--草稿
- Android开发学习之Service详解三,Android跨进程通信
- Android之进程通信机制(上)(Serializable,Parcelable,Binder)
- Android:学习AIDL,这一篇文章就够了(上)
- android 进程与线程 进程与进程 线程与线程通信问题
- Android进程通信之Messenger&AIDL使用详解
- Android IPC进程通信——Messager方式
- Android中跨进程通信的几种方式
- Android:学习AIDL,这一篇文章就够了(下)
- 一篇胎死腹中的Android文章——Dex文件结构解析
- android Messenger 跨进程通信
- Android:学习AIDL,这一篇文章就够了(下)
- 一篇文章理解所有android关于存储的方法
- Android service: Messenger 进程通信
- Android进程通信之一:两种序列化方式