Provided Scope in Android Studio

provided scope包引入方式就是,项目编译时只包含对库的classpath引用,但是一并打包库到编译的项目中,一般情况是目标运行环境或容器已经包含了该库。

Android Studio自从0.4.3((Gradle 0.8)版本就引入Provided Scope库编译选项[1],但是我一直都没有试验成功,之前都是使用AS(Android Studio)打开eclipse的项目才可以。来到Android Studio beta 0.8.2版本,在AS的Project Structure Setting的module dependencies里面的scope选项里provided竟然消失了,手工修改项目build.gradle依然不行。 

 

在Android Issues看到很多人反馈的bug请求,其中一篇比较早的Google工程师提到可以使用Android-apt第三方gradle插件来解决provided引入问题[2]。我试验了下,测试通过。

 Android-apt的项目地址:https://bitbucket.org/hvisser/android-apt/overview

在项目公共build.gradle中插入

 

然后在项目build.gradle中插入:

 

如上面代码所示,如果想使用provided方式引入项目,格式为: apt ‘com.google.code.gson:gson:2.2.+’ 

参考资料:

  1. http://tools.android.com/recent/androidstudio043released
  2. https://code.google.com/p/android/issues/detail?id=65898#c7
  3. https://bitbucket.org/hvisser/android-apt/overview

 

Android开发指导文档(译)–Bound Services

翻译原作者:Tjxin_xd   原文  英文原文

Bound Services

bound service 在客户端服务器接口中作为服务器。一个bound service允许组件(比如activity)绑定到service,发送请求,接收响应,甚至执行进程间通信(IPC)。一个bound service通常在为其他程序组件服务时才存在并且不会无限期的在后台运行。         

这个文档将显示如何创建一个bound service,包括如何绑定到其他应用程序的service


基本原理

       一个bound service是一个Servcie类的实现,它允许其他应用程序绑定到它上面并与之交互。为了为service提供绑定,你必须实现onBind()回调函数。这个函数返回一个IBinder对象,该对象定义了客户端与service交互的编程接口。

       一个客户端可以通过调用bindService()绑定到service。它必须提供一个ServiceConnection的实现,该实现监听与service的连接。这个bindService()方法没有返回值立即返回,但是当Android系统创建客户端与service之间的连接时,它调用ServiceConnectiononServiceConnected()函数来传递一个IBinder,客户端可以用其与service通信。

       多个客户端可以同时与service连接。然而,在第一个客户端绑定到service时,系统调用你的serviceonBind()方法来获得IBinder。然后系统将同一个IBinder传递给之后添加的所有绑定客户端,而不会再调用onBinder()

       当最后一个客户端从service解除绑定时,系统会destroy这个service(除非service通过调用startService()启动)。

       当你实现一个bound service,最为重要的部分是定义你的onBinder()回调函数返回的接口。有多种途径来定义你的serviceIBinder接口。

 


创建一个
Bound Service

       创建IBinder的几种途径:

继承Binder

 

       如果你的service对你的应用程序是私有的并与客户端运行在相同的进程中,你应该通过继承Binder类创建接口并从onBind()返回。客户端接收到这个Binder并用它直接访问在Binder实现的甚至是Service的公共可用的方法。

       当你的service仅仅是一个自己应用程序的后台worker时,这种方法首选。你不会使用这种方法创建一个接口的唯一原因是你的service被其他的应用程序使用或者使用了单独的进程。

使用Messenger

       如果你需要你的接口在不同的进程使用,可以使用Messengerservice创建一个接口.使用这种方式,service定义一个Handler来回应不同类型的Message对象。这个Handler作为可以与客户端共享IBinderMessenger的基础,允许客户端使用Message对象给service发送命令。另外,这个客户端可以定义一个自己的Messenger,所以service可以送回消息。

       这是来执行进程间通信最简单的方式,因为这个Messenger查询所有的单独的线程的请求,所以你不需要将你的service设计成线程安全的。

使用AIDL

       AIDL(Android接口定义语言) 执行所有的工作来将对象分解为操作系统可以理解的原始状态并marshall它们来跨线程执行IPC。先前使用Messenger的方式事实上是使用AIDL作为它的底层结构。如前面提到的,这个Messenger在一个单独的线程中创建一个所有客户端请求的队列,所以service依次接收请求。然而,如果你希望你的service来同时处理多个请求,你可以直接使用AIDL。在这种情况下,你的service必须是多线程的并且可以用线程安全的方式建立。

       为了直接使用AIDL,你必须创建一个.aidl文件来声明编程接口。Android SDK工具使用这个文件来生成一个实现了接口并处理IPC的抽象类,该类可以在你的service内部继承。

       Note:多数应用程序不应该使用AIDL来创建bound service,因为它或许需要多线程能力并导致更为复杂的实现。因此,AIDL不适合大多数的应用程序。本文档不会讨论如何为你的service使用它,如果你确信需要直接使用AIDL,查看AIDL文档。

 

下面依次介绍每种途径:

继承Binder

       如果你的service只在本地程序使用并且不需要跨进程工作,这时你可以实现你自己的Binder类来为你的客户端提供对service公有函数的直接访问。

       Note:这种方式只有在客户端和service在同一个程序中时有效。比如说,一个音乐程序需要绑定一个activity到它自己的service来在后台播放音乐,采用这种方式将很有效。

      

下面是如何建立它:

1.
在你的service中创建一个Binder的实例,该实例:

  •  或者包含客户端调用的公共函数
  • 或者返回当前Service的实例,该实例包含客户端可以调用的公共函数
  • 或者返回另一个类的实例,这个类包含客户端可以调用的公共函数且其宿主为service

2.
onBind()回调函数返回这个Binder实例

3.
在客户端从onServiceConnected()回调函数接收这个Binder并使用其提供的方法访问bound service

Noteservice和客户端必须在同一个程序的原因是客户端可以对返回的对象转换并正确的调用其APIservice和客户端同样必须在同一个进程中的原因是因为这项技术不执行任何跨进程编组(marshalling)。

比如,下面的例子显示service通过实现Binder使客户端能访问其内部方法:

 

LocalBinder为客户端提供getService()方法来检索当前LocalService实例。这种方式使得客户端可以使用service中的公共的方法。比如说,客户端可以从service调用getRandomNumber()。

       下面是一个activity绑定到LocalService并在按钮按下时调用getRandomNumber()方法。

 

 

上面的例子简单的演示了如何使用ServiceConnectiononServiceConnection()回调函数将客户端绑定到service。下一节将提供更多关于处理绑定的service的信息。

          Note:上面的例子没有明确的从service解绑定,但是所有客户端应该在合适的时机解除绑定(如当activity
pause 时)。

 

使用Messenger

   如果你需要service与远程进程通信,你可以使用Messenger来为你的service提供接口。这项技术允许你执行进程间通信(IPC)而不需要使用AIDL

   下面是如何使用Messenger

  •  Service实现一个Handler为来自客户端的访问接收回调
  • Handler是用来创建Messenger对象(它是一个对Handler的引用)
  •  Messenger创建一个IBinder并由service通过onBind()返回给客户端。
  • 客户端使用IBinder来实例化Messenger,客户端使用它来给service发送Message对象
  • Service在其Handler接收每个Message—尤其是在handleMessage()方法中。通过这种方式客户端没有”方法”来访问service,取而代之的是客户端传递”消息”(Message对象),消息在serviceHandler中接收。

下面是一个使用Messenger接口的简单例子:

 

 

 

 

注意Handler中的handleMessage()方法是service接收传递过来的Message和基于what参数决定做什么的地方。

   客户端要做的是基于service返回的IBinder创建一个Messenger并使用send()发送消息。比如说,下面是一个简单的绑定到serviceactivity并传递了一个MSG_SAY_HELLO消息给service

 

注意到这个例子没有显示service如何能响应客户端,如果你想service作出回应,你需要在客户端创建一个Messenger。然后在客户端接收到onServiceConnected()回调时,它发送一个Message给服务器,该消息在send()方法的replyTo参数包含了客户端的Messenger。


 

 

 

绑定到Service

应用程序组件(客户端)可以通过调用bindService()来绑定到service。然后Android系统调用serviceonBind()方法,该方法返回一个IBinder来与service交互。

       这个绑定是异步的。bindService()立即返回并且不将IBinder返回给客户端,为了接收这个IBinder,客户端必须创建一个ServiceConnection的实例然后将其传递给bindService()。这个ServiceConnection包含供系统传递IBinder的回调函数。

       Note:只有activityservicecontent provider可以绑定到service—你不能把一个broadcast receiver绑定到service

所以,为了将客户端绑定到一个service,你必须:

1、  实现ServiceConnection

你的实现必须重写两个回调函数:

onServiceConnected():系统调用该函数来传递由serviceonBind()方法返回的IBinder

onServiceDisconnection():对service的连接意外丢失,比如当service崩溃或被kill时,Android系统会调用该函数。当客户端解除绑定时不会调用。

2、  调用bindService(),传递ServiceConnection实现。

3、  当系统调用你的onServiceConnected()回调函数是,你就可以使用接口中定义的方法开始访问service

4、  为了从service解除连接,可以调用unbindService()

当你的客户端被销毁是,它将从service解除绑定,但是你因该在与service完成交互或者你的activity pause时解除绑定,这样service就可以在其不用时关闭。

比如说,下面一个片段连接一个客户端到service,该service是之前用继承Binder类方式创建的。所有要做的是将返回的IBinder换作LocalService类并请求LocalService实例。

 

 

客户端可以绑定到service,并将ServiceConnection传递给bindService()。比如:

 

 

  • bindService()的第一个参数是一个Intent指明要绑定的service的名字(虽然这个Intent可以是implicit的)
  • 第二个参数是ServiceConnection对象
  • 第三个参数是一个表示(flag),来指明绑定的选项,它常常是BIND_AUTO_CREATE来在service不存在时创建。其它的值是BIND_DEBUG_UNBINDBIND_NOT_FOREGROUND0

附加注释:

下面是关于绑定到service的重要注释:

  • 你必须总是捕获DeadObjectException,该异常在连接损坏是抛出。这是远程方法抛出的唯一异常
  •  对象是跨进程计算的引用(Objects are reference counted across processes.
  •  在客户端声生命周期的匹配的建立(bring-up)和卸载(tear-down)时刻,你应该总是匹配绑定和解绑定,比如说:

         1、  如果你只需要在你的activity可见时与service交互,应该在onStart()中绑定,并在onStop()中解除绑定。

         2、  如果你需要你的activity即使在后台停止的时候也可以接收响应,你可以在onCreate()时绑定service,然后在onDestroy()中解除绑定。当心,这意味着你的activity在整个生命周期都需要用到service(即使是在后台),所以如果service在其他的进程中,你增加了这个进程的负担,它变得更容易被系统kill

Note:你不因该在onResume()onPause()绑定以及解绑定,因为这些回调函数发生在每次生命周期变换,你应该确保这些处理发生在转换的最低限度。同样,如果在你的应用程序中多个activity绑定到同一个service并且两个activity之间有一个转换(transition),在当前activity解除绑定(正在pause,而在下一个activity绑定之前,service或许会被销毁和重新创建。

 

 


管理Bound Service的生命周期

当一个service从所有的客户端解除绑定时,Android系统会销毁它(c除非它是通过onStartCommand()启动的)。因此,

如果service只是个bound service,你不需要管理它的生命周期。—Android系统会根据其是否绑定到客户端来管理它。

       然而,如果你选择实现onStartCommand()回调函数,此时你必须明确的stop service,因为这个service此时被认定是started。在这种情况下,这个service一直运行,直到service通过调用stopSelf()停止自己或者其他的组件调用stopService()来停止它,此时不用考虑是否有组件绑定到它上面。

       另外,如果你的servicestarted并接受绑定,在系统调用你的onUnbind()方法时,如果你想在下一次一个客户绑定到service时接收onRebind()的调用,你可以选择返回trueonRebind()返回void,但是客户端仍在其onServiceConnected()回调中接收到IBinder,下图阐明了这种生命周期的逻辑:

 

9908308_1303042555T1HS

 

 

[翻译]Android接口定义语言 (AIDL)

转载自:WolfCS 原文:  点这里 英文原文:AIDL

AIDL(Android接口定义语言)与你可能使用过的其它的IDLs是类似的。它允许你定义客户端与service协调一致的编程接口,以便于彼此之间使用进程间通信(IPC)机制进行通信。在Android上,一个进程通常不能访问另一个进程的内存。可以说,它们需要把它们的对象分解为操作系统能够理解的原始数据类型,并在进程之间按次序排列对象。那种排列对象的代码写起来是很乏味的,因此Android通过AIDL来为你处理这些事情。

注意:只有在你允许来自于不同的应用的客户端访问你的service以实现IPC,并想要在你的service中处理多线程时,才需要使用AIDL。如果你不需要跨不同应用执行并发IPC,你应该通过实现一个Binder来创建你的接口,或者如果你想要执行IPC,但不需要处理多线程,可以使用一个Messenger来实现你的接口。无论哪种,请确保在实现一个AIDL之前,你理解了Bound Services。/

在开始设计你的AIDL接口之前,请意识到对于一个AIDL接口的调用是直接的函数调用(大概指的是阻塞调用,在调用一个IPC方法时,客户端线程会一直等待,直到service端处理完成并返回)。你不应该假设调用所发生的线程。依赖于调用是来自于本进程的一个线程,还是一个远端进程,则会发生不同的事情。特别地:

  • 发起于本进程的调用将在发起调用相同的线程中执行。如果这是你的UI线程,则线程将继续在AIDL接口中执行。如果它是另外一个线程,那它将是执行你的service代码的线程。因此,如果只有本地线程访问service,你可以完全控制在哪个线程中来执行它(但如果是那种情况,则你不应该使用AIDL,而应该通过实现一个Binder创建接口)。
  • 调用来自于远端进程,并从平台维护的你自己的进程中的一个线程池派发。你必须为调用可能来自于未知线程做好准备,多个调用可能在同一时刻发生(Service是一个对象,而不是一个线程,这里的未知线程大概指的是binder线程,这里的意思大概是说,AIDL service类要按照线程安全类的标准来构造)。换句话说,一个AIDL接口的实现必须完全是线程安全的。
  • 单路行为的远程调用。当使用时,一个远程调用不发生阻塞;它仅仅是发送事务数据并立即返回。接口的实现最终将它作为来自于Binder线程池的一个普通远端调用来接收。如果单路被用于一个本地调用,则没有影响,调用依然是同步的。

 

定义一个AIDL接口

你必须使用Java编程语言语法在一个.aidl文件中定义你的AIDL接口,然后同时保存在service所在的应用和其他要bind到service的应用的源代码中(在src/目录下)。

当你编译每个包含.aidl文件的应用时,Android SDK工具会基于.aidl文件产生一个IBinder接口,并把它保存在项目的gen/目录下。Service必须适当的实现IBinder接口。客户端应用可以bind到service并调用来自于IBinder的方法来执行IPC。

要使用AIDL来创建一个bounded service,则遵从以下几个步骤:

  • 创建.aidl文件/a>这个文件通过方法签名定义了编程接口。
  • 实现接口Android SDK工具基于.aidl文件以Java编程语言产生一个接口。这个接口具有一个名为Stub的内部抽象类,该抽象类扩展了Binder并实现了来自于你的AIDL接口的方法。你必须扩展Stub类并实现那些方法。
  • 将接口暴露给客户端实现一个Service并覆写onBind()来返回你的Stub类的实现。

注意:在你首次发布你的AIDL接口之后,对它的任何修改都必须要保持向后兼容,以避免破坏使用了你的service的其他应用。即,由于你的.aidl文件必须被复制到其他的应用以使它们能够访问你的service的接口,你必须维护对于原始接口的支持。

 

 

1. 创建.aidl文件

AIDL使用了一种简单的语法供你声明一个接口,接口可以有一个或多个方法,每个方法可以接收一些参数并返回值。参数和返回值可以是任何类型,甚至是其他的AIDL-generated接口。

你必须使用Java编程语言构造.aidl文件。每个.aidl文件必须定义一个单独的接口,并且只需要接口声明和方法签名。

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

  • Java编程语言中的所有原始数据类型(比如int, long, char, boolean, 等等)
  • String
  • CharSequence
  • ListList中的所有元素必须是所列出的被支持的数据类型,或某种其它的AIDL-generated接口,或你已经声明的parcelables。一个List可能被用作一个”generic”类(比如,List<String>)。另外一边实际接收到的具体类总是一个ArrayList,尽管产生的方法使用List接口。
  • MapMap中的所有元素必须是所列出的被支持的数据类型,或某种其它的AIDL-generated接口,或你已经声明的parcelables。泛型maps,(比如那些形如Map<String,Integer>的maps)是不被支持的。另外一边实际接收到的具体类总是一个HashMap,尽管产生的方法使用Map接口。

对于没有列出的每种额外的类型你都必须包含一个import声明,即使它们定义在与你的接口相同的package。

 

当定义你的service接口时,请意识到:

  • 方法可以接收0个或多个参数,并返回一个值或void。
  • 所有的非原始数据类型需要一个指示标记来表明数据的流向。in, out, 或者inout(请参考下面的例子)。原始数据类型默认是in,其他则不是。注意:你必须把方向限制在真正需要的方向,因为对于参数的排列是昂贵的。
  • .aidl文件中的所有代码注释被包含进了产生的IBinder接口中(除了那些在import和package声明前面的注释)。
  • 只支持方法;你不能在AIDL中暴露static域。

这里有一个.aidl文件的例子:

 

把你的.aidl文件保存在你的工程的src/目录下,当你编译你的应用时,SDK工具会在你的工程的gen/目录下产生IBinder接口文件。所产生的文件名与.aidl文件名匹配,但扩展名为.java(比如,IRemoteService.aidl产生IRemoteService.java)。

如果你使用Eclipse,增量编译几乎可以立即产生binder类。如果你不使用Eclipse,Ant工具在你下一次编译你的应用时产生binder类——你应该在你写完.aidl文件之后立即通过ant debug (或ant release) 编译你的工程,以使你的代码可以再次链接产生的类。

 

 

2. 实现接口

当你编译你的应用时,Android SDK工具产生一个以你的.aidl文件命名的.java接口文件。产生的接口包含一个名为Stub的子类,它是一个它的父接口的抽象实现 (比如,YourInterface.Stub),并声明了来自于.aidl文件的所有方法。

注意:Stub也定义了一些辅助方法,最值得注意的就是asInterface()了,它接收一个IBinder (通常是传递给客户端的onServiceConnected()回调方法的那个)并返回一个stub接口的实例。参考调用一个IPC方法部分,来获取更多关于如何做强制类型转换的信息。

要实现产生自.aidl的接口,则扩展产生的Binder接口 (比如YourInterface.Stub)并实现继承自.aidl文件的方法。

这里有一个称为IRemoteService的接口(由上面的例子IRemoteService.aidl定义)的一个示例实现:

现在mBinder是一个Stub类的实例(一个Binder),它为service定义了IPC接口。在下一步中,这个实例会被暴露给客户端,以使它们能够与service交互。

当实现你的AIDL接口时有一些规则应该注意:

  • 进入的调用不保证在主线程执行,因此你需要从一开始就考虑多线程,并适当的构建你的service以实现线程安全。
  • 默认情况下,IPC调用是同步的。如果你知道service需要好多毫秒的时间来完成一个请求,则你不应该在activity的主线程中调用,因为它可能挂起应用(Android可能显示一个”Application is Not Responding“对话框)——你通常应该在客户端的另外一个线程中来调用它们。
  • 你throw的exceptions不会被发回调用者。

 

 

3. 将接口暴露给客户端

为你的service实现了接口之后,你需要把它暴露给客户端,以使它们可以bind到它。要将你的service暴露出来,则扩展Service并实现onBind()来返回一个你的实现了产生的Stub的类的实例(如前面的讨论)。这里是一个示例service,它将IRemoteService例子接口暴露给客户端。

 

现在当一个客户端(比如一个activity)调用bindService()来连接这个service时,客户端的onServiceConnected()回调将接收由service的onBind()方法返回的mBinder实例。

客户端必须也有访问接口类的权限,因此,如果客户端和service在不同的应用中,则客户端的应用必须具有一份.aidl文件的拷贝放在它的src/目录(其产生android.os.Binder接口——提供客户端访问AIDL方法的权限)下。

当客户端在onServiceConnected()回调中接收了IBinder,它必须调用YourServiceInterface.Stub.asInterface(service)来把返回的参数转换为YourServiceInterface类型。比如:

更多示例代码,请参考ApiDemos中的RemoteService.java类。

透过IPC传递对象

如果你有一个类,你想要通过IPC接口将它从一个进程发送到另一个进程,那么你可以那样做。然而,你必须确保你的类的代码在IPC通道的另一端也是可以访问的,并且你的类必须支持Parcelable接口。支持Parcelable接口是很重要的,因为它允许Android系统来把对象分解为可以被跨进程处理原始数据类型。

要创建一个支持Parcelable协议的类,你必须做这些事情:

  1. 使你的类实现Parcelable接口。
  2. 实现writeToParcel,它将对象的当前状态写入一个Parcel
  3. 给你的类添加一个static成员CREATOR,它是一个对象,并实现了Parcelable.Creator接口。
  4. 最后,创建一个.aidl文件,它声明了你的parcelable类 (如下面的Rect.aidl文件所显示的那样)。如果你在使用一个定制的编译过程,则不要把.aidl文件添加到你的build。类似于C语言中的头文件,这个.aidl文件不被编译。

AIDL使用代码中它产生的这些方法和成员来排序和解序你的对象。

比如,这里是一个Rect.aidl文件,用于创建一个parcelable的Rect类。

这里是一个Rect类如何实现Parcelable协议的例子。

Rect类中的成员排列组织相当简单。看一下Parcel上的其它方法,来了解你可以写入一个Parcel的其它种类的值。

警告:不要忘记从其它进程接收数据的安全隐患。在这个例子中,RectParcel读取4个数字,但你要确保这些数字在可接受的值的范围以内,而无论调用者要去做什麽。参考Security and Permissions来获取更多关于如何使你的应用更安全并远离病毒的信息。

 

 

调用一个IPC方法

这里是一个调用类调用一个AIDL定义的远程接口所要采取的步骤:

  1. 在项目的src/目录下包含.aidl文件。
  2. 声明一个IBinder接口的实例(基于AIDL而产生)。
  3. 实现ServiceConnection
  4. 调用Context.bindService(),传入你的ServiceConnection实现。
  5. 在你的onServiceConnected()实现中,你将接收一个IBinder实例(称为service)。调用YourInterfaceName.Stub.asInterface((IBinder)service)来将返回的参数转换为YourInterface类型。
  6. 调用你的接口中定义的方法。你应该总是捕捉DeadObjectException异常,当连接断开时会抛出这个异常;这是远程方法将会抛出的唯一的异常。
  7. 要断开连接,则通过你的接口的实例调用Context.unbindService()

关于调用IPC service的一些说明:

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

更多关于binding到一个service的信息,请阅读Bound Services文档。

这里是一些实例代码,演示了调用一个AIDL-created service,来自于ApiDemos工程中的Remote Service示例。

Done.