深刻解析 Android 的 AIDL 介面
在Android應用框架裡,應用層級的軟體大多是Java類別,而系統層級的軟體大多是C或 C++類別。Android使用JNI介面來達成這個目標。例如,在Android裡有個MediaPlayer.cpp類別,當它搭配上JNI介面之後,在VM上執行的MediaPlayer.java就可以透過JNI介面與MediaPlayer.cpp類別溝通了。
圖1-1
基於這樣的架構,我門就可以盡量將MediaPlayer.java裡的程式邏輯移入MediaPlayer.cpp類別裡,以便加快程式的執行速度。此外,以JNI介面包裝MediaPlayer.cpp類別,並且銜接相互輝映的MediaPlayer.java類別,可以讓眾多的Java應用程式透過MediaPlayer.java來使用MediaPlayer.cpp類別的服務。這是一種非常有用的包裝技巧,藉由包裝來創造更多的應用機會。
為了進一步創造更多的應用機會,可以替MediaPlayer.java類別加上AIDL介面,讓更多的Java應用程式能與MediaPlayer.java類別進行遠距的IPC溝通。如下圖所示:
圖1-2
上圖表示出JNI在Android裡扮演的角色,以及Android框架裡Java與C/C++類別融合的基本架構。上面的图1-2是一個較為抽象的圖,凸显JNI与AIDL的相互呼應之角色。
於此,以高煥堂所寫的第2本Android書:<> 一书里的范例:HalfAdder組件為例,展示其幕后的细节架构。首先看看其JNI介面之上的細節架構,如下UML圖:
圖1-3
上圖1-2幕後的細節架構之一
上圖凸顯了AIDL介面的細節架構。下圖1-4將換個角度,從*.so開發者來看,當我們開發系統層級的C/C++類別時,也能善用JNI,創造C/C++類別的廣大商機。其細節架構如下UML圖:
圖1-4
上圖1-2幕後的細節架構之二
1.3
說明C/C++組件開發
從上圖1-4可看到此范例的C/C++組件部份。其詳細的程式碼,請閱讀高煥堂所寫的第2本Android書:<> 一書之第5~6章。
1.4
說明AIDL介面類別之開發
基於剛才所撰寫的相對應Java類別:Calculator.java,就能順利配上AIDL介面了。
1.4.1
細說AIDL介面與IBinder介面
其實,AIDL介面幕後是仰賴著IBinder介面的。所以,我們的應用程式可以選擇使用IBinder介面,也可以使用AIDL介面。如果採取IBinder介面,就不必使用aidl.exe工具去产生calInterface.java介面定义档了,其介面類別較單純一些,如下圖所示:
圖1-5
僅使用較單純的IBinder介面
由於IBinder介面只提供單一函數(即transact()函數)來進行远距沟通,呼叫起來比較不方便。例如,當Calculator類別有多個函數時,myActivity要如何呼叫它們呢?
可以呼叫IBinder介面的transact()函數,再转而呼叫Calculator的各個函數。由於它並不太方便,所以Android提供Proxy/Stub結構的AIDL介面來化解這個問題,其架構圖如下:
圖1-6
更方便的AIDL介面(其介面類別結構較複雜一些)在本範例裡,將採取AIDL介面,同時也介紹其幕後的IBinder介面,以及其兩這之間的密切關係。
1.4.2細說Stub類別的程式碼
這個Stub類別就是由aidl.exe所产出的;也就是在calInterface.java裡面。并再重复列出calInterface.java程式碼如下:
/*---- calInterface.java ----*/
/*
* This file is auto-generated.
DO NOT MODIFY.
* Original file: calInterface.aidl
*/
package com.misoo.gx06;
import java.lang.String;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Binder;
import android.os.Parcel;
public interface calInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.misoo.gx06.calInterface
{
private static final java.lang.String DESCRIPTOR = "com.misoo.gx06.calInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an calInterface interface,
* generating a proxy if needed.
*/
public static com.misoo.gx06.calInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
com.misoo.gx06.calInterface in = (com.misoo.gx06.calInterface)obj.queryLocalInterface(DESCRIPTOR);
if ((in!=null)) {
return in;
}
return new com.misoo.gx06.calInterface.Stub.Proxy(obj);
}
public android.os.IBinder asBinder()
{
return this;
}
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_EvDigitPress:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.EvDigitPress(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_EvCPress:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.EvCPress();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_EvPlusPress:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.EvPlusPress();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_EvAssignPress:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.EvAssignPress();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.misoo.gx06.calInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
public java.lang.String EvDigitPress(int d) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(d);
mRemote.transact(Stub.TRANSACTION_EvDigitPress, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public java.lang.String EvCPress() throws android.os.RemoteException
{
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_EvCPress, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public java.lang.String EvPlusPress() throws android.os.RemoteException
{
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_EvPlusPress, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public java.lang.String EvAssignPress() throws android.os.RemoteException
{
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_EvAssignPress, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_EvDigitPress = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_EvCPress = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_EvPlusPress = (IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_EvAssignPress = (IBinder.FIRST_CALL_TRANSACTION + 3);
}
public java.lang.String EvDigitPress(int d) throws android.os.RemoteException;
public java.lang.String EvCPress() throws android.os.RemoteException;
public java.lang.String EvPlusPress() throws android.os.RemoteException;
public java.lang.String EvAssignPress() throws android.os.RemoteException;
}
這個calInterface.java檔案是由aidl.exe工具程式所产出的。
首先,calInterface界面继承了Android提供的Iinterface界面,如下述程式碼:
public interface calInterface extends android.os.IInterface
{
………
}這個類別定義又內含有一個Stub類別,其繼承Android提供的Binder父類別,而且實作calInterface介面,如下述程式碼:
public interface calInterface extends android.os.IInterface
{
……….
public static abstract class Stub extends android.os.Binder implements
com.misoo.gx06.calInterface
{
……….
private static class Proxy implements com.misoo.gx06.calInterface
{
………
}
………..
}
由於Stub類別继承了Binder父類別,而Binder又實作了IBinder介面,所以Stub類別支持兩個介面:IBinder和calInterface。其內含的Proxy类別也支持calInterface。
圖1-7
IBinder與calInterface介面之關係
1.4.4細說CalBinder類別的程式碼
基於這個結構,就可以從Stub類別衍生(Derive)出子類別:calBinder,其程式碼為:
/*---- CalBinder.java ----*/
package com.misoo.gx06;
import android.os.RemoteException;
public class CalBinder extends calInterface.Stub{
private Calculator mCal = null;
public CalBinder(){
mCal = new Calculator();
}
@Override
public String EvAssignPress() throws RemoteException {
mCal.EvAssignPress();
return mCal.retStr;
}
@Override
public String EvCPress() throws RemoteException {
mCal.EvCPress();
return mCal.retStr;
}
@Override
public String EvDigitPress(int d) throws RemoteException {
mCal.EvDigitPress(d);
return mCal.retStr;
}
@Override
public String EvPlusPress() throws RemoteException {
mCal.EvPlusPress();
return mCal.retStr;
}
}
其中,指令:
public CalBinder(){
mCal = new Calculator();
}
誕生了一個Calculator物件,如此與Calculator類別銜接起來了。
1.4.5函數呼叫的情境
此範例程式需要用到各AIDL類別的詳細函數如下圖所示:
圖1-8AIDL各類別裡的函數及其關係
只要對物件觀念和技術較為熟悉的話,就可看出來其並不太複雜,只是基本物件觀念之應用而已。更詳細之說明,請看高煥堂所寫的第2本Android書:<> 一書。
分享到:
相关推荐
深刻解析Android的AIDL界面 深刻解析Android的AIDL界面
Android AIDL使用例子
Android AIDL demo (Android Studio)
android AIDL详情解析
使用AIDL实现Android进程间通信,Demo包含Service,Client代码。
android aidl源码实例
android AIDL 完整DEMO ,简单易懂
Android aidl 实现进程间通信(IPC)
android aidl进程间的通讯,适合入门dome下载使用,在不同应用进程之间的通讯
android aidl的使用及demoandroid aidl的使用及demo
android aidl service
Android Aidl 包括了服务端和客户端demo,通过Aidl 客户端可以调用服务器中的方法
android aidl文件不一致引起的调用出错问题.doc
android aidl activity 调用部分。
Android AIDL Binder 实现与详解。此资源实现了 Android AIDL 通信,自定义 AIDL 数据类型。同时演示了定向 Tag 『inout in out』的区别。并且配有博文详细解释相关知识点以及需要注意的细节。
Android AIDL 服务端与客户端实现Demo
andorid 不同进程间的通信, uses the aidl to comple the demo,hope you can learn ,and give me you idear .
Aidldemo 备份一个 用到的时候直接拿出来用 Service 和Activity 是两个独立的Apk
Android AIDL传输对象代码 Parcelable 在AIDL client的Activity中点击传输对象按钮查看日志,就会看到对象里面的内容