`
zhifeiji512
  • 浏览: 119748 次
  • 性别: 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文件不一致引起的调用出错问题.doc

    AIDL 文件是用于定义服务端和客户端之间的接口,用于实现远程过程调用(RPC)。但是, 当服务端和客户端的 AIDL 文件不一致时,可能会引起调用出错的问题。 在 Android 应用程序中,AIDL 文件用于定义服务端和...

    Android使用AIDL完成简单的远程加法计算示例

    在上述示例中,我们展示了如何使用AIDL来实现一个简单的远程加法计算,这只是一个基本的示例,实际应用中的AIDL可能会涉及更复杂的接口和数据类型。理解和掌握AIDL对于开发Android系统的大型、多组件应用至关重要。

    Android使用AIDL完成自定义对象的远程传递示例

    AIDL文件是一种接口定义文件,它允许我们声明方法签名,这些方法将在服务和客户端之间使用。当我们定义了一个接口,Android会自动生成相应的Java代理类,供客户端和服务端使用。 1. 创建AIDL文件 在项目中创建一个...

    android简单AIDL调用Demo

    在"android简单AIDL调用Demo"中,我们将深入探讨如何使用AIDL创建和调用跨进程服务。 首先,让我们了解AIDL的基本结构。AIDL文件是一个简单的文本文件,后缀为.aidl,它定义了接口以及接口中的方法。例如,如果你有...

    android aidl远程方法调用例子

    总结起来,AIDL是Android中实现跨进程通信的重要工具,它允许应用之间通过定义清晰的接口进行数据交换和服务调用,从而实现了进程间的协作。在实际开发中,理解并熟练掌握AIDL能够帮助我们构建更加健壮和可扩展的...

    android 进程之间通信--Android 使用【AIDL】调用外部服务

    AIDL文件本质上是文本文件,使用类似于Java的方法签名来定义接口,然后Android编译器会自动生成对应的Binder类和接口实现,以处理跨进程通信的细节。 **AIDL的使用步骤** 1. **创建AIDL文件** 首先,在Android项目...

    Android(AIDL)调用远程服务

    现在,让我们深入了解Android AIDL调用远程服务的详细知识。 首先,AIDL的基本概念是定义一个接口文件,这个文件包含了客户端与服务端之间交互所需要的方法声明。例如,我们可以创建一个名为`IService.aidl`的文件...

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

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

    Android使用.aidl远程调用

    本示例通过"AidlService"和"AidlClient"两个应用展示了如何利用.aidl进行远程调用,实现不同应用间的交互。 首先,我们来看服务端(Service)部分——`AidlService`。在这个项目中,`AidlService`会声明一个公开的...

    android AIDL 多个客户端调用一个服务器代码

    在客户端,首先需要通过`IBinder`获取服务端的接口实例,然后就可以像调用本地对象一样调用远程服务的方法。这通常通过`bindService()`和`onServiceConnected()`回调完成: ```java private IMyService myService...

    AIDL 远程调用实例

    **Android Interface Definition Language (AIDL) 远程调用实例详解** 在Android系统中,当一个应用程序需要与另一个运行在不同进程或者不同设备上的应用程序进行交互时,就需要使用到远程通信技术。Android提供了...

    Android调用远程服务(AIDL)

    Android调用远程服务(AIDL, Android Interface Definition Language)就是一种这样的机制,它允许开发者定义接口,使得客户端应用能够与运行在另一个进程中(即远程服务端)的应用组件进行通信。下面我们将详细探讨...

    android 使用AIDL和远程服务实现进程通信的学习

    在Android系统中,进程间通信...总结,使用AIDL和远程服务进行Android进程通信,能有效地解决应用间的协作问题,但需要注意其带来的性能影响。通过实践和理解这些知识点,开发者可以构建更加复杂和健壮的Android应用。

    android_aidl例子

    4. **进程间通信**:客户端通过IBinder接口的`asInterface()`方法获取到远程服务的代理对象,然后就可以调用服务的方法,实现在不同进程间的数据传递。 在"22_LoL游戏"项目中,AIDL的应用可能包括: 1. **游戏状态...

    Android使用AIDL实现跨进程通信

    服务端使用服务端代理类来暴露接口,客户端则通过Stub类来调用远程服务的方法。 4. **服务端实现** 在服务端,我们需要实现AIDL定义的接口,并在`onBind()`方法中返回服务端代理对象。例如: ```java public ...

    Androidaidl跨进程调用.zip

    这个"Androidaidl跨进程调用.zip"文件可能包含了一些关于如何在Android应用程序中使用AIDL进行跨进程调用的示例和教程。 首先,我们需要理解Android进程的概念。每个Android应用运行在自己的进程中,不同的应用进程...

    aidl.rar_AIDL_AIDL.rar_android AIDL service_android service_andr

    因此,应谨慎设计服务的接口,避免频繁的AIDL调用。 9. **安全性**: 使用AIDL的服务暴露了其接口给其他应用,可能带来安全风险。开发者需要确保只有授权的应用才能访问服务,可以通过AndroidManifest.xml中的权限...

    aidl调用远程服务

    本篇文章将深入探讨如何使用AIDL调用远程服务,以及在实际应用中的操作步骤。 首先,我们需要理解AIDL的基本概念。AIDL是一种接口定义语言,它允许我们定义在不同进程间通信的接口。这些接口可以包含方法声明,参数...

Global site tag (gtag.js) - Google Analytics