热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

详解Android跨进程IPC通信AIDL机制原理

本篇文章主要介绍了详解Android跨进程IPC通信AIDL机制原理,详细的介绍了AIDL的概念和使用,具有一定的参考价值,有兴趣的可以了解一下

简介

AIDL:Android Interface Definition Language,即Android接口定义语言,用于生成Android不同进程间进行进程通信(IPC)的代码,一般情况下一个进程是无法访问另一个进程的内存的。如果某些情况下仍然需要跨进程访问内存数据,这时候Android系统就要将其对象分解成能够识别的原数据,编写这一组操作的代码是一项繁琐的工作,但是AIDL对底层进行了抽象的封装,简化了跨进程操作。

AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

在Android中跨进程操作的方式不止一种,四大组件中ContentProvider天生就是为跨进程操作而存在的,但是ContentProvider所谓的跨进程操作数据,这些数据不一定是存放在内存中的,如通讯录数据时存放在Sqlite数据库中的。AIDL支持的跨进程操作的数据是要存放在内存中的,AIDL底层实际上也是使用的Binder进行的跨进程操作,后续另起一篇博文继续介绍Binder的跨进程机制。

使用场景

只有不同应用之间需要进行IPC,并且想要在Service中处理多线程时,这种场景才有必要使用AIDL。如果仅仅需要跨进程但是不是跨应用,这时候应该通过Binder进行数据交互;另外如果仅仅是需要跨进程IPC,但是不需要处理多线程,这时候应该通过Messenger类进行数据交互。

定义AIDL接口

在Android Studio中使用AIDL的项目的目录结构跟eclipse中有很大差异,下图是使用AIDL的项目的目录结构。

在Android Studio中只需要在某个Module中使用右键菜单中new就会显示创建AIDL文件的菜单,当新建成功后AIDL文件位于工程的同java同一级的aidl目录文件夹下面。在 .aidl 文件中存放的就是AIDL接口。

定义.aidl文件

.aidl文件名称必须同接口名称保持一致,必须使用Java语言的语法定义AIDL文件。AIDL使用简单语法,通过可带参数和返回值的一个或多个方法来声明接口。参数和返回值可以是任意类型,甚至可以是其他 AIDL 生成的接口。每个.aidl文件都必须定义单个接口,并且只需包含接口声明和方法签名,也意味着在.aidl文件中接口名称和方法名称都不可以使用权限修饰符。

默认情况下,AIDL 支持下列数据类型:

  1. Java语言中所有的基本数据类型,字符类型char,布尔类型boolean以及数值类型byte、short、int、long、float、double;
  2. String和CharSequence类型;
  3. 集合List类型,List中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。可选择将 List用作泛型类型(例如,List )。另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是List接口, 在AIDL中不可以使用ArrayList类型进行定义,只能使用List接口定义 ,否则会报unknown type编译错误。
  4. 集合Map类型,中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。不同于集合List接口, 在AIDL中不支持泛型Map(如 Map 形式的 Map)。 另一端实际接收的具体类始终是 HashMap,但生成的方法使用的是 Map 接口。类似List接口, 在.aidl文件中不能使用HashMap,只能使用Map接口 。
  5. 自定义类型必须实现Parcelable接口,并且在aidl文件夹下有对应类型的aidl文件;
  6. 非JDK中定义的类型,类似于Java语法,必须使用import进行引入。

定义AIDL接口时需要注意如下:

  1. 方法可带零个或多个参数,返回值或空值,但是方法名称不能相同;
  2. 所有非基本数据类型参数都需要指示数据走向的方向标记。可以是 in、out 或 inout。基本数据类型默认为 in,不能是其他方向;
  3. .aidl 文件中包括的所有代码注释都包含在生成的 IBinder 接口中(import 和 package 语句之前的注释除外);
  4. 只支持方法,不应在AIDL中定义静态字段。

如下是定义是IRemoteService.aidl:

package com.sunny.server;
import com.sunny.server.bean.User;
interface IRemoteService {
  void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
      double aDouble, String aString);
   String getName();
   void setListData(in List inList,out List outList);
   void setMapData(in Map map);
   void setUser(in User user); 
}

下面是自定义的JavaBean类型User.aidl

package com.sunny.server.bean; 
parcelable User;

User类在使用的时候必须实现Parcelable接口,代码这里就不再贴出来了。

Service实现AIDL接口

在定义的AIDL接口编译后实际上会生成一个跟.aidl同名的Java类文件,里面包含了所有的AIDL文件中声明的方法,并且包含了一个默认的实现类Stub,该类是抽象类,继承了Binder类实现了AIDL接口。在Stub类中有两个方法一个是asInterface()方法,该方法返回的是AIDL文件生成的接口,另外一个方法是asBinder(),该方法返回的是一个IBinder类型的实例。

asInterface()和asBinder()方法非常有用,asInterface()方法可以用于客户端的IPC方法调用,另外一个方法可以用于在服务端返回Binder实例,并在服务端实现响应的接口方法。

上面介绍过在定义非基本数据类型的时候必须定义数据走向,声明in或out或者inout,在AIDL生成的Java文件中就可以看出来究竟了,这里可以参看setListData()方法的生成实现。

@Override
public void setListData(java.util.List inList, java.util.List outList) throws android.os.RemoteException {
 android.os.Parcel _data = android.os.Parcel.obtain();
 android.os.Parcel _reply = android.os.Parcel.obtain();
 try {
 _data.writeInterfaceToken(DESCRIPTOR);
 _data.writeStringList(inList);
 mRemote.transact(Stub.TRANSACTION_setListData, _data, _reply, 0);
 _reply.readException();
 _reply.readStringList(outList);
 } finally {
 _reply.recycle();
 _data.recycle();
 }
}

如果声明数据时是in,在生成相对应的方法的时候调用的实际上是Parcel的writeXXX方法,如果声明的是out,在实现上面采用的是readXXX,所以在定义的时候一定要明确调用逻辑。

接下来看一下服务端中MyService类的实现。

public class MyService extends Service {
   private static final String TAG = "AIDL_Server"; 
  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    return mBinder;
  }
 
  private IRemoteService.Stub mBinder = new IRemoteService.Stub() { 
    @Override
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
      Log.d(TAG, "anInt:" + anInt + " aLong:" + aLong + " aBoolean:" + aBoolean + " aFloat:" + aFloat + " aDouble:" + aDouble + " aString:" + aString);
    }
 
    @Override
    public String getName() throws RemoteException {
      return "admin";
    }
 
    @Override
    public void setListData(List inList, List outList) throws RemoteException {
      Log.d(TAG, "inList:" + inList.toString());
      setOutList(outList);
    }
 
    @Override
    public void setMapData(Map map) throws RemoteException {
      Log.d(TAG, "map:" + map.toString());
    }
 
    @Override
    public void setUser(User user) throws RemoteException {
      Log.d(TAG, "user:" + user.toString());
    }
  };
 
  private void setOutList(List list) {
    list.add("out_01");
    list.add("out_02");
    list.add("out_03");
  }
}

在MyService类中,除了getName是一个有返回值的方法,其余的方法都是void类型的,另外在数据走向方面,除了setListData方法的第二个参数outList是输出类型的参数,其余的参数都是输入类型参数,所以这里将其它参数直接打印出来了。

调用IPC方法

在客户端想要调用Android的AIDL中定义的IPC方法,可以通过如下步骤实现:

  1. 首先需要定义一个相同包名相同目录的AIDL文件夹;
  2. 声明一个AIDL文件生成的接口实例;
  3. 实现ServiceConnection接口;
  4. 调用bindService绑定服务,传入生成的ServiceConnection实例;
  5. 在onServiceConnected()实现中,将收到的IBinder实例(名为 service)。调用 XXX.Stub.asInterface((IBinder)service),以将返回的参数转换为 AIDL生成的接口类型。
  6. 通过调用生成的AIDL接口实例中对应的方法就可以实现IPC调用了;
  7. 在不使用的时候解除服务的绑定Context.unbindService()。

如下是客户端Activity中代码的实现:

public class MainActivity extends AppCompatActivity { 
  private static final String TAG = "AIDL_Client";
  private MyConnection conn;
  private IRemoteService service;
  private List outList=new ArrayList<>();
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }
 
  public void startBind(View v) {
    Intent intent = new Intent();
    cOnn=new MyConnection();
    intent.setAction("com.sunny.server.service.MyService");
    bindService(intent, conn, Context.BIND_AUTO_CREATE);
  }
 
  public void startExecute(View v) {
    try {
      service.basicTypes(1, 10000L, true, 1.5f, 300.3, "Hello World");
 
      Log.d(TAG, "getName:" + service.getName());
      List inList = new ArrayList();
      inList.add("inList01");
      inList.add("inList02");
      service.setListData(inList, outList);
      Log.d(TAG, "outList:" + outList.toString()); 
      Map map = new HashMap();
      map.put("key01", "value01");
      map.put("key02", "value02");
      service.setMapData(map); 
      User user = new User();
      user.setId(1001);
      user.setName("admin");
      service.setUser(user);
    } catch (RemoteException e) {
      e.printStackTrace();
    }
  }
  private class MyConnection implements ServiceConnection { 
    public void onServiceConnected(ComponentName name, IBinder binder) {
      service = IRemoteService.Stub.asInterface(binder);
    } 
    public void onServiceDisconnected(ComponentName name) {
    }
  }
 
  @Override
  protected void onDestroy() {
    super.onDestroy();
    unbindService(conn);
  }
}

其它

上述示例只是为了介绍AIDL如何跨进程通信的,所以在客户端接收到的数据直接就在主线程中处理了。但是实际上客户端调用服务端的远程方法,被调用的方法运行在服务端的Binder线程池中的,同时客户端线程会被挂起,这时候如果服务端方法执行比较耗时,就会导致客户端长时间阻塞在这里,如果客户端方法位于UI线程中,可能会引起ANR。在实际开发的时候注意,客户端进行IPC通信的时候尽量放在子线程中。由于服务端的方法本身就是运行在服务端的Binder线程池中,所以即使服务端需要执行大量耗时的工作也不需要开启新的线程去执行。

另外一定要注意的就是安全性,默认情况下远程服务任何人都可以连接,这应该不是我们所需要的,所以还需要考虑一下权限验证。一般情况下有两种处理方法,第一种是通过自定义权限的方法,我们在服务端Service方法的onBinder()方法中添加权限验证,如果权限验证不通过直接返回null。另外一种就是在服务端的onTransact()方法中做验证,也是做权限验证,如果不通过直接返回false。除了上面讲的权限验证之外,可以通过getCallingPid()和getCallingUid()拿到客户端应用的Pid和Uid进行校验。

有关AIDL的介绍就先到这里了,后续继续介绍一下Binder有关内容。以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了adg架构设置在企业数据治理中的应用。随着信息技术的发展,企业IT系统的快速发展使得数据成为企业业务增长的新动力,但同时也带来了数据冗余、数据难发现、效率低下、资源消耗等问题。本文讨论了企业面临的几类尖锐问题,并提出了解决方案,包括确保库表结构与系统测试版本一致、避免数据冗余、快速定位问题等。此外,本文还探讨了adg架构在大版本升级、上云服务和微服务治理方面的应用。通过本文的介绍,读者可以了解到adg架构设置的重要性及其在企业数据治理中的应用。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • MyBatis错题分析解析及注意事项
    本文对MyBatis的错题进行了分析和解析,同时介绍了使用MyBatis时需要注意的一些事项,如resultMap的使用、SqlSession和SqlSessionFactory的获取方式、动态SQL中的else元素和when元素的使用、resource属性和url属性的配置方式、typeAliases的使用方法等。同时还指出了在属性名与查询字段名不一致时需要使用resultMap进行结果映射,而不能使用resultType。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了一款名为TimeSelector的Android日期时间选择器,采用了Material Design风格,可以在Android Studio中通过gradle添加依赖来使用,也可以在Eclipse中下载源码使用。文章详细介绍了TimeSelector的构造方法和参数说明,以及如何使用回调函数来处理选取时间后的操作。同时还提供了示例代码和可选的起始时间和结束时间设置。 ... [详细]
  • 模块化区块链生态系统的优势概述及其应用案例
    本文介绍了相较于单体区块链,模块化区块链生态系统的优势,并以Celestia、Dymension和Fuel等模块化区块链项目为例,探讨了它们解决可扩展性和部署问题的方案。模块化区块链架构提高了区块链的可扩展性和吞吐量,并提供了跨链互操作性和主权可扩展性。开发人员可以根据需要选择执行环境,并获得奖学金支持。该文对模块化区块链的应用案例进行了介绍,展示了其在区块链领域的潜力和前景。 ... [详细]
  • 本文介绍了在Ubuntu 11.10 x64环境下安装Android开发环境的步骤,并提供了解决常见问题的方法。其中包括安装Eclipse的ADT插件、解决缺少GEF插件的问题以及解决无法找到'userdata.img'文件的问题。此外,还提供了相关插件和系统镜像的下载链接。 ... [详细]
  • 项目运行环境配置及可行性分析
    本文介绍了项目运行环境配置的要求,包括Jdk1.8、Tomcat7.0、Mysql、HBuilderX等工具的使用。同时对项目的技术可行性、操作可行性、经济可行性、时间可行性和法律可行性进行了分析。通过对数据库的设计和功能模块的设计,确保系统的完整性和安全性。在系统登录、系统功能模块、管理员功能模块等方面进行了详细的介绍和展示。最后提供了JAVA毕设帮助、指导、源码分享和调试部署的服务。 ... [详细]
  • 本文介绍了常用的编辑器快捷键,包括快速转换编辑器、浏览选项卡、提取本地变量和方法、编辑器窗口最大化等功能。通过使用这些快捷键,可以提高编辑器的使用效率,减少复杂度,并提升代码的可测试性。 ... [详细]
  • 初探PLC 的ST 语言转换成C++ 的方法
    自动控制软件绕不开ST(StructureText)语言。它是IEC61131-3标准中唯一的一个高级语言。目前,大多数PLC产品支持ST ... [详细]
  • 使用J2SE模拟MVC模式开发桌面应用程序的工程包的介绍
    以我开发过的一个娱乐管理系统为例:下图为我系统的业务逻辑的MVC流程:下图为以Eclipse开发中各包的说明:转载于:https:blog ... [详细]
author-avatar
速度向前迈进
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有