概述

AIDL(Android interface definition Language)——Android 接口定义语言, 是 Android 提供的一种进程间通信 (IPC) 机制。可以利用它定义客户端与服务端使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。 在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。如果你有阅读源码的习惯,你会发现在Android系统提供的服务中大量使用了AIDL机制,使得其他进程能够访问并使用系统提供的服务。

使用步骤

服务端通过一个前台的Service结合SqliteDatabase数据持久化技术实现了一个简单的学生管理系统,提供学生的增删改查功能。客户端通过AIDL技术绑定服务端,进而访问和使用服务端提供的服务,对学生进行增删改查操作。同时,对服务端数据的改变进行监听,以便客户端做出相应的响应。下面分别从服务端和客户端的实现,一步一步讲解对应的实现。

服务端的实现

(1)创建服务端.aidl文件
* 自定义数据封装类

派生Parcelable自定义了Student类用于封装学生相关信息,里面包含了预先设定的几个属性。

public class Student implements Parcelable {private long id;private String name;private int gender;private int age;private int score;...@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeLong(this.id);dest.writeString(this.name);dest.writeInt(this.gender);dest.writeInt(this.age);dest.writeInt(this.score);}protected Student(Parcel in) {this.id = in.readLong();this.name = in.readString();this.gender = in.readInt();this.age = in.readInt();this.score = in.readInt();}public static final Creator<Student> CREATOR = new Creator<Student>() {@Overridepublic Student createFromParcel(Parcel source) {return new Student(source);}@Overridepublic Student[] newArray(int size) {return new Student[size];}};
}

如果在AIDL文件中用到了自定义的Parcelable对象,必须创建一个和它同名的AIDL文件,并在其中声明它为Parcelable。

// Student.aidl
package com.android.peter.aidlservicedemo.bean;
// 注意:文件中Student声明处parcelable为小写
parcelable Student;
  • 创建数据变化监听接口文件
// IOnDataChangeListener.aidl
package com.android.peter.aidlservicedemo;interface IOnDataChangeListener {void onDataChange();
}
  • 创建服务接口文件

除了基本数据类型,其他类型的参数都需要标上方向类型:in(输入), out(输出), inout(输入输出)。

// IStudentManager.aidl
package com.android.peter.aidlservicedemo;
import com.android.peter.aidlservicedemo.bean.Student;
import com.android.peter.aidlservicedemo.IOnDataChangeListener;interface IStudentManager {List<Student> getStudentList();long addStudent(in ContentValues contentValues);int deletedStudent(String whereClause, in String[] whereArgs);int updateStudent(in ContentValues contentValues, String whereClause, in String[] whereArgs);List<Student> queryStudent(in String[] columns, String selection,in String[] selectionArgs,String groupBy, String having,String orderBy, String limit);void registerDataChangeListener(IOnDataChangeListener listener);void unregisterDataChangeListener(IOnDataChangeListener listener);
}

(2)实现服务端接口

public class StudentManagerService extends Service {...private RemoteCallbackList<IOnDataChangeListener> mListenerList = new RemoteCallbackList<>();private IBinder mService = new IStudentManager.Stub() {// 检查调用进程的权限@Overridepublic boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {int checkPermission = checkCallingOrSelfPermission(PERMISSION_STUDENT_MANAGER_SERVICE);if( checkPermission != PackageManager.PERMISSION_GRANTED) {Log.i(TAG,"Do not have permission !");return false;}String packageName = null;String[] packages = getPackageManager().getPackagesForUid(getCallingUid());if(packages != null && packages.length > 0) {packageName = packages[0];}if (packageName != null && !packageName.startsWith("com.android.peter")) {Log.i(TAG,"Package name must be contains \"com.android.peter\" !");return false;}return super.onTransact(code, data, reply, flags);}@Overridepublic List<Student> getStudentList() throws RemoteException {Log.d(TAG,"getStudentList");return StudentListDaoImpl.getInstance(mContext).getAllStudent();}@Overridepublic long addStudent(ContentValues contentValues) throws RemoteException {Log.d(TAG,"addStudent contentValues = " + contentValues);long id = StudentListDaoImpl.getInstance(mContext).insert(contentValues);// the row ID of the newly inserted row, or -1 if an error occurredif(id != -1) {notifyDataChanged();}return id;}@Overridepublic int deletedStudent(String whereClause, String[] whereArgs) throws RemoteException {Log.d(TAG,"deletedStudent whereClause = " + whereClause + " , whereArgs = " + whereArgs);int num = StudentListDaoImpl.getInstance(mContext).delete(whereClause,whereArgs);// the number of rows affected if a whereClause is passed in, 0 otherwise.if(num > 0) {notifyDataChanged();}return num;}@Overridepublic int updateStudent(ContentValues contentValues, String whereClause, String[] whereArgs) throws RemoteException {Log.d(TAG,"deletedStudent contentValues = " + contentValues + " , whereClause = " + whereClause + " , whereArgs = " + whereArgs);int num = StudentListDaoImpl.getInstance(mContext).update(contentValues,whereClause,whereArgs);// the number of rows affectedif(num > 0) {notifyDataChanged();}return num;}@Overridepublic List<Student> queryStudent(String[] columns, String selection,String[] selectionArgs, String groupBy, String having,String orderBy, String limit) throws RemoteException {Log.d(TAG,"queryStudent columns = " + columns + " , selection = " + selection + " , selectionArgs = " + selectionArgs+ " , groupBy = " + groupBy + " , having = " + having + " , orderBy = " + orderBy + " , limit = " + limit);return StudentListDaoImpl.getInstance(mContext).query(columns,selection,selectionArgs,groupBy,having,orderBy,limit);}@Overridepublic void registerDataChangeListener(IOnDataChangeListener listener) throws RemoteException {mListenerList.register(listener);}@Overridepublic void unregisterDataChangeListener(IOnDataChangeListener listener) throws RemoteException {mListenerList.unregister(listener);}};...// 数据发生变化时候调用private void notifyDataChanged() {mListenerList.beginBroadcast();int N = mListenerList.getRegisteredCallbackCount();for(int i = 0 ; i < N ; i++) {try {mListenerList.getBroadcastItem(i).onDataChange();} catch (RemoteException e) {e.printStackTrace();}}mListenerList.finishBroadcast();}...
}

(3)公开相关接口文件

客户端的实现

(1)把相关接口文件拷贝到相应位置
需要把所有相关的文件和类全部拷贝到客户端对应的位置,切记不要修改完整路径和文件名,否则会编译不过。

(2)绑定服务、获得服务

public class ClientActivity extends AppCompatActivity {private final static String TAG = "peter.ClientActivity";private final static String REMOTE_SERVICE_PACKAGE_NAME = "com.android.peter.aidlservicedemo";private final static String REMOTE_SERVICE_CLASS_NAME = "com.android.peter.aidlservicedemo.StudentManagerService";private final static int MESSAGE_DATA_CHANGED = 20180804;@SuppressLint("HandlerLeak")private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case MESSAGE_DATA_CHANGED:Log.i(TAG,"I have received a data changed message!");break;default:// do nothing}}};private IStudentManager mRemoteService;private IOnDataChangeListener mOnDataChangeLister = new IOnDataChangeListener.Stub() {@Overridepublic void onDataChange() throws RemoteException {Log.i(TAG,"onDataChange");// running in Binder's thread pool,could be used to do long-time task,// and could not to access to UI thread directlymHandler.sendEmptyMessage(MESSAGE_DATA_CHANGED);}};private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {@Overridepublic void binderDied() {Log.i(TAG,"Remote service is died !");if(mRemoteService == null) {return;}mRemoteService.asBinder().unlinkToDeath(mDeathRecipient,0);mRemoteService = null;// rebind remote servicebindRemoteService();}};private ServiceConnection mSC = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.i(TAG,"onServiceConnected name = " + name);// 获得服务端服务mRemoteService = IStudentManager.Stub.asInterface(service);if(mRemoteService != null) {try {// 设置死亡代理mRemoteService.asBinder().linkToDeath(mDeathRecipient,0);// 注册监听器mRemoteService.registerDataChangeListener(mOnDataChangeLister);} catch (RemoteException e) {e.printStackTrace();}} else {Log.i(TAG,"Connect error!");}// onServiceConnected is running in main thread,// can not do long-time tasknew Thread(new Runnable() {@Overridepublic void run() {insertStudent();queryStudent();updateStudent();deleteStudent();}}).start();}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.i(TAG,"onServiceDisconnected name = " + name);mRemoteService = null;}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_client);bindRemoteService();}// bind remote serviceprivate void bindRemoteService() {Intent intent = new Intent();intent.setComponent(new ComponentName(REMOTE_SERVICE_PACKAGE_NAME, REMOTE_SERVICE_CLASS_NAME));bindService(intent,mSC, Context.BIND_AUTO_CREATE);}// inset three studentsprivate void insertStudent() {Log.i(TAG,"insertStudent");ContentValues peter = new ContentValues();peter.put(StudentTable.COLUMN_NAME,"peter");peter.put(StudentTable.COLUMN_GENDER,0);peter.put(StudentTable.COLUMN_AGE,33);peter.put(StudentTable.COLUMN_SCORE,100);ContentValues lemon = new ContentValues();lemon.put(StudentTable.COLUMN_NAME,"lemon");lemon.put(StudentTable.COLUMN_GENDER,1);lemon.put(StudentTable.COLUMN_AGE,30);lemon.put(StudentTable.COLUMN_SCORE,100);ContentValues baoyamei = new ContentValues();baoyamei.put(StudentTable.COLUMN_NAME,"baoyamei");baoyamei.put(StudentTable.COLUMN_GENDER,1);baoyamei.put(StudentTable.COLUMN_AGE,30);baoyamei.put(StudentTable.COLUMN_SCORE,90);try {if(mRemoteService != null) {mRemoteService.addStudent(peter);mRemoteService.addStudent(lemon);mRemoteService.addStudent(baoyamei);List<Student> studentList = mRemoteService.getStudentList();Log.i(TAG,"studentList = " + studentList.toString());}} catch (RemoteException e) {e.printStackTrace();}}// query the student who's age is 30private void queryStudent() {Log.i(TAG,"queryStudent");if(mRemoteService != null) {try{List<Student> queryList = mRemoteService.queryStudent(StudentTable.TABLE_COLUMNS,StudentTable.COLUMN_AGE + "=?",new String[]{"30"},null,null,null,null);Log.i(TAG,"queryList = " + queryList.toString());} catch (RemoteException e) {e.printStackTrace();}}}// update the student who's score is 90private void updateStudent() {Log.i(TAG,"updateStudent");if(mRemoteService != null) {ContentValues lemon = new ContentValues();lemon.put(StudentTable.COLUMN_SCORE,100);try {mRemoteService.updateStudent(lemon,StudentTable.COLUMN_NAME + "=?",new String[]{"baoyamei"});List<Student> studentList = mRemoteService.getStudentList();Log.i(TAG,"studentList = " + studentList.toString());} catch (RemoteException e) {e.printStackTrace();}}}// delete the student who's name is baoyameiprivate void deleteStudent() {Log.i(TAG,"deleteStudent");if(mRemoteService != null) {try{mRemoteService.deletedStudent(StudentTable.COLUMN_SCORE + "=?",new String[]{"100"});List<Student> studentList = mRemoteService.getStudentList();Log.i(TAG,"studentList = " + studentList.toString());} catch (RemoteException e) {e.printStackTrace();}}}@Overrideprotected void onDestroy() {super.onDestroy();if(mRemoteService != null && mRemoteService.asBinder().isBinderAlive()) {try {mRemoteService.unregisterDataChangeListener(mOnDataChangeLister);} catch (RemoteException e) {e.printStackTrace();}}if(mSC != null) {unbindService(mSC);}}
}

小结

本文结合实例扼要的介绍了AIDL的使用方法,关于SqliteDatabase的使用可以参考我的另一篇文章——Android数据存储之SQLiteDatabase。使用过程中应该注意的地方:

  • 在定义Parcelable类和对应的.aidl文件的名字以及完整路径必须保持一致,否则编译会报错。同时,注意在.aidl文件中的parcelable声明为小写。
  • 除了基本数据类型,其他类型的参数都需要标上方向类型:in(输入), out(输出), inout(输入输出)。
  • 需要把所有相关的文件和类全部拷贝到客户端对应的位置,切记不要修改完整路径和文件名,否则会编译不过。
  • 在实现监听器的时候,推荐选用RemoteCallbackList类作为服务端存储监听器列表的容器。进程间是不允许直接传递对象的,是通过序列化和反序列化来实现的,这也是为什么在定义自定义数据类型时需要派生Parcelable类。RemoteCallbackList内部是以CallBack的IBinder对象作为key值,以CallBack对象作为value值进行存储的,服务端反序列化后生成的CallBack对象其实是新生成的,但是底层对应IBinder并没有改变,客户端和服务端的CallBack对象是通过IBinder来关联的。
  • 类似于访问网络,与服务端交互的时候很有可能也是耗时操作,最好不要在UI线程使用,有可能导致ANR。
  • 通过单实例保证服务端全局只有一个SQLiteOpenHelper对象与数据库连接,解决数据库多线程并发访问问题。
  • 从安全角度出发,需要为你的服务添加权限控制和包名检测,只有具有权限和指定包名的应用才能访问服务。添加权限控制的方法有多种,比如可以在AndroidManifest中添加权限声明,然后在Service声明处添加android:permission属性。或是在实现服务端接口的时候重写onTransact方法,这个方法在每次服务被调用的时候都会先被调用,可以在里面判断权限和包名。

更多的细节可以参看《Android开发艺术探索》关于AIDL的章节,里面对技术细节讲解的非常详细。

AIDLClientDemo
AIDLServiceDemo

AIDL的简单使用和注意事项相关推荐

  1. android的timertask,Android TimerTask 的简单应用及注意事项

    Android  TimerTask 的简单应用及注意事项 Android应用开发中常常会用到定时器,不可避免的需要用到 TimerTask 定时器任务这个类 下面简单的一个示例演示了如何使用Time ...

  2. 弘辽科技:开淘宝直通车有哪些简单技巧?注意事项是什么?

    原标题<弘辽科技:开淘宝直通车有哪些简单技巧?注意事项是什么?> 淘宝直通车这个工具,想必淘宝商家都很熟悉不陌生了吧,淘宝商家在做推广的时候,经常会用到的一个工具,接下来就位大家分享一些开 ...

  3. windows桌面待办事项_有没有一款使用简单的电脑桌面待办事项提醒软件

    云菲在大学时养成了一个习惯,就是使用便签记录老师布置的学习任务,这样既不会出现纰漏,也可以让大脑减负,而且还能在学业上投入更多的精力.进入职场后,这个习惯一直没有改变,但是问题也就随之而来了. 因为已 ...

  4. gorm软删除_gorm的简单使用和注意事项

    Gorm当前支持MySql, PostgreSql, Sqlite等主流数据库 1.安装 首先安装数据库驱动go get github.com/go-sql-driver/mysql 然后安装gorm ...

  5. ThinkPad笔记本电脑海淘简单教程及注意事项

    一.注意事项 不管怎么说,海淘笔记本电脑质量高,单单这一点就能让很多人奋不顾身.在此我们就来看看海淘笔记本应该注意些什么吧? 1.首先要考虑的是你要买的产品是否国外比国内的性价比要高,如果是还要确定哪 ...

  6. Kotlin的数组array和集合list的简单使用以及注意事项

    学习到Kotlin的数组和集合,这里做个记录. 数组Array Kotlin中数组也是个容器,提供了不同的类型有:ByteArray, CharArray, ShortArray, IntArray, ...

  7. 虚幻4制作简单手雷的注意事项

    今天用虚幻4做了一个简单的扔手雷, 其中出现问题,手雷落到地面上没有爆炸特效,只有声音,但是空中爆炸就没这个问题,后来发现是手雷由于捧住停在地板上,而特效却在地板下面爆炸.究其原因是使用get act ...

  8. 软件著作权申请流程很简单,不过这些事项需要格外注意

    上年年末时编写了一个软件,然后就想顺便申请一个软著.一开始在网上看到有人自主申请成功以后,就想着自己申请软著,但了解到自主申请软著需要一到四个月的时间,而找代理机构却可以最快几天就拿证以后,为避免夜长 ...

  9. Angular中实现一个简单的toDoList(待办事项)示例代码

    场景 Angular介绍.安装Angular Cli.创建Angular项目入门教程: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/detail ...

  10. python中elif和while简单介绍及注意事项(含笔记)

    一.elif 想一想: if能完成当xxx时做事情:if-else能完成当xxx时做事情1,否则做事情2; 如果有这样一种情况:当xxx1时做事情1,当xxx2时做事情2,当xxx3时做事情3,那该怎 ...

最新文章

  1. 可视化swing界面编辑--转载
  2. Electron中 提示:Uncaught ReferenceError: process is not defined
  3. cetk使用方法--nand flash测试
  4. python 温度 符号_Python通过小实例入门学习---1.0(温度转换)
  5. php中颜色的索引值,PHP imagecolorsforindex - 取得某索引的颜色
  6. goldendict设置使用vlc或mplayer发音
  7. java jdk官网下载地址
  8. 中国水电基础局携手友勤开展2018年P6软件培训班
  9. linux下安装Adobe Reader(acroread)
  10. 非线性控制2.0——鲁棒控制之H无穷控制器设计
  11. cs229 学习笔记四 学习理论
  12. Raspberry Pi 4b CUPS AirPrint 共享网络打印机
  13. 京沪广深同日发布网约车新规细则草案 滴滴回应称或将导致车辆供给骤减
  14. 【精品整站】WordPress自适应美女写真网站源码/美图整站源码带数据/安装即可运营
  15. 简单易学又有效的颈椎病自我康复操
  16. 人工神经网络理论及应用pdf,人工智能的相关书籍
  17. 困扰了已久的TCP/IP 协议,终于有人讲的明明白白,太强了
  18. The Evils of Duplication
  19. RSA非对称加密解密概念
  20. 如何用VS2017生成可执行文件

热门文章

  1. sql语句中having的用法
  2. 环境监测设备中,使用GPS模拟器测试TTFF和灵敏度的注意点
  3. 虚拟打印机 android版,Doro PDF Writer
  4. 计算机软件登记文档,计算机软件著作权登记申请表范本
  5. matlab的基本使用
  6. TakeColor 屏幕取色器 8.0 中文绿色版
  7. java 取色器_Arava: 用 swing 写一个取色器
  8. Windows下TexLive2015 TeXstudio 和SumatraPDF安装配置
  9. 7000个源码批量下载
  10. 企业数字化转型之道(值得收藏)