AIDL

从Android develop文档复制而来,只为自己阅读方便,翻墙不易。


Android的接口定义语言(AIDL)

AIDL(Android界面定义语言)相似,你可能已经使用过其他的IDL。它允许您定义的编程接口,客户端和服务,才能与对方使用进程间通信(IPC)进行通信商定。在Android上,一个进程无法正常访问另一个进程的内存。所以讲,他们需要自己的对象分解成原语操作系统能够理解,马歇尔跨边界的对象为您服务。要做到这一点编组的代码是繁琐的编写,因此Android与AIDL为您代劳。

注意:使用AIDL是必要的前提是你让来自不同应用程序的客户端访问服务的IPC,并希望在处理你的服务多线程。如果你不需要在不同的应用程序执行并发IPC,你应该创建界面实现活页夹,或者,如果要执行IPC,但并不需要处理多线程,实现你的界面使用Messenger的。无论如何,请确保您了解绑定服务实现一个AIDL之前。

在你开始设计AIDL接口,要知道是直接的函数调用,调用一个AIDL接口。你不应该对其中调用发生线程的假设。根据呼叫是否来自本地进程中的线程或远程进程会发生什么事是不同的。特别:

  • 从本地工艺制成的呼叫在正在呼叫的同一个线程执行。如果这是你的主UI线程,该线程将继续在AIDL接口来执行。如果它是另一个线程,即,在服务执行代码中的之一。因此,如果只是本地线程访问该服务,可以完全哪些线程中执行它(但如果是这样的话,那么你不应该在所有使用AIDL控制,但应改为创建接口实现一个活页夹)。
  • 从远程进程调用从一个线程池的平台,自己的过程内保持调度。必须从未知线程来电,具有多个呼叫同时发生来制备。换句话说,一个AIDL接口的实现必须完全线程安全。
  • 单向关键字改变远程调用的行为。在使用时,一个远程调用不会阻塞; 它只是发送的交易数据,并立即返回。的接口的实现最终接收该从常规呼叫粘合剂线程池作为正常远程调用。如果单向与本地电话使用的,不存在影响和通话仍然是同步的。

定义一个AIDL接口


您必须确定您的AIDL接口在.aidl使用Java编程语言的语法文件,然后将其保存在源代码(在SRC /目录下)这两个应用托管服务,并结合该服务的任何其他应用程序。

当你建立一个包含每个应用程序.aidl文件时,Android SDK工具生成的IBinder基于接口.aidl文件并将其保存在项目的根/目录下。该服务必须实现的IBinder 接口为宜。然后,客户端应用程序绑定到从服务和调用方法的IBinder执行IPC。

要使用AIDL有界服务,请按照下列步骤操作:

  1. 创建.aidl文件

    该文件定义了方法签名的编程接口。

  2. 实现接口

    在Android SDK工具生成的Java编程语言的界面,根据您的 .aidl文件。这个接口有一个内部抽象类名为存根扩展 粘结剂并实现从你的AIDL接口中的方法。您必须扩展 存根类,并实现方法。

  3. 公开接口给客户

    实施服务,并覆盖onBind()来回报您的实施存根 类。

注意:您对您的AIDL接口的先放后的任何修改必须保留,以避免破坏使用你的服务的其他应用程序向后兼容。也就是说,因为你.aidl文件必须为了他们访问您的服务的接口,你必须保持原有的接口支持被复制到其他应用程序。

1.创建.aidl文件

AIDL使用一个简单的语法,它允许您声明一个接口,可以采取参数和返回值的一种或多种方法。的参数和返回值可以是任何类型的,即使是其他的AIDL生成接口。

您必须构建.aidl使用Java编程语言文件。每个.aidl 文件必须定义一个接口,只需要接口声明和方法签名。

默认情况下,AIDL支持以下数据类型:

  • 所有原始类型的Java编程语言(如INT, 焦炭布尔,等等)
  • 为CharSequence
  • 名单

    在所有的元素列表必须在此列表中支持的数据类型之一,或者您已经声明了其他AIDL生成的接口或Parcelables并之一。一个列表可以选择作为“通用”类(例如, 名单<String>的)。认为对方收到实际的具体类始终是一个ArrayList中,虽然会产生使用方法列表界面。

  • 地图

    在所有元素地图必须在此列表中支持的数据类型之一,或者您已经声明了其他AIDL生成的接口或Parcelables并之一。通用图,(如那些形式的 地图<字符串,整数>不被支持。认为对方收到实际的具体类始终是一个HashMap中,虽然会产生使用方法地图界面。

您必须包括进口以上未列出的其他每个类型的语句,即使他们在同一个包为你的接口定义。

在定义服务接口,请注意:

  • 方法可以采取零个或多个参数,并返回一个值或空隙。
  • 所有非基本参数要求定向标记指示数据去哪个方向。无论是,或INOUT(见下面的例子)。

    原语是默认情况下,不能以其他方式。

    注意:您应该限制的方向是什么真正需要的,因为编组参数是昂贵的。

  • 包括在所有代码的注释.aidl文件都包含在生成的IBinder接口(除进口和包语句之前的评论)。
  • 只有方法的支持; 你不能揭露AIDL静态字段。

下面是一个例子.aidl文件:

// IRemoteService.aidl 
融为一体例如机器人; 

//此处声明任何非默认类型的import语句

/ **示例服务接口* / 
接口 IRemoteService  { 
    / **要求这项服务的进程ID,做坏事用它。* / 
    INT GETPID (); 

    / **表明可以作为参数使用一些基本类型
     。*在AIDL返回值
     * / 
    无效basicTypes INT ANINT  一起 布尔aBoolean  漂浮水上
            aDouble  字符串ASTRING ) ; 
}

只需保存.aidl文件在项目的的src /目录下,当你建立你的应用程序,SDK工具生成的IBinder在项目的接口文件根/目录下。所生成的文件名 ​​相匹配的.aidl文件名 ​​,但是具有的.java扩展名(例如,IRemoteService.aidl导致IRemoteService.java)。

如果你使用Android工作室,增量构建几乎立即产生粘合剂类。如果你不使用Android工作室,那么摇篮工具生成构建应用程序,你应该建立与项目粘合剂类下一次gradle这个assembleDebug (或gradle这个assembleRelease只要你写完).aidl文件,所以你的代码可以对链接生成的类。

2.实现接口

当你建立你的应用程序时,Android SDK工具生成的.java您的名字命名的接口文件.aidl文件。生成的接口包括一个名为子类存根 这是一个抽象实现其父接口(例如,YourInterface.Stub),并声明所有从方法.aidl文件。

注: 存根还定义了一些辅助方法,最值得注意的是asInterface() ,它接受一个的IBinder(通常是一个传递给客户的onServiceConnected()回调方法)并返回存根接口的一个实例。请参见调用一个IPC方法为如何使这个转换的更多细节。

为了实现从所产生的界面.aidl,延长所生成的粘合剂的接口(例如,YourInterface.Stub)并实施从继承的方法.aidl文件。

这里是称为界面的示例实施IRemoteService(由定义的 IRemoteService.aidl使用匿名实例例如,上文):

private  final  IRemoteService . Stub mBinder =  new  IRemoteService . Stub ()  { 
    public  int getPid (){ 
        return  Process . myPid (); 
    } 
    public  void basicTypes ( int anInt ,  long aLong ,  boolean aBoolean , 
        float aFloat ,  double aDouble ,  String aString )  { 
        //不执行任何操作
    } 
};

现在mBinder是的一个实例存根类(粘合剂),其定义了服务的RPC接口。在下一步骤中,这种情况下被暴露于客户端,这样他们可以与服务进行交互。

还有,你应该知道你的实现AIDL接口时,一些规则:

  • 呼入电话无法保证会在主线程上执行的,所以你需要考虑从一开始多线程和正确建立服务是线程安全的。
  • 默认情况下,RPC调用是同步的。如果您知道该服务需要超过几毫秒的时间来完成一个请求,你不应该从活动的主线程中调用它,因为它可能会挂起应用(Android版可能会显示一个“应用程序没有响应”对话框) - 你应该通常在客户端一个单独的线程调用它们。
  • 没有你抛出异常发送回调用者。

3.公开接口给客户

一旦你实现的接口为您服务,您需要将其暴露给客户,使他们能够绑定到它。揭露为您服务的接口,扩展服务并实现onBind()返回类实现所产生的一个实例存根(如上一节中讨论)。下面是一个公开的例子服务IRemoteService例如接口给客户。

     
    
      
        
    

    
      
        返回 
        
    

         
         
             
        
           
               
            不执行任何操作
        } 
    }; 
}

现在,当一个客户端(如活动)调用bindService()来连接到该服务,客户端的onServiceConnected()回调接收 mBinder由服务的返回的实例onBind() 方法。

客户端还必须能够访问的接口类,因此,如果在客户端和服务都在独立的应用程序,则在客户端的应用程序必须具有的副本.aidl在其文件的src /目录(它产生android.os.Binder 接口-提供给AIDL方法,客户端访问)。

当客户端收到的IBinderonServiceConnected()回调,它必须调用 YourServiceInterface .Stub.asInterface(服务)将返回参数转换为YourServiceInterface类型。例如:

IRemoteService mIRemoteService ; 
私人 ServiceConnection mConnection =   ServiceConnection () { 
    //当与服务建立连接调用
    公共 无效onServiceConnected 组件名的className  的IBinder 服务 { 
        //按照上面一个AIDL接口的例子,
        //这得 ​​到一个在IRemoteInterface,我们可以用它来 ​​在服务调用的实例
        mIRemoteService =  IRemoteService 存根asInterface 服务
    } 

    //调用时与服务的连接意外断开
    公共 无效onServiceDisconnected 组件名的className  { 
        日志Ë TAG  “服务意外中断” ); 
        mIRemoteService =  ; 
    } 
};

欲了解更多示例代码,请参见RemoteService.javaApiDemos

传递对象超过IPC


如果你有,你想通过IPC接口从一个进程发送到另一个类,你可以做到这一点。但是,你必须确保你的类的代码可到IPC通道的另一边,你的类必须支持Parcelable接口。支持Parcelable接口很重要,因为它可以让Android系统分解物进入,可以跨进程编组元。

要创建支持类Parcelable协议,必须做到以下几点:

  1. 让你的类实现Parcelable接口。
  2. 实施writeToParcel,这需要对象的当前状态,并把它写入到一个
  3. 一个叫做静态字段添加CREATOR到您的类,这是实现一个对象Parcelable.Creator接口。
  4. 最后,创建一个.aidl声明你parcelable类(如图所示的文件 Rect.aidl文件,下同)。

    如果您使用的是自定义生成过程中,千万不能添加.aidl文件到您的构建。类似于C语言的头文件,这.aidl文件不被编译。

AIDL使用这些方法和字段在它生成于编组和解组的对象的代码。

例如,这里是一个Rect.aidl文件来创建一个矩形类的parcelable:

包装机器人图形; 

//声明矩形这样AIDL可以找到它,知道它实现
//将parcelable协议。
parcelable 矩形;

这里是该怎么一个例子矩形类实现 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 (); 
    } 
}

在编组矩形类是非常简单的。看看在其他的方法包裹看到其他类型的值,你可以写一个包裹。

警告:不要忘记其他进程接收数据的安全问题。在这种情况下,矩形读取四个数字包裹,但它是由你来确保,这些是任何呼叫者正试图做的值的可接受的范围内。见安全和权限有关如何保证应用程序的安全免受恶意软件的更多信息。

调用一个IPC方法


这里有一个调用的类必须调用与AIDL定义的远程接口的步骤:

  1. 包括.aidl在项目文件中的src /目录下。
  2. 声明的一个实例的IBinder接口(生成基于所述AIDL)。
  3. 实施ServiceConnection
  4. 呼叫Context.bindService() ,并传入您的ServiceConnection实现。
  5. 在你执行onServiceConnected() ,您将收到的IBinder实例(称为服务)。呼叫 YourInterfaceName .Stub.asInterface((的IBinder)服务将返回的参数强制转换为YourInterface类型。
  6. 请致电您在接口上定义的方法。你应该总是陷阱 DeadObjectException异常,当连接中断而抛出; 这将是由远程方法抛出的唯一例外。
  7. 要断开连接,调用Context.unbindService()与接口的实例。

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

  • 对象引用跨进程计数。
  • 您可以发送匿名对象作为方法的参数。

有关绑定到服务的更多信息,请阅读绑定服务 文档。

下面是一些示例代码演示调用一个AIDL创建的服务,从项目ApiDemos远程服务取试样。

公共 静态  绑定 扩展 活动 { 
    / **主界面,我们将在服务调用进行。* / 
    IRemoteService MSERVICE =  ; 
    / **我们使用该服务的另一个接口。
     

    
    

     

    
     本次活动的标准初始化。设置UI,然后等待
     *为用户做之前,它捅 

    
      
        



        留意按钮 
         

 

 



 
附“ ); 
    } 

    / ** 
     *分类为与所述主界面交互 

        
         
                 
            这与该服务的连接已被调用时
            //建立,使我们的服务对象,我们可以用它来
            ​​//使用服务进行交互。我们正在与我们沟通
            ,通过IDL接口//服务,因此得到了客户端
            //从原始服务的代表 
 



            我们想监控,只要我们的服务
            //连接到它。
            尝试 { 
                MSERVICE registerCallback mCallback ); 
            }  赶上 RemoteException的ē  { 
                //在这种情况下,我们可能甚至在服务已经崩溃
                //做与任何东西,我们可以指望很快被
                //断开(然后重新连接,如果可以重新启动)
                //所以没有必要做任何事情在这里。
            } 

            //由于样本的一部分,告诉用户什么 
            
                    
        

          
            这就是所谓的当与服务的连接已
            //意外断开-也就是说,它的进程崩溃。
            MSERVICE =  ; 
            mKillButton 的setEnabled ​​假); 
            mCallbackText 的setText “断开” ); 

            //作为一部分样本,告诉用户什么 
            
                    
        
    

    
     类具有的二级界面进行交互 

        
         
                 
            连接到辅助接口是相同的任何
            //其他 
 

        

          
 

        
    

        
          
            与该服务建立了几个连接,结合
            由接口名称//这允许其它应用程序是
            //装通过实施替换远程服务
            //相同 
               

 

 
 

        
    

        
          
              
                如果我们收到了该服务,因此与注册
                //它,那么现在是注销的时间
                ,如果 MSERVICE !=   { 
                    尝试 { 
                        MSERVICE unregisterCallback mCallback ;)
                    }  赶上 RemoteException的ē  { 
                        //有没什么特别的,我们需要做的,如果服务
                        //已崩溃。
                    } 
                } 

                //分离我们现有的 



 

            
        
    

        
          
            杀死进程托管我们的服务,我们需要知道它
            // PID。我们的便利服务,具有呼叫,将返回
            //给我们的信息。
            如果 mSecondaryService !=   { 
                尝试 { 
                    INT PID = mSecondaryService GETPID ( ); 
                    //需要注意的是,虽然该API允许我们请求
                    //杀根据它的PID任何进程,内核会
                    //仍然征收标准限制它的PID你
                    //居然能够杀死通常,这意味着只。
                    //运行应用程序和任何其他的过程
                    由应用程序创建//流程如下图所示;包
                    //共用一个UID也能自相残杀
                    。//其他的流程
                    过程killProcess PID ); 
                    mCallbackText 的setText “杀了服务流程。” ); 
                }  赶上 RemoteException的 { 
                    //从托管过程中恢复正常
                    。//服务器垂死
                    //只为样本的目的,提出了一个 
                    

                            
                
            
        
    

    -------------------------------------------------- -------------------- 
    //显示如何处理回调代码。
    // ------------------ -------------------------------------------------- - 

    / ** 
     *这种实现用于从远程接收回调
     *服务。
     * / 
    私人 IRemoteServiceCallback mCallback =   IRemoteServiceCallback 存根() { 
        / ** 
         *这是由远程服务名为定期向我们讲述
         *新。值的注意的是IPC调用都是通过一个线程进行调度
         的每个进程中运行*池,所以这里执行的代码将
         *不是我们最喜欢的其他东西主线程中运行-因此,
         *更新UI,我们需要使用一个Handler来跃过 

          
 
        
    

        

        
           
              
                
从服务:“  + 味精ARG1 ); 
                    打破; 
                默认
                    的handleMessage 味精); 
            } 
        } 

    }; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值