`
zhifeiji512
  • 浏览: 115464 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Android使用AIDL设计和调用远程接口

阅读更多

在Android中, 每个应用程序都可以有自己的进程。在写UI应用的时候, 经常要用到Service。 在不同的进程中,怎样传递对象呢? 在Android平台中不允许跨进程内存共享。 因此传递对象, 只能把对象拆分成操作系统能理解的简单形式,所以,他们需要把对象拆分成操作系统能理解的简单形式,以便伪装成对象跨越边界访问。 


写"marshall"的代码是繁琐而枯燥的工作,好在AIDL提供了比较实在的工具以便让它更有趣。 


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


AIDL的IPC机制与COM或CORBA类似,是基于接口,只不过AIDL IPC是轻量级的。 它使用代理类在客户端和实现层间传递值。 


如果要使用AIDL,需要完成2件事情: 


通过AIDL实现IPC 

调用 .aidl (IPC)类 

通过AIDL实现IPC通信 

使用AIDL按照如下步骤实现一个IPC通信服务 


创建.aidl文件 - 在这个文件里面定义接口, 该接口定义了可供客户端访问的方法和属性。 

添加.aidl文件到makefile - (使用Eclipse plugin 管理)。通过Eclipse plugin ,AIDL可以自动产生java并编译,这些都工具位于tools/目录。 

实现AIDL接口方法 - 编译器会根据AIDL接口, 产生一个JAVA接口(并且实现一些必要的附加方法供IPC调用)。 这个接口有一个名为Stub的内部抽象类,你必须创建一个类来扩展这个Stub内部抽象类(Stub内部抽象类名称为接口名称.Stub), 并实现了远程调用需要的几个方法,这些方法定义于 .aidl文件中。 

向客户端开放接口 - 如果写的是Service,应该扩展该Service并重载Service.onBind(Intent)方法 来返回一个实现上述接口的类的实例。 

AIDL语法很简单,可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。 这些参数和返回值可以是任意类型,甚至在其他AIDL生成的接口上也可以这样。 然而, 需要注意的重点 你必须导入所有的non-built-in类型, 即使他们定义的package和你的interface一样也要导入。 AIDL支持的数据类型如下: 


Java编程语言中基本类型(int, boolean, etc) — 不需要 import 再次声明。 

以下的类 (不需要 import 声明): 

String 

List - List中所有的元素都必须是被支持的同一种数据类型中的一个, 包括其他AIDL生成的Interface和Parcelables。 List可以作为泛型类来灵活使用(e.g. List<String>)。 实际上这些类将却是ArrayList,虽然调用的还是名称为List接口。 

Map - Map中所有的元素都必须是被支持同一种数据类型中的一个, 包括其他AIDL生成的Interface和Parcelables。 泛型化的Map, (e.g. 不支持Map<String,Integer> 这种形式)。 实际操作中使用的却是HashMap,不过使用的名字依然是Map接口。 

CharSequence - CharSequence的作用是可以被TextView和其他Widget对象使用。 

其他的,可以根据需要,使用import声明,所以import声明是必须的。 

封装Parcelable 协议 需要使用自定义的类是值传递,所以import 也是必须的。 

下面是AIDL的基本语法: 


// AIDL 文件, 文件名SomeClass.aidl 

// 文件可以有注释 

// 在import和package声明以前的注释将被忽略 

// 但是可以在接口/方法/属性之前添加注释 



// package位置声明 

package com.android.sample; 


// 需要声明的类 

// import引入声明 

import com.android.sample.IAtmService; 


// 接口定义 

interface IBankAccountService { 


// 可以有0到多个参数,可以有0到1个返回值 

int getAccountBalance(); 

void setOwnerNames(in List<String> names); 


// 方法中的参数可以在其他AIDL中被定义 

BankAccount createAccount(in String name, int startingDeposit, in IAtmService atmService); 


// 所有非non-Java原始参数(e.g., int, bool, etc) 都必须 

// 标明参数方向. 有效的参数方向 in, out, inout. (Java原始参数默认是in形式参数,并且没有其他方式)。 

// 限制参数方向是根据实际需要来定,要注意的是不注明参数方向将会耗费大量资源来真理匹配该参数的方向。 

int getCustomerList(in String branch, out String[] customerList); 

实现接口 

AIDL 生成接口的名称与.aidl文件名称是一样的。 如果使用Eclipse插件, AIDL被将会自动创建 (而用不着先运行AIDL工具然后再创建项目)。 如果你还准备通过原始方式来搞,那你只能运行AIDL工具了。 


编译器会根据AIDL接口, 产生一个名为Stub的内部抽象类,它声明的所有方法都将出现在.aidl文件中。 Stub类包含一部分有用的的方法,比如asInterface(),它执行一个IBinder(在 applicationContext.bindService()执行成功后传给客户端onServiceConnected()方法), 并且返回一个接口实例以便调用IPC方法。详情参见Calling an IPC Method。 


接口的大概步骤就是扩展YourInterface.Stub和实现方法。( 你可以创建一个aidl文件并实现stub方法而不用绑定-AndRoid创建过程在java文件之前会处理aidl文件。 ) 


下面是一个实现调用接口IRemoteService的例子,使用匿名实例公开一个简单的方法gerPid(): 


//在同一个项目中不需要import IRemoteService。 

private final IRemoteService.Stub mBinder = new IRemoteService.Stub(){ 

public int getPid(){ 

return Process.myPid(); 

关于实现接口方法的几个规则: 


抛出的异常不要返回给调用者。 

IPC调用是同步的。如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话, 你应该避免在Activity/View 的主线程中调用。 也就是IPC调用会挂起应用程序导致界面失去响应。 这种情况应该考虑单独一个线程来处理。 

只有方法才获得支持,不能在AIDL接口中声明静态属性 

向客户端开放接口 

既然你已经实现接口,现在需要对客户端开放接口的访问。 这就是所谓的 "发布服务" 发布一个服务,就是继承 Service并使用 Service.onBind(Intent)方法 返回到实例的接口实现。 下面这个代码片段是一个服务允许客户端访问的IRemoteService接口。 


public class RemoteService extends Service { 

... 

@Override 

public IBinder onBind(Intent intent) { 

// 选择返回接口,如果只有一个接口,那么直接放回即可 

if (IRemoteService.class.getName().equals(intent.getAction())) { 

return mBinder; 

if (ISecondary.class.getName().equals(intent.getAction())) { 

return mSecondaryBinder; 

return null; 


/** 

* 通过IDL定义IRemoteInterface 

*/ 

private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { 

public void registerCallback(IRemoteServiceCallback cb) { 

if (cb != null) mCallbacks.register(cb); 

public void unregisterCallback(IRemoteServiceCallback cb) { 

if (cb != null) mCallbacks.unregister(cb); 

}; 


/** 

* secondary接口 

*/ 

private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() { 

public int getPid() { 

return Process.myPid(); 

public void basicTypes(int anInt, long aLong, boolean aBoolean, 

float aFloat, double aDouble, String aString) { 

}; 


Parcelables的参数值传递 

如果你有类需要通过AIDL接口从一个进程发送到另一个,你可以使用Parcelables的参数值传递。你必须确保类代码可以被IPC接收端所使用。通常这意味着一开始你就要和service进行通讯。 


要支持Parcelable协议需要注意以下五个部分: 


你创建的类要实现 Parcelable接口。 

通过public void writeToParcel(Parcel out) 方法把当前对象打包。 

通过public void readFromParcel(Parcel in) 方法从包中读取信息到对象中。 

向类中添加一个静态成员CREATOR,该象可以实现Parcelable.Creator接口 

最后(这不是最重要的): 

如果你是使用Eclipse/ADT工具开发, 按照以下步骤来做: 

进入Package Explorer视图模式,右击项目节点。 

选择 Android Tools > Create Aidl preprocess file for Parcelable classes。 

这将在项目根目录创建一个可被调用的"project.aidl" 文件。 它可以自动编译aidl文件为parcelable类。 

如果是使用Ant工具或者其他编译工具,就需要创建一个aidl文件用来定义你的parcelable 类(如下所示)。如果其他编译工具,不需要把aidl文件添加到编译过程中。类似于C语言中的头文件,aidl文件不需要编译。 

AIDL将使用代码中生成的这些方法和成员来伪装或解读对象。 


下面这个例子说明了Rect 类如何实现了Parcelable协议。 


import android.os.Parcel; 

import android.os.Parcelable; 


public final class Rect implements Parcelable { 

public int left; 

public int top; 

public int right; 

public int bottom; 


public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() { 

public Rect createFromParcel(Parcel in) { 

return new Rect(in); 


public Rect[] newArray(int size) { 

return new Rect[size]; 

}; 


public Rect() { 


private Rect(Parcel in) { 

readFromParcel(in); 


public void writeToParcel(Parcel out) { 

out.writeInt(left); 

out.writeInt(top); 

out.writeInt(right); 

out.writeInt(bottom); 


public void readFromParcel(Parcel in) { 

left = in.readInt(); 

top = in.readInt(); 

right = in.readInt(); 

bottom = in.readInt(); 


Rect.aidl示例 


package android.graphics; 


// 定义Rect,AIDL才能找到并且实现parcelable接口协议。 

parcelable Rect; 


Rect类中的伪装是相当简单的。仔细查看Parcel类中的其他方法,,你会看到其他各种值你都可以写进Parcel。 


警告: 不要忽视从其他进程接收数据时的安全性考虑。在本例中,rect将从parcel中读四个数字,而你的工作则是确保这些都在可接受的值得范围内而不管调用者想要干什么。Security and Permissions in Android 中有更多关于如何确保应用程序安全的信息。 


调用一个IPC方法 

调用类调用远程接口的步骤: 


声明一个接口类型的变量,该接口类型在.aidl文件中定义。 

实现ServiceConnection。 

调用Context.bindService(),并在ServiceConnection实现中进行传递。 

在ServiceConnection.onServiceConnected()实现中, 你会收到一些IBinder实例(被调用 service)。 调用 YourInterfaceName.Stub.asInterface((IBinder)service) 将参数转换为YourInterface 类型。 

调用接口中定义的方法。你总会捕捉到 DeadObjectException异常, w该异常在连接断开时被抛出。它只会被远程方法抛出。 

断开连接,调用接口实例中的Context.unbindService()方法。 

调用IPC服务需要注意几点: 


通过过程对对象引用的统计。 

匿名对象可以通过方法参数发送。 

下面的代码展示了在ApiDemos项目从远程Activity例子中调用AIDL创建Service的过程。 



public class RemoteServiceBinding extends Activity { 

/** 初始化主要接口*/ 

IRemoteService mService = null; 

/** 其他接口 */ 

ISecondary mSecondaryService = null; 


Button mKillButton; 

TextView mCallbackText; 


private boolean mIsBound; 


/** 

* 标准的activity初始化,设置UI 

*/ 

@Override 

protected void onCreate(Bundle savedInstanceState) { 

super.onCreate(savedInstanceState); 


setContentView(R.layout.remote_service_binding); 


//按钮点击事件监听 

Button button = (Button)findViewById(R.id.bind); 

button.setOnClickListener(mBindListener); 

button = (Button)findViewById(R.id.unbind); 

button.setOnClickListener(mUnbindListener); 

mKillButton = (Button)findViewById(R.id.kill); 

mKillButton.setOnClickListener(mKillListener); 

mKillButton.setEnabled(false); 


mCallbackText = (TextView)findViewById(R.id.callback); 

mCallbackText.setText("Not attached."); 


/** 

* 类与main函数交互 

*/ 

private ServiceConnection mConnection = new ServiceConnection() { 

public void onServiceConnected(ComponentName className, 

IBinder service) { 

// 连接建立时被调用的服务,客户端可以通过IDL接口与该Service通信。 


mService = IRemoteService.Stub.asInterface(service); 

mKillButton.setEnabled(true); 

mCallbackText.setText("Attached."); 


// 监视服务 

try { 

mService.registerCallback(mCallback); 

} catch (RemoteException e) { 

// 异常抛出,服务崩溃之前可以做的一些事情; 


// 回显信息 

Toast.makeText(RemoteServiceBinding.this, R.string.remote_service_connected, 

Toast.LENGTH_SHORT).show(); 


public void onServiceDisconnected(ComponentName className) { 

// 服务异常断开连接 -- 比如进程崩溃 

mService = null; 

mKillButton.setEnabled(false); 

mCallbackText.setText("Disconnected."); 


// 回显信息 

Toast.makeText(RemoteServiceBinding.this, R.string.remote_service_disconnected, 

Toast.LENGTH_SHORT).show(); 

}; 


/** 

* 类与secondary接口的交互. 

*/ 

private ServiceConnection mSecondaryConnection = new ServiceConnection() { 

public void onServiceConnected(ComponentName className, 

IBinder service) { 

// 连接一个Secondary与连接其他接口的方法一样 

mSecondaryService = ISecondary.Stub.asInterface(service); 

mKillButton.setEnabled(true); 


public void onServiceDisconnected(ComponentName className) { 

mSecondaryService = null; 

mKillButton.setEnabled(false); 

}; 


private OnClickListener mBindListener = new OnClickListener() { 

public void onClick(View v) { 

// 界面和代码动作绑定之后,允许其他应用通过远程服务的方式调用该组件,已完成相同的重复的工作 


bindService(new Intent(IRemoteService.class.getName()), 

mConnection, Context.BIND_AUTO_CREATE); 

bindService(new Intent(ISecondary.class.getName()), 

mSecondaryConnection, Context.BIND_AUTO_CREATE); 

mIsBound = true; 

mCallbackText.setText("Binding."); 

}; 


private OnClickListener mUnbindListener = new OnClickListener() { 

public void onClick(View v) { 

if (mIsBound) { 

// 注销服务 

if (mService != null) { 

try { 

mService.unregisterCallback(mCallback); 

} catch (RemoteException e) { 

// 异常信息处理,不过一般情况没什么需要做的 


//断开连接 

unbindService(mConnection); 

unbindService(mSecondaryConnection); 

mKillButton.setEnabled(false); 

mIsBound = false; 

mCallbackText.setText("Unbinding."); 

}; 


private OnClickListener mKillListener = new OnClickListener() { 

public void onClick(View v) { 

// 需要获知进程的PID才能结束本地服务, 

// 好在我们的服务可以方便的返回一些有用的信息 

if (mSecondaryService != null) { 

try { 

int pid = mSecondaryService.getPid(); 

// 使用这个API可以结束任何已知PID的进程 

// 比如只运行一个进程的应用,在运行过程中,会产生一些附加进程,这些进程会共享一个UID, 

// 结束这个UID,那么其他的附加进程也会被一起结束 


Process.killProcess(pid); 

mCallbackText.setText("Killed service process."); 

} catch (RemoteException ex) { 

// 进程结束失败,显示一个失败通知 


Toast.makeText(RemoteServiceBinding.this, 

R.string.remote_call_failed, 

Toast.LENGTH_SHORT).show(); 

}; 


// ---------------------------------------------------------------------- 

// 回调示例代码 

// ---------------------------------------------------------------------- 


/** 

* 实现远程服务的回调 

*/ 

private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() { 

/** 

* 被调用的远程服务定时返回新的值。 

*/ 

public void valueChanged(int value) { 

mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0)); 

}; 


private static final int BUMP_MSG = 1; 


private Handler mHandler = new Handler() { 

@Override public void handleMessage(Message msg) { 

switch (msg.what) { 

case BUMP_MSG: 

mCallbackText.setText("Received from service: " + msg.arg1); 

break; 

default: 

super.handleMessage(msg); 


}; 

}

分享到:
评论

相关推荐

    Android工作实践总结:Aidl 远程调用(aidl实例总结)

    Android工作实践总结:Aidl 远程调用(aidl实例总结) AIDL的IPC的机制和COM或CORBA类似, 是基于接口的,但它是轻量级的。它使用代理类在客户端和实现层间传递值. 如果要使用AIDL, 需要完成2件事情: 1. 引入AIDL的...

    Android中如何利用AIDL机制调用远程服务

    在Android中,则采用AIDL(Android Interface DefinitionLanguage:接口描述语言)方式实现。 AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC)。...

    AIDL最简单的使用步骤

    为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface ...

    AIDL示例(Android Interface Definition Language)

    为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface ...

    android 3个APK见的AIDL 通信

    实现B为一个主程序,A远程调用B,并对外提供给C一个接口,实现对B函数的笤俑

    实例讲解Android中的AIDL内部进程通信接口使用

    首先描述下我们想要实现的内容,我们希望在一个应用中通过点击按钮,去操作另一个进程中应用的音乐播放功能。 如图,我们点击“播放”时,系统就会去远程调用我们提供的一...AIDL(android interface definition lan

    Android应用程序四大组件之使用AIDL如何实现跨进程调用Service

    一、问题描述  Android应用程序的四大组件中Activity、BroadcastReceiver、...与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。对于Se

    aidl使用实例

    包括两个工程,一个是定义aild文件的app,即远程app,一个是调用远程接口的app

    Android Service的跨进程通信实战&Service/AIDL远程调用过程解析(Android Q)

    设想这么一个场景,我们有2个APP(或者2个进程,均可),其中一个APP需要提供一个Person相关的服务(该服务有一个名为eat的接口),我们叫它PersonServer;另一个APP需要访问PersonServer提供的服务,我们叫它Client。...

    Android Service

    2、远程服务:调用远程服务的代理提供的接口可以实现调用远程服务中的方法。(需要将代理接口.java扩展名改为.aidl,继承接口的实现类Stub),另外需要将远程服务中的aidl包名和文件拷贝到本地应用程序的工程中。 ...

    Android远程服务编写和调用教程

    网上汗牛充栋的文章都是介绍Android远程服务的,一个个将Binder机制、AIDL讲得头头是道,然而没有几个人能够给出清晰的范例说明如何用最快的方法学会编写和调用一个Android远程服务。若你仅仅是想如何编写或者调用...

    HBriqAIDLExample:Android AIDL示例

    HBriqAIDL示例 Android AIDL示例 源组件: app:AIDL服务部分hbriqaidlclient:...debug.apk 证实: -启动hbriqaidlclient应用-“连接”以连接服务-“ getToken”以将getToken应用程序调用到远程服务-hbriqAidlService

    精通ANDROID 3(中文版)1/2

    1.5 使用Android SDK开发最终用户应用程序  1.5.1 Android模拟器  1.5.2 Android UI  1.5.3 Android基础组件  1.5.4 高级UI概念  1.5.5 Android Service组件  1.5.6 Android媒体和电话组件  1.5.7 ...

    Android开发案例驱动教程 配套代码

    13.3.5 调用远程Service 336 13.3.6 组件间参数传递 343 本章小结 347 第14章 Broadcast Receiver和Notification 348 14.1 Broadcast Receiver 348 14.1.1 音频播放案例 349 14.1.2 编写音频播放Broadcast ...

    Android 代码分析

    3 Android JAVA AIDL Analysis 15 4 Android JAVA Binder IPC System 20 5 Android Media Scanner Process 27 5.1 JAVA layer initialize 27 5.2 JAVA layer prescan 28 5.3 C++ layer processDirectory 28 5.4 JAVA...

    精通Android 3 (中文版)2/2

    1.5 使用Android SDK开发最终用户应用程序  1.5.1 Android模拟器  1.5.2 Android UI  1.5.3 Android基础组件  1.5.4 高级UI概念  1.5.5 Android Service组件  1.5.6 Android媒体和电话组件  1.5.7 ...

    android的服务

    Android提供了一个 AIDL (Android接口定义语言)工具 来处理序列化和通信。这种情况下Service需要以aidl文件的方式提供服务接口,AIDL工具将生成一个相应的java接口,并且在生成的服务接口中包含一个功能调用的stub...

    Android典型技术模块开发详解

    6.3.1 定义AIDL接口 6.3.2 使用AIDL开发程序的一般步骤 6.3.3 实现远程控制计数器示例 6.4 AIDL深入练习 6.4.1 服务端实现 6.4.2 客户端实现 6.5 系统服务 6.5.1 获得系统服务 6.5.2 获取屏幕分辨率 6.5.3 剪贴板...

    详解Android Service 使用时的注意事项

    最近有个项目刚好使用了Service,特别是AIDL远程服务,经过这次项目对Service有了更好的理解,在这里作个总结。 startService / bindService 混合使用 每一次调用 startService 都会回调onStartCommand,之后调用了...

Global site tag (gtag.js) - Google Analytics