您的位置:首页 > 移动开发 > Android开发

【朝花夕拾】一篇文章搞懂Android跨进程通信

2019-01-11 17:33 921 查看

前言

       只要是面试中高级工程师岗位,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,所以详略上是前半部分略,后半部分详,完全是按照笔者对知识点掌握程度来行文的。读者在阅读中,如果有些部分因为写得太简略而看得不过瘾,只能自己去查更详细的资料了;如果有些部分因为写得太详细而嫌啰嗦,完全可以跳着看;如果有些知识点因为笔者经验和水平问题写得有误或者阐述欠妥,请不吝赐教,万分感激!!!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: