AIDL的简单使用和注意事项
概述
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的简单使用和注意事项相关推荐
- android的timertask,Android TimerTask 的简单应用及注意事项
Android TimerTask 的简单应用及注意事项 Android应用开发中常常会用到定时器,不可避免的需要用到 TimerTask 定时器任务这个类 下面简单的一个示例演示了如何使用Time ...
- 弘辽科技:开淘宝直通车有哪些简单技巧?注意事项是什么?
原标题<弘辽科技:开淘宝直通车有哪些简单技巧?注意事项是什么?> 淘宝直通车这个工具,想必淘宝商家都很熟悉不陌生了吧,淘宝商家在做推广的时候,经常会用到的一个工具,接下来就位大家分享一些开 ...
- windows桌面待办事项_有没有一款使用简单的电脑桌面待办事项提醒软件
云菲在大学时养成了一个习惯,就是使用便签记录老师布置的学习任务,这样既不会出现纰漏,也可以让大脑减负,而且还能在学业上投入更多的精力.进入职场后,这个习惯一直没有改变,但是问题也就随之而来了. 因为已 ...
- gorm软删除_gorm的简单使用和注意事项
Gorm当前支持MySql, PostgreSql, Sqlite等主流数据库 1.安装 首先安装数据库驱动go get github.com/go-sql-driver/mysql 然后安装gorm ...
- ThinkPad笔记本电脑海淘简单教程及注意事项
一.注意事项 不管怎么说,海淘笔记本电脑质量高,单单这一点就能让很多人奋不顾身.在此我们就来看看海淘笔记本应该注意些什么吧? 1.首先要考虑的是你要买的产品是否国外比国内的性价比要高,如果是还要确定哪 ...
- Kotlin的数组array和集合list的简单使用以及注意事项
学习到Kotlin的数组和集合,这里做个记录. 数组Array Kotlin中数组也是个容器,提供了不同的类型有:ByteArray, CharArray, ShortArray, IntArray, ...
- 虚幻4制作简单手雷的注意事项
今天用虚幻4做了一个简单的扔手雷, 其中出现问题,手雷落到地面上没有爆炸特效,只有声音,但是空中爆炸就没这个问题,后来发现是手雷由于捧住停在地板上,而特效却在地板下面爆炸.究其原因是使用get act ...
- 软件著作权申请流程很简单,不过这些事项需要格外注意
上年年末时编写了一个软件,然后就想顺便申请一个软著.一开始在网上看到有人自主申请成功以后,就想着自己申请软著,但了解到自主申请软著需要一到四个月的时间,而找代理机构却可以最快几天就拿证以后,为避免夜长 ...
- Angular中实现一个简单的toDoList(待办事项)示例代码
场景 Angular介绍.安装Angular Cli.创建Angular项目入门教程: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/detail ...
- python中elif和while简单介绍及注意事项(含笔记)
一.elif 想一想: if能完成当xxx时做事情:if-else能完成当xxx时做事情1,否则做事情2; 如果有这样一种情况:当xxx1时做事情1,当xxx2时做事情2,当xxx3时做事情3,那该怎 ...
最新文章
- 可视化swing界面编辑--转载
- Electron中 提示:Uncaught ReferenceError: process is not defined
- cetk使用方法--nand flash测试
- python 温度 符号_Python通过小实例入门学习---1.0(温度转换)
- php中颜色的索引值,PHP imagecolorsforindex - 取得某索引的颜色
- goldendict设置使用vlc或mplayer发音
- java jdk官网下载地址
- 中国水电基础局携手友勤开展2018年P6软件培训班
- linux下安装Adobe Reader(acroread)
- 非线性控制2.0——鲁棒控制之H无穷控制器设计
- cs229 学习笔记四 学习理论
- Raspberry Pi 4b CUPS AirPrint 共享网络打印机
- 京沪广深同日发布网约车新规细则草案 滴滴回应称或将导致车辆供给骤减
- 【精品整站】WordPress自适应美女写真网站源码/美图整站源码带数据/安装即可运营
- 简单易学又有效的颈椎病自我康复操
- 人工神经网络理论及应用pdf,人工智能的相关书籍
- 困扰了已久的TCP/IP 协议,终于有人讲的明明白白,太强了
- The Evils of Duplication
- RSA非对称加密解密概念
- 如何用VS2017生成可执行文件