Android程序设计之AIDL实例详解

通常来说,AIDL这项技术在我们的应用开发过程中并不是很常用,虽然新浪微博提供了SSO登录,但是其原理就是使用AIDL。本文就以完整的实例形式讲述了AIDL的原理及实现方法。

AIDL(AndRoid接口描述语言)是一种借口描述语言; 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一个Activity中, 访问另一个Service中的某个对象, 需要先将对象转化成 AIDL可识别的参数(可能是多个参数), 然后使用AIDL来传递这些参数, 在消息的接收端, 使用这些参数组装成自己需要的对象.

说白了,AIDL就是定义一个接口,客户端(调用端)通过bindService来与远程服务端简历一个连接,在该连接建立时会将返回一个IBinder对象,该对象是服务端Binder的BinderProxy,在建立连接时,客户端通过asInterface函数将该BinderProxy对象包装成本地的Proxy,并将远程服务端的BinderProxy对象赋值给Proxy类的mRemote字段,就是通过mRemote执行远程方法调用。需要对Binder机制有更深的理解,请参考老罗的Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析。下面我们看一个AIDL实例。

AIDL接口声明

在src目录下创建一个com.example.advanceandroid.aidl包,然后在该包下创建一个ILogin.aidl文件,注意是创建文件而不是类或者接口类型。在ILogin.aidl中声明接口,实例如下 :

package com.example.advanceandroid.aidl; interface ILogin { String login(); }

注意看,接口和方法声明都不用public,方法加入public会提示错误。编写完后如果eclipse开启了自动编译则会在gen/com.example.advanceandroid.aidl下生成一个ILogin.java类,内容大致如下:

package com.example.advanceandroid.aidl; public interface ILogin extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.advanceandroid.aidl.ILogin { private static final java.lang.String DESCRIPTOR = "com.example.advanceandroid.aidl.ILogin"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.advanceandroid.aidl.ILogin * interface, generating a proxy if needed. */ public static com.example.advanceandroid.aidl.ILogin asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.advanceandroid.aidl.ILogin))) { return ((com.example.advanceandroid.aidl.ILogin) iin); } return new com.example.advanceandroid.aidl.ILogin.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_login: { // 1、登录请求,执行的是this.login(); data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.login(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.advanceandroid.aidl.ILogin { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public java.lang.String login() throws android.os.RemoteException // 2、Proxy中的login,通过Binder机制实现IPC { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public java.lang.String login() throws android.os.RemoteException; }

可以看到,该类中自动生成了ILogin接口,该接口中又一个login()函数。但最重要的是里面生成了一个Stub类,该类集成子Binder类,并且实现了ILogin接口。Stub里面最重要的就是asInterface()这个函数,在这个函数中会判断obj参数的类型,如果是该obj是本地的接口类似,则认为不是IPC,会将该obj转换成ILogin类型;否则会通过自动生成的另一个内部类Proxy来包装obj,将其赋值给Proxy中的mRemote属性。Proxy类也实现了ILogin接口,在login()函数中,Proxy将通过Binder机制向服务端传递请求和数据,如上面代码中的注释2。这是客户端的工作算是完成了。

服务端AIDL接口

服务端也需要在相同的包下创建同名的aidl文件,我们直接将客户端的com.example.advanceandroid.aidl包下的ILogin.aidl拷贝到服务端即可,如果用到了自定义的类型,那么该自定义类型也需要在客户端、服务端都有。拷贝完aidl后,在服务端程序中也会在gen中生成对应的ILogin.java文件,内容同客户端一样。这里的重点我们要看onTransact函数,即上述代码中的注释1处,可以看到,在case TRANSACTION_login处执行了this.login()函数,意思是当接收到客户端的TRANSACTION_login请求时,执行this.login()函数,通过客户端的分析我们知道,当我们调用login()时实际上就是通过mRemote向服务端提交了一个TRANSACTION_login请求,因此就两端通过Binder机制就对接上了,我们可以简单的理解为C/S模式。

服务端还没有完,最重要的一步时建立一个Service,内容大致如下 :

/** * AIDL服务端接口,LoginStubImpl实现了ILogin接口. * * @author mrsimple */ public class LoginService extends Service { /** * */ IBinder mBinder = new LoginStubImpl(); /** * @author mrsimple */ class LoginStubImpl extends Stub { @Override public String login() throws RemoteException { return "这是从 " + this.getClass().getName() + " 返回的字符串"; } } /* * 返回Binder实例,即实现了ILogin接口的Stub的子类,这里为LoginStubImpl * [url=home.php?mod=space&uid=133757]@see[/url] android.app.Service#onBind(android.content.Intent) */ @Override public IBinder onBind(Intent intent) { return mBinder; } }

该Service我们这里命名为LoginService,继承自Service,然后建一个名为LoginServiceImpl的内部类,该类继承自自动生成的Stub,然后实现login()方法。在LoginService中声明一个IBinder字段mBinder :

IBinder mBinder = new LoginStubImpl();

并且在LoginService的onBind函数中将mBinder对象返回。即在客户端建立与服务端的连接时,会调用onBind方法将mBinder对象返回,在客户端的ServiceConnection类的onServiceConnected函数中得到的对象IBinder就是经过BinderProxy包装的LoginService中的mBinder对象。因此在服务端中的onTransact中调用的this.login()函数实际上就是调用的LoginStubImpl中的login()函数。

在服务端程序的AndroidManifest.xml中注册LoginService,如下 :

<!-- aidl server service --> <service android:name="com.example.advanceandroid.aidl.LoginService" > <intent-filter> <action android:name="com.example.advanceandroid.aidl.LoginService" /> </intent-filter> </service>

客户端建立连接

在Activity中加入如下代码 :

ServiceConnection mLoginConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { Log.d("", "### aidl disconnected."); } @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d("", "### aidl onServiceConnected. service : " + service.getClass().getName()); ILogin login = Stub.asInterface(service); Log.d("", "### after asInterface : " + login.getClass().getName()); try { Log.d("", "### login : " + login.login()); // Toast.makeText(MainActivity.this, "onServiceConnected : " + // login.login(), // Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } } }; @Override protected void onResume() { super.onResume(); // 服务端的action Intent aidlIntent = new Intent("com.example.advanceandroid.aidl.LoginService"); bindService(aidlIntent, mLoginConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // unbind unbindService(mLoginConnection); }

运行

先运行服务端程序,然后在启动客户端程序,可以看到客户端输出如下Log:

09-02 10:40:54.662: D/(9589): ### aidl onServiceConnected. service : android.os.BinderProxy 09-02 10:40:54.662: D/(9589): ### after asInterface : com.example.advanceandroid.aidl.ILogin$Stub$Proxy 09-02 10:40:54.662: D/(9589): ### login : 这是从 com.example.advanceandroid.aidl.LoginService$LoginStubImpl 返回的字符串

可以看淡onServiceConnected(ComponentName name, IBinder service)中的service对象是BinderProxy类型,经过asInterface转换后被包装成了Proxy类型,但是调用的时候,执行的是服务端LoginStubImpl中的login()函数。因此,LoginStubImpl实例mBinder被服务端包装成BinderProxy类型,再经过客户端的Proxy进行包装,通过Binder机制进行数据传输,实现IPC。

希望本文所述对大家进一步深入掌握Android程序设计有所帮助。

时间: 2024-08-02 20:24:17

Android程序设计之AIDL实例详解的相关文章

Android程序设计之AIDL实例详解_Android

通常来说,AIDL这项技术在我们的应用开发过程中并不是很常用,虽然新浪微博提供了SSO登录,但是其原理就是使用AIDL.本文就以完整的实例形式讲述了AIDL的原理及实现方法. AIDL(AndRoid接口描述语言)是一种借口描述语言; 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一个Activity中, 访问另一个Service中的某个对象, 需要先将对象转化成 AIDL可识别的参数(可能是多个参数), 然后使用AIDL来传递这些参数,

Android编程之SurfaceView实例详解_Android

本文实例讲述了Android编程之SurfaceView用法.分享给大家供大家参考,具体如下: 关于surfaceView相关知识: View和SurfaceView主要区别: 1. View只能在UI线程中刷新,而SurfaceView可以在子线程中刷新 2. SurfaceView可以控制刷新频率 SurfaceView几个重要的方法: 1. 继承SurfaceView 后调用getHolder()方法可以获取到mSurfaceHolder对象这个对于可以控制SurfaceView的绘制 2

android 显示gif图片实例详解

  android 显示gif图片实例详解           在android中不支持gif格式的图片,但是由于我希望在我的程序中刚刚加载的时候有一个小人在跑步表示正在加载.而这个小人跑就是一个gif图片.也就是希望程序一启动时就加载gif图片.在网上查找了一些方法不知道是我使用的android的版本高(android4.4)还是什么问题就是加载不出来.最后想了一个办法加载了出来.这个办法就是将gif放在webView中让其显示. 下面是关于这个的代码: activity_prepare_fu

Java中的instanceof关键字在Android中的用法实例详解_java

在下面介绍Android中如何使用instanceof关键字开发更方便时,先来温习一下java中instanceof的概念. instanceof大部分的概念是这样定义的:instanceof是Java的一个二元操作符,和==,>,<是同一类东西.由于它是由字母组成的,所以也是Java的保留关键字.它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据.举个栗子: String s = "I AM an Object!"; boolean isObj

Android的搜索框架实例详解_Android

基础知识 Android的搜索框架将代您管理的搜索对话框,您不需要自己去开发一个搜索框,不需要担心要把搜索框放什么位置,也不需要担心搜索框影响您当前的界面.所有的这些工作都由SearchManager类来为您处理(以下简称"搜索管理器"),它管理的Android搜索对话框的整个生命周期,并执行您的应用程序将发送的搜索请求,返回相应的搜索关键字. 当用户执行一个搜索,搜索管理器将使用一个专门的Intent把搜索查询的关键字传给您在配置文件中配置的处理搜索结果的Activity.从本质上讲

Android 判断网络状态实例详解

Android 判断网络状态实例详解 实例代码 package com.example.android; import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.URL; import java.util.

Android的搜索框架实例详解

基础知识 Android的搜索框架将代您管理的搜索对话框,您不需要自己去开发一个搜索框,不需要担心要把搜索框放什么位置,也不需要担心搜索框影响您当前的界面.所有的这些工作都由SearchManager类来为您处理(以下简称"搜索管理器"),它管理的Android搜索对话框的整个生命周期,并执行您的应用程序将发送的搜索请求,返回相应的搜索关键字. 当用户执行一个搜索,搜索管理器将使用一个专门的Intent把搜索查询的关键字传给您在配置文件中配置的处理搜索结果的Activity.从本质上讲

Android 帧动画的实例详解

Android 帧动画的实例详解 对于 Android 帧动画 大体上可以理解成 一张张图片 按一定顺序切换, 这样当连续几张图是一组动画时,就可以连起来了看成是一个小电影,你懂得 好得,比就装到这里,下面开始进入正题,由于产品需求 需要做一个 声音喇叭动态切换的样式,我特么第一就想到是帧动画切换,然后就百度了一些资料,发现 真的, 现在这个网上太多的资料是 copy粘贴过来的, 一错全错,对于这种情况我只想说,made,一群垃圾, 所以今天我将带你们走进Android 正确帧动画地址. 第一步

Android开发之Android.mk模板的实例详解

Android开发之Android.mk模板的实例详解 关于Android NDK开发的文章已经比较多了,我的博客中也分享了很多NDK开发相关经验和技巧,今天简单写了一个 Android.mk 的示例模板,供初学者参考. 本模板主要给大家示例 Android NDK 开发中的如下几个问题: 1. 如何自动添加需要编译的源文件列表   2. 如何添加第三方静态库.动态库的依赖   3. 如何构造一个完整的NDK工程框架 假设我们的项目依赖 libmath.a, libjson.a, libffmp