安卓IPC之aidl使用(一)--aidl常见使用

2024-09-03 02:18
文章标签 使用 常见 ipc 安卓 aidl

本文主要是介绍安卓IPC之aidl使用(一)--aidl常见使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在android上有很多跨进程的通讯方法例如aidl,messenger,ContentProvider,BroadCast,Socket等等,安卓进程间通信(IPC)那肯定要谈到AIDL。

你知道你需要进程间通信、需要AIDL(以及Binder),那么可以默认你对这些概念已经有了一些了解,你(大致)知道它们是什么,它们有什么用,所以为了节约大家的眼力和时间。
安卓IPC之aidl使用(一)–aidl常见使用
安卓IPC之aidl使用(二)—aidl本地实现
安卓IPC之aidl使用(三)—System aidl调用

AIDL简单介绍

AIDL:Android Interface Definition Language,即Android接口定义语言

Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。我们知道建立AIDL服务的步骤有一下三个步骤:
建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同
建立一个服务类(Service的子类)
实现由aidl文件生成的Java接口

AIDL 语法

其实AIDL这门语言非常的简单,基本上它的语法和 Java 是一样的,只是在一些细微处有些许差别——毕竟它只是被创造出来简化Android程序员工作的,太复杂不好——所以在这里我就着重的说一下它和 Java 不一样的地方。主要有下面这些点:

  • 文件类型:用AIDL书写的文件的后缀是 .aidl,而不是 .java。

  • 数据类型:AIDL默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包, 就算目标文件与当前正在编写的 .aidl 文件在同一个包下 ——在 Java 中,这种情况是不需要导包的。比如,现在我们编写了两个文件,一个叫做 Person.java ,另一个叫做 Person.aidl ,它们都在com.losileeya.aidlmaster包下 ,现在我们需要在 .aidl 文件里使用 Peerson 对象,那么我们就必须在 .aidl 文件里面写上 *import com.losileeya.aidlmaster.Person;哪怕 .java 文件和 .aidl 文件就在一个包下。

    默认支持的数据类型包括:

    • Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。
    • String 类型。
    • CharSequence类型。
    • List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。
    • Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。
  • 定向tag:这是一个极易被忽略的点——这里的“被忽略”指的不是大家都不知道,而是很少人会正确的使用它。在我的理解里,定向 tag 是这样的: AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。
    另外,Java 中的基本类型和 String ,CharSequence 的定向 tag 默认且只能是 in 。还有,请注意, 请不要滥用定向 tag ,全都一上来就用 inout ,等工程大了系统的开销就会大很多——因为排列整理参数的开销是很昂贵的。

  • 两种AIDL文件:在我的理解里,所有的AIDL文件大致可以分为两类。一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型的。一类是用来定义方法接口,以供系统使用来完成跨进程通信的。可以看到,两类文件都是在“定义”些什么,而不涉及具体的实现,这就是为什么它叫做“Android接口定义语言”。
    注: 所有的非默认支持数据类型必须通过第一类AIDL文件定义才能被使用。

AIDL实现步骤

我的编译环境为Android Studio2.1.3,SDK Version 24,JDK 1.8

service端

.AIDL生成

鼠标移到module上面去,点击右键,然后 new->AIDL->AIDL File,按下鼠标左键就会弹出一个框提示生成AIDL文件了。生成AIDL文件之后,项目的目录会变成这样的:

里面的代码按照你的意思来,必需以interface开头,并且参数里面使用了引用的parcelable对象,我们需要import,示例如下:

// IRemoteAidl.aidl
package com.losileeya.aidlmaster;
import com.losileeya.aidlmaster.Person;
// Declare any non-default types here with import statements
//这个文件的作用是引入了一个序列化对象 Person 供其他的AIDL文件使用
//注意:Person.aidl与Person.java的包名应当是一样的
parcelable Person;
interface IRemoteAidl {int add(int num1,int num2);//计算2个数之和//传参时除了Java基本类型以及String,CharSequence之外的类型//都需要在前面加上定向tag,具体加什么量需而定List<Person>addPerson(in Person p);//添加一个parcelable
}

好了我们这里提到了parcelable对象,所以我们需要1个Person.aidl与Person.java而且须保证包名应当是一样的

Parcelable 类型的使用

先写Person.java,并且实现parcelable接口,android studio早就替我们提供了这样的插件。

package com.losileeya.aidlmaster;
import android.os.Parcel;
import android.os.Parcelable;
/*** User: Losileeya (847457332@qq.com)* Date: 2016-07-10* Time: 00:37* 类描述:实现Parcelable序列化** @version :*/
public class Person implements Parcelable {private String name;private int age;@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(this.name);dest.writeInt(this.age);}public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}protected Person(Parcel in) {this.name = in.readString();this.age = in.readInt();}public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {@Overridepublic Person createFromParcel(Parcel source) {return new Person(source);}@Overridepublic Person[] newArray(int size) {return new Person[size];}};
}

Person.aidl

package com.losileeya.aidlmaster;
// Declare any non-default types here with import statements
parcelable Person;

到这里我们就已经将AIDL文件新建并且书写完毕了,clean 一下项目,如果没有报错,这一块就算是大功告成了。

编写Service

在服务端实现AIDL中定义的方法接口的具体逻辑,然后在客户端调用这些方法接口,从而达到跨进程通信的目的。

接下来我直接贴上我写的服务端代码:

package com.losileeya.aidlmaster;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;import org.jetbrains.annotations.Nullable;import java.util.ArrayList;
import java.util.List;/*** User: Losileeya (847457332@qq.com)* Date: 2016-07-09* Time: 12:28* 类描述:aidl实现功能的逻辑代码** @version :*/
public class IRemoteService extends Service{private ArrayList<Person>persons;@Nullable@Overridepublic IBinder onBind(Intent intent) {persons=new ArrayList<>();return mBinder;}private IBinder mBinder=new IRemoteAidl.Stub(){@Overridepublic int add(int num1, int num2) throws RemoteException {Log.d("TAG","服务已开启");return num1+num2;}@Overridepublic List<Person> addPerson(Person p) throws RemoteException {persons.add(p);return persons;}};
}

整体的代码结构很清晰,大致可以分为2块:第一块是 重写 IRemoteAidl.Stub 中的方法 。在这里面提供AIDL里面定义的方法接口的具体实现逻辑。第三块是 重写 onBind() 方法 。在里面返回写好的 IRemoteAidl.Stub 。

manifest 配置
        <service android:name=".IRemoteService"><intent-filter><action android:name="com.losileeya.aidlmaster.IRemoteService"/></intent-filter></service>

到这里我们的服务端代码就编写完毕了,真正是扯得多,代码少。

Client代码编写

首先就是拷贝AIDL文件夹到客户端目录下(与java目录同级)

然后需要重新写一个Person.java,也可以直接从服务端拷贝过来,必须保证Person.java和aidl的文件是相同的包名。

获得IRemoteAidl和ServiceConnection
 private ServiceConnection conn=new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {iRemoteAidl=   IRemoteAidl.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {iRemoteAidl=null;}};
bindService
 private void bindService() {Intent intent=new Intent();intent.setComponent(new ComponentName("com.losileeya.aidlmaster","com.losileeya.aidlmaster.IRemoteService"));bindService(intent,conn, Context.BIND_AUTO_CREATE);}

ComponentName里面的绝对不能写错,前面一个是包名,后面是启动的服务类,

这样基本就写好了,可以通信了。

## MainActivity
package com.losileeya.aidlclient;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;import com.losileeya.aidlmaster.IRemoteAidl;
import com.losileeya.aidlmaster.Person;import java.util.ArrayList;
import java.util.Iterator;public class MainActivity extends AppCompatActivity implements View.OnClickListener {private IRemoteAidl iRemoteAidl=null;private EditText etNum1,etNum2,etResult;private Button btnCalculate;private ArrayList<Person>datas;private TextView tv_desc;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();bindService();}private void initView() {etNum1= (EditText) findViewById(R.id.et_num1);etNum2= (EditText) findViewById(R.id.et_num2);etResult= (EditText) findViewById(R.id.et_result);btnCalculate= (Button) findViewById(R.id.btn_calculate);tv_desc= (TextView) findViewById(R.id.tv_desc);btnCalculate.setOnClickListener(this);}private void bindService() {Intent intent=new Intent();intent.setComponent(new ComponentName("com.losileeya.aidlmaster","com.losileeya.aidlmaster.IRemoteService"));bindService(intent,conn, Context.BIND_AUTO_CREATE);}private ServiceConnection conn=new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {iRemoteAidl=   IRemoteAidl.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {iRemoteAidl=null;}};@Overrideprotected void onDestroy() {super.onDestroy();unbindService(conn);}@Overridepublic void onClick(View v) {int num1=Integer.parseInt(etNum1.getText().toString().trim());int num2=Integer.parseInt(etNum2.getText().toString().trim());try {int result=iRemoteAidl.add(num1,num2);etResult.setText(result+"");datas= (ArrayList<Person>) iRemoteAidl.addPerson(new Person("小a",21));Iterator i=datas.iterator();while (i.hasNext()){tv_desc.setText(i.next().toString());}} catch (RemoteException e) {e.printStackTrace();etResult.setText("失败");}}
}

效果如下:

demo传送门:AIDLMaster

这篇关于安卓IPC之aidl使用(一)--aidl常见使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1131745

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV

Redis 基本数据类型和使用详解

《Redis基本数据类型和使用详解》String是Redis最基本的数据类型,一个键对应一个值,它的功能十分强大,可以存储字符串、整数、浮点数等多种数据格式,本文给大家介绍Redis基本数据类型和... 目录一、Redis 入门介绍二、Redis 的五大基本数据类型2.1 String 类型2.2 Hash

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他