本文主要介绍了Android跨进程IPC通信的AIDL机制的原理,并详细介绍了AIDL的概念和使用,具有一定的参考价值。有兴趣的可以看看。
简介
AIDL: Android接口定义语言,即Android接口定义语言,用于生成不同Android进程间进程间通信(IPC)的代码。通常,一个进程不能访问另一个进程的内存。在某些情况下,如果仍然需要跨进程访问内存数据,那么Android系统将不得不将其对象分解为可识别的原始数据。写这组操作的代码是一项繁琐的工作,但AIDL抽象了底层,简化了跨进程操作。
Aidipc机制是面向接口的,像COM或Corba,但更轻量级。它使用代理类在客户端和实现之间传输数据。
Android中跨进程操作的方式不止一种。在这四个组件中,ContentProvider是为跨进程操作而存在的。但是ContentProvider所谓的跨进程操作数据并不一定存储在内存中,比如存储在Sqlite数据库中的通讯录数据。AIDL支持的跨进程操作的数据存储在内存中,AIDL的底层实际上是使用的绑定器进行的跨进程操作。另一篇博文继续介绍Binder的跨进程机制。
使用场景
只有当不同应用程序之间需要IPC,并且您希望在服务中处理多线程时,才有必要在这种情况下使用AIDL。如果只需要跨流程而不需要跨应用,那么数据交互应该通过Binder进行。另外,如果只需要跨进程的IPC,但不需要处理多线程,就要通过Messenger类来交换数据。
定义AIDL接口
Android Studio中使用AIDL的项目的目录结构与eclipse中的目录结构有很大不同。下图显示了使用AIDL的项目的目录结构。
在Android Studio中,只需要使用某个模块中的右键菜单,就可以显示创建aidl文件的菜单。当新文件成功创建后,AIDL文件位于项目中与java同级的AIDL目录文件夹下。aidl接口存储在。AIDL档案。
定义.aidl文件
的名称。AIDL文件必须与接口名一致,而aidl文件必须使用Java语言的语法来定义。AIDL使用简单的语法通过一个或多个可以接受参数和返回值的方法来声明接口。参数和返回值可以是任何类型,甚至可以是其他AIDL生成的接口。每一个。aidl文件必须定义单个接口,它只需要包含接口声明和方法签名,这意味着权限修饰符不能用于。aidl文件。
默认情况下,AIDL支持以下数据类型:
Java中所有的基本数据类型,如char、boolean和byte、short、int、long、float和double;
和CharSequence类型;
列表类型,列表中的所有元素必须是上述列表中受支持的数据类型、其他AIDL生成的接口或您声明的可打包类型。您可以选择使用List作为泛型类型(例如,List)。另一端实际收到的具体类总是ArrayList,但是生成的方法使用的是List接口。在AIDL,不能定义ArrayList类型,只能使用List接口,否则会报错未知类型编译错误。
映射类型,其中的所有元素必须是上述列表中支持的数据类型,由其他AIDL生成的接口,或您声明的可打包类型。与收藏列表界面不同,AIDL不支持通用地图(例如地图形式的地图)。另一端实际收到的具体类总是HashMap,但是生成的方法使用Map接口。类似于List接口,在。aidl文件,只能使用地图界面。
自定义类型必须实现Parcelable接口,aidl文件夹下有对应类型的aidl文件;
JDK中没有定义的类型,类似于Java语法,必须使用import引入。
定义AIDL接口时需要注意如下:
方法可带零个或多个参数,返回值或空值,但是方法名称不能相同;
所有非基本数据类型参数都需要指示数据走向的方向标记。可以是进、出或inout。基本数据类型默认为在,不能是其他方向;aidl文件中包括的所有代码注释都包含在生成的伊宾德接口中(导入和包裹语句之前的注释除外);
只支持方法,不应在AIDL中定义静态字段。
如下是定义是IRemoteService.aidl:
包com。阳光明媚。服务器;
导入com。阳光明媚。服务器。比恩。用户;
接口IRemoteService {
void basicTypes(int anInt,long aLong,boolean aBoolean,float aFloat,
double aDouble,String String);
string getName();
void setListData(in list in list,out list string outList);
void setMapData(在地图地图中);
void setUser(在用户用户中);
}
下面是自定义的JavaBean类型User.aidl
包com。阳光明媚。服务器。豆;
可打包用户;
用户类在使用的时候必须实现可包装的接口,代码这里就不再贴出来了。
Service实现AIDL接口
在定义的AIDL接口编译后实际上会生成一个跟100 . aidl同名的Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)类文件,里面包含了所有的AIDL文件中声明的方法,并且包含了一个默认的实现类存根,该类是抽象类,继承了粘合剂类实现了AIDL接口。在烟蒂类中有两个方法一个是asInterface()方法,该方法返回的是AIDL文件生成的接口,另外一个方法是asBinder(),该方法返回的是一个伊宾德类型的实例。
asInterface()和asBinder()方法非常有用,作为接口()方法可以用于客户端的工业程序控制(工业过程控制的缩写)方法调用,另外一个方法可以用于在服务端返回粘合剂实例,并在服务端实现响应的接口方法。
上面介绍过在定义非基本数据类型的时候必须定义数据走向,声明在或在外或者inout,在AIDL生成的Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)文件中就可以看出来究竟了,这里可以参看setListData()方法的生成实现。
@覆盖
公共空集合列表数据(Java。util。列出Java。郎。列表中的字符串。util。列出Java。郎。字符串大纲列表)抛出android.os.RemoteException {
安卓。OS。package _ data=Android。OS。包裹。获取();
安卓。OS。包裹_回复=安卓。OS。包裹。获取();
尝试{
_data.writeInterfaceToken(描述符);
_ data。writestringlist(inList);
mRemote.transact(存根TRANSACTION_setListData,_data,_reply,0);
_回复。读取异常();
_回复。读取字符串列表(outList);
}最后{
_回复。recycle();
_ data。recycle();
}
}
如果声明数据时是在,在生成相对应的方法的时候调用的实际上是包裹的写XXX方法,如果声明的是出去,在实现上面采用的是readXXX,所以在定义的时候一定要明确调用逻辑。
接下来看一下服务端中我的服务类的实现。
公共类我的服务扩展服务{
私有静态最终字符串AIDL服务器;
@Nullable
@覆盖
公共IBinder onBind(意图意图){
返回mBinder
}
私有远程服务.存根mBinder=new IRemoteService .存根(){
@覆盖
public void基本类型(int anInt、long aLong、boolean aBoolean、float aFloat、double aDouble、String aString)抛出RemoteException {
Log.d(TAG,' anInt:' anInt ' aLong:' aLong ' aboo lean:' aboo lean ' flower:' flower ' a double:' a double ' astrin:' astrin ');
}
@覆盖
公共字符串getName()引发RemoteException {
返回”管理”;
}
@覆盖
public void setListData(ListString in list,ListString outList)引发RemoteException {
Log.d(标记,'在列表中:'在列表中。tostring());
设置列表(outList);
}
@覆盖
public void setMapData(Map map)引发RemoteException {
Log.d(标签,' map:' map。tostring());
}
@覆盖
公共void setUser(用户User)引发RemoteException {
Log.d(标签,'用户:'用户。tostring());
}
};
私有void抵销列表(列表字符串列表){
列表。add(' out _ 01 ');
列表。add(' out _ 02 ');
列表。add(' out _ 03 ');
}
}
在MyService类中,除了getName是一个有返回值的方法,其他方法都是void类型。另外,在数据趋势方面,除了setListData方法的第二个参数outList是一个输出类型参数外,其他参数都是输入类型参数,所以这里直接打印其他参数。
调用IPC方法
当客户端想要调用Android AIDL中定义的IPC方法时,可以通过以下步骤实现:
首先需要定义一个包名相同、目录相同的AIDL文件夹;
声明一个由AIDL文件生成的接口实例;
实现ServiceConnection接口;
调用bindService绑定服务,传入生成的ServiceConnection实例;
在onServiceConnected()实现中,将接收IBinder(命名服务)的一个实例。调用XXX . stub . as interface((I binder)service)将返回的参数转换为AIDL生成的接口类型。
IPC调用可以通过调用生成的AIDL接口实例中相应的方法来实现;
不使用时解除服务上下文的绑定。unbindService()。
下面是代码在客户端活动中的实现:
公共类MainActivity扩展AppCompatActivity {
私有静态最终字符串TAG=' AIDL _客户端';
专用MyConnection连接器;
私有IRemoteService服务;
private ListString outList=new ArrayList();
@覆盖
受保护的void onCreate(Bundle saved instancestate){
super . oncreate(savedInstanceState);
setContentView(r . layout . activity _ main);
}
public void startBind(视图v) {
Intent Intent=new Intent();
conn=new my connection();
intent . set action(' com . sunny . server . service . my service ');
bindService(意图,连接,上下文。BIND _ AUTO _ CREATE);
}
public void startExecute(视图v) {
尝试{
service.basicTypes(1,10000L,true,1.5f,300.3,' Hello World ');
Log.d(TAG,' getName:' service . getName());
ListString in list=new ArrayList string();
in list . add(' in list 01 ');
inlist . add(' inlist 02 ');
service.setListData(inList,outList);
Log.d(TAG,' outList:' outList . tostring());
MapString,String map=new HashMapString,String();
map.put('key01 ',' value 01 ');
map.put('key02 ',' value 02 ');
service.setMapData(地图);
User User=new User();
user . setid(1001);
user . set name(“admin”);
service.setUser(用户);
} catch(远程异常e) {
e . printstacktrace();
}
}
私有类MyConnection实现ServiceConnection {
公共void onServiceConnected(component name name,IBinder binder) {
service=IRemoteService。Stub.asInterface(活页夹);
}
public void onServiceDisconnected(component name name){
}
}
@覆盖
受保护的void onDestroy() {
super . on destroy();
unbind service(conn);
}
}
其它
上面的例子只是为了介绍AIDL是如何跨进程通信的,所以客户端收到的数据直接在主线程中处理。但实际上客户端调用的是服务器的远程方法,被调用的方法运行在服务器的Binder线程池中,同时客户端线程会被挂起。此时,如果服务器方法需要时间来执行,客户端将被长时间阻塞在这里,如果客户端方法位于UI线程中,可能会导致ANR。在实际开发中,注意客户端在与IPC通信时,尽量放在子线程中。因为服务器端方法本身运行在服务器端绑定器线程池中,所以即使服务器端需要执行大量耗时的工作,也不需要启动新的线程来执行。
另一个需要注意的重要问题是安全性。默认情况下,任何人都可以连接到远程服务,这应该不是我们需要的,所以我们还需要考虑权限验证。一般有两种处理方式。第一种方法是自定义权限。我们在服务器端给服务方法的onBinder()方法添加权限验证,如果权限验证失败直接返回null。另一种是在服务器的onTransact()方法中验证,也是验证权限。如果失败,直接返回false。除了上面提到的权限验证,还可以通过getCallingPid()和getCallingUid()获取客户端应用的Pid和Uid进行验证。
AIDL的介绍到此结束。下面我们继续介绍Binder的相关内容。这就是本文的全部内容。希望对大家的学习有帮助,支持我们。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。