安卓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 function函数式接口的使用方法与实例

《Javafunction函数式接口的使用方法与实例》:本文主要介绍Javafunction函数式接口的使用方法与实例,函数式接口如一支未完成的诗篇,用Lambda表达式作韵脚,将代码的机械美感... 目录引言-当代码遇见诗性一、函数式接口的生物学解构1.1 函数式接口的基因密码1.2 六大核心接口的形态学

使用DeepSeek API 结合VSCode提升开发效率

《使用DeepSeekAPI结合VSCode提升开发效率》:本文主要介绍DeepSeekAPI与VisualStudioCode(VSCode)结合使用,以提升软件开发效率,具有一定的参考价值... 目录引言准备工作安装必要的 VSCode 扩展配置 DeepSeek API1. 创建 API 请求文件2.

使用TomCat,service输出台出现乱码的解决

《使用TomCat,service输出台出现乱码的解决》本文介绍了解决Tomcat服务输出台中文乱码问题的两种方法,第一种方法是修改`logging.properties`文件中的`prefix`和`... 目录使用TomCat,service输出台出现乱码问题1解决方案问题2解决方案总结使用TomCat,

解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题

《解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题》文章详细描述了在使用lombok的@Data注解标注实体类时遇到编译无误但运行时报错的问题,分析... 目录问题分析问题解决方案步骤一步骤二步骤三总结问题使用lombok注解@Data标注实体类,编译时

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

使用Python实现高效的端口扫描器

《使用Python实现高效的端口扫描器》在网络安全领域,端口扫描是一项基本而重要的技能,通过端口扫描,可以发现目标主机上开放的服务和端口,这对于安全评估、渗透测试等有着不可忽视的作用,本文将介绍如何使... 目录1. 端口扫描的基本原理2. 使用python实现端口扫描2.1 安装必要的库2.2 编写端口扫

使用Python实现操作mongodb详解

《使用Python实现操作mongodb详解》这篇文章主要为大家详细介绍了使用Python实现操作mongodb的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、示例二、常用指令三、遇到的问题一、示例from pymongo import MongoClientf

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

使用Python合并 Excel单元格指定行列或单元格范围

《使用Python合并Excel单元格指定行列或单元格范围》合并Excel单元格是Excel数据处理和表格设计中的一项常用操作,本文将介绍如何通过Python合并Excel中的指定行列或单... 目录python Excel库安装Python合并Excel 中的指定行Python合并Excel 中的指定列P