上篇文章以线程间的通信方式Handler类结尾,服务Service还支持的进程间通信,又是具体怎么实现的呢?这就要用到加载服务一文中提到的AIDL语言规范了。

AIDL是 Android Interface Definition Language 的缩写,即Android接口定义语言,使用其定义的规范编程,可实现Android系统上不同进程间的通信。官网ADIL概述中以服务端和客户端通信为例做了大致讲述。与线程间的通信类似,不同进程间的通信也是分为通信消息内容、消息发送方、消息接收方三个部分的,其中的消息内容也就是AIDL支持的数据类型;而由于服务已经位于当前应用程序所在的默认进程,所以服务中所实现的接口内容即对接收消息的处理,也就是消息接收方;相对的,其他需要发送消息的进程都可以看做是客户端进程,只要绑定服务之后都可以调用服务的接口方法,也就是消息发送方。

确定通信的消息内容

在AndroidStudio中,通信的消息内容以 .aidl 后缀格式的文件默认保存在开发目录 /src/main/aidl 下。在 .adil 格式的文件中,编码规则与Java语言一致,其中内容与Java语言中的接口定义类似。

其中支持通信的消息类型分为两类,一类是基本类型,使用基本类型时不需要在文件开头增加导入包名的声明,这些基本类型包括Java语言中的八种基本数据类型, String, CharSequence, List, Map;还有一类是附加类型,除了第一类基本类型,其他在 .java 格式的文件中定义的任何实现android.os.Parcelable接口的类都可以作为附加类型消息传递。附加类型在使用时需要在 .aidl 文件开头增加导入该类型所在包名的声明。

关于附加类型的定义,以com.java.process.myinterface.Person类型为例。

不仅在 /src/main/java/com/java/process/myinterface 目录下创建 Person.java ,在其中定义Person类并实现Parcelable接口的相关方法。

还要在 /src/main/aidl/com/java/process/myinterface 目录下创建对应的 Person.aidl 文件,在其中除了指定包名外,还要声明该类为parcelable接口,示例代码如下

package com.java.process.myinterface;
parcelable Person;

.aidl.java 格式的文件中定义的接口相比有两处不同,其一是无需访问修饰符,其二是对基本数据类型增加数据流向标识符(包括数据流入接收方的in,数据在接收方修改后可流出的out, 数据可流向接收方并流出的inout )。

关于通信消息的定义,以com.java.process.myinterface.DataInterface类型为例。

只需要在 /src/main/aidl/com/java/process/myinterface 目录下创建 DataInterface.aidl 文件,在其中定义相关接口即可。示例代码如下

package com.java.process.myinterface;
import com.java.process.myinterface.Person;
interface DataInterface{
void setVersion(int version);
int getVersion();
void updatePerson(in Person person);
Person getMainPerson();
}

在通信消息定义之后,可以使用AndroidStudio的编译指令 Build - Make Project 编译当前项目,以使得AndroidStudio自动生成消息定义的 .java 文件。上文 DataInterface.aidl 文件在项目编译之后,会在 /build/generated/aidl_source_output_dir/debug/out/com/java/process/myinterface 目录下生成不可编辑的 DataInterface.java 文件,之后即可在项目中通过导包import com.java.process.myinterface.DataInterface;的形式正常使用该接口类了。

通信接收方的服务接收处理

通信接收方就是常说的服务端,通常是自定义的服务Service类,主要负责实现上述通信消息内容的接口,以此接收消息内容并处理。

针对自定义的服务Service类,这里就用到在加载服务文章中提到的绑定服务的生命周期了。重写onBind(Intent intent)方法,在该方法中返回android.os.IBinder类型的对象实例。

而这个IBinder对象是怎么创建的呢?在上文通信消息定义之后自动生成的接口类中,也自动生成了其Stub内部类,在创建该内部类的无参构造方法时,即可自动实现其相关接口方法,继续使用上文示例,这里在服务Service中的接口实现代码如下

package com.java.process.myinterface;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.support.annotation.Nullable; import com.java.process.myinterface.DataInterface; public class ProcessService extends Service { private String versionName;
private Person person;
private final DataInterface.Stub binder=new DataInterface.Stub() { @Override
public String getVersionName() throws RemoteException {
//这里可以返回消息versionName给客户端
return versionName;
} @Override
public void setVersionName(String versionName) throws RemoteException {
//这里接收处理来自客户端的消息versionName
ProcessService.this.versionName=versionName;
} @Override
public void updatePerson(Person person) throws RemoteException {
//这里接收处理来自客户端的消息person
ProcessService.this.person.setName(person.getName());
ProcessService.this.person.setAge(person.getAge());
} @Override
public Person getMainPerson() throws RemoteException {
//这里可以返回消息person给客户端
return ProcessService.this.person;
}
}; @Override
public void onCreate() {
super.onCreate();
versionName="defaultName";
person=new Person();
person.setName("initName");
person.setAge(10);
} @Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}

通信发送方的服务绑定

通信发送方就是所谓的客户端了,发送方是与上文中的接收方不在同一个进程的,所以发送方通常需要先绑定接收方的自定义服务Service,也就是在发送方通过上下文环境Context对象调用bindService(Intent service, ServiceConnection conn, int flags)方法,这里的参数在加载服务中已有详细说明。

conn 参数的onServiceConnected(ComponentName name, IBinder service)方法中通过调用静态方法(通信消息定义接口的内部类Stub.asInterface(IBinder service)方法)得到通信消息的定义接口的实例化对象。在当前客户端绑定自定义服务Service成功之后,回调该方法,即可将通信消息的定义接口的实例化对象赋值给全局变量使用。

conn 参数的onServiceDisconnected(ComponentName name)方法中,要记得将全局通信消息定义接口变量的实例化对象置为空,否则在当前客户端与自定义服务Service断开连接后,还调用全局变量的通信消息定义接口的实例化对象的相关方法,可能会出现OOM内存泄露的问题。

最后在需要发送消息的位置,只需要调用全局通信消息定义接口变量的相关方法即可。其中用到的示例代码如下

    private DataInterface dataInterface;
private ServiceConnection conn=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
dataInterface = DataInterface.Stub.asInterface(service);
} @Override
public void onServiceDisconnected(ComponentName name) {
dataInterface = null;
} //此处省略对文本编辑控件editText的定义
//该方法在点击editText控件时回调
public void clickName(View view) {
try {
String setName = editText.getText().toString();
dataInterface.setVersionName(setName);
} catch (RemoteException e) {
e.printStackTrace();
}
}
};

Android系统编程入门系列之服务Service中的进程间通信的更多相关文章

  1. Android系统编程入门系列之服务Service齐头并进多线程任务

    在上篇文章中初步了解了Android系统的四大组件之一的服务Service,在服务内可以执行无用户交互的耗时操作任务,但是包括之前关于界面系列文章在内,生命周期方法都是在主线程内被系统回调的.如果直接 ...

  2. Android系统编程入门系列之广播接收者BroadcastReceiver实现进程间通信

    在前边几篇关于Android系统两个重要组件的介绍中,界面Activity负责应用程序与用户的交互,服务Service负责应用程序内部线程间的交互或两个应用程序进程之间的数据交互.看上去这两大组件就能 ...

  3. Android系统编程入门系列之加载界面Activity

    上回说到应用初始化加载及其生命周期,在Android系统调用Applicaiton.onCreate()之后,继续创建并加载清单文件中注册的首个界面即主Activity,也可称之为入口界面.主Acti ...

  4. Android系统编程入门系列之加载服务Service

    之前几篇文章简单梳理了在Android系统的四大组件之一,最主要的界面Activity中,使应用程序与用户进行交互响应的相关知识点,那对于应用程序中不需要与用户交互的逻辑,又要用到哪些内容呢?本文开始 ...

  5. Android系统编程入门系列之硬件交互——通信硬件Bluetooth

    通信硬件NFC的文章,虽然可以在Android系统中通过非直接接触的形式与支持NFC硬件的设备通信,但是也只能交互一些简短的标签内容,对大量的持续性数据,却并不能很好的支持.因此针对这个弊端,可以考虑 ...

  6. Android系统编程入门系列之应用环境及开发环境介绍

        作为移动端操作系统,目前最新的Android 11.0已经发展的比较完善了,现在也到了系统的整理一番的时间,接下来的系列文章将以Android开发者为中心,争取用归纳总结的态度对初级入门者所应 ...

  7. Android系统编程入门系列之应用间数据共享ContentProvider

    内容提供者ContentProvider与前文的界面Activity.服务Service.广播接收者BroadcastReveiver,并列称为Android的四大组件,均是需要自定义子类继承上述组件 ...

  8. Android系统编程入门系列之清单文件

    在上一篇文章中已经提到,Android系统加载应用程序之后,首先会读取该应用程序的AndroidManifest.xml清单文件,之后根据该清单文件加载后边的东西.所以要开发应用程序,自然要先知道清单 ...

  9. Android系统编程入门系列之应用内键值对数据的简单保存

    在应用程序间及与用户的通信交互过程中,会产生并传递一系列数据.针对这些数据,有部分是只在应用程序中使用的缓存数据,还有一部分是在不同位置多次或长时间使用的持久化数据. 对于缓存数据来说,通常以代码中定 ...

随机推荐

  1. mysql binlog恢复数据实战

    在前面,我们了解了mysql binlog日志的作用以及使用方法:  http://www.php20.cn/article/237 在后面讲到了,可以通过binlog进行恢复数据,那么,具体步骤是怎 ...

  2. bugKu变量1

    重点:两个$$,这暗示着第二个$变量的值,作为第一个$的名称. 而且代码审计之后,发现只含有字母. 又暗示在变量中,那么猜想是在全局变量中,而php中全局变量是$GLOBALS.

  3. 分库分表框架ShardingSphere入门学习1

    背景 传统的将数据集中存储至单一数据节点的解决方案,在性能.可用性和运维成本这三方面已经难于满足互联网的海量数据场景. 从性能方面来说,由于关系型数据库大多采用 B+ 树类型的索引,在数据量超过阈值的 ...

  4. IT面试最全逻辑题,收藏后成功率提高10%

    这是小学二年级的数学题: 猫妈妈钓到一些鱼,平均分给了7只小猫,每只小猫分到的鱼和剩下的鱼刚好一样多.猫妈妈最多钓到了多少条鱼? 这个是出来工作后的现场面试题: [1]假设有一个池塘,里面有无穷多的水 ...

  5. hadoop源码_hdfs启动流程_2_DataNode

    执行start-dfs.sh脚本后,集群是如何启动的? 本文阅读并注释了start-dfs脚本,以及datanode的启动主要流程流程源码. DataNode 启动流程 脚本代码分析 start-df ...

  6. 第一个用户进程 - Android 的 Init 进程

    本文尝试对着 <深入理解 Android 5.0 系统>来对 android 9.0 的启动代码进行分析,但是分析过程中发现自己缺乏操作系统方面的知识,以致于只能做一些简单分析.最近也买了 ...

  7. Python之抖音快手代码舞--字符舞

    先上效果,视频敬上: 字符舞: 代码舞 源代码: video_2_code_video.py 1 import argparse 2 import os 3 import cv2 4 import s ...

  8. 团队开发day07

    开始整合项目,测试登录,注册,搜索功能, 在安卓中数据处理存在个别错误,功能逻辑有个别不正确 进行修改和完善,添加二次确认退出

  9. Python+Scrapy+Crawlspider 爬取数据且存入MySQL数据库

    1.Scrapy使用流程 1-1.使用Terminal终端创建工程,输入指令:scrapy startproject ProName 1-2.进入工程目录:cd ProName 1-3.创建爬虫文件( ...

  10. 【洛谷P5008 逛庭院】tarjan缩点+贪心

    既然没有题解,那么我就来提供给一份. -- 首先我们看到数据范围.妈耶!数据这么大,一开始还想用个DP来做,但是看着就不行,那么根据这个数据范围,我们大致可以猜到这道题的算法是一个贪心,那么我们怎么贪 ...