(API GUIDE 5)Service(服务)
来源:互联网 发布:js为div添加事件 编辑:程序博客网 时间:2024/05/23 01:32
服务
服务是一个可以执行后台长期运行操作而没有提供用户接口的应用成分。另一个应用的成分可以启动一个服务并且即使用户转换到另一个应用后它将会继续在后台执行。而且,一个成分可以绑定到一个服务以便和它进行交互甚至是进程间通信。例如,一个服务可能处理网络传输,播放音乐,执行文件I/O操作,或者和一个内容提供者交互,所有这些都来自于后台。
服务从本上来说有以下两种形式:
启动的:服务当一个应用成分(例如一个活动)调用startService()启动它的时候“启动”。一旦启动,服务可以在后台不确定地运行。通常,一个启动的服务执行一个单一的操作并且不会返回一个结果给调用者。例如,可能通过网络下载或者上传一个文件。当操作结束,服务应该自行停止。
绑定:当一个应用成分通过调用bingService()绑定到一个服务的时候,这服务称作“绑定”。一个绑定的服务提供一个客户服务器接口允许成分和服务交互,发送请求,获得结果,甚至用(IPC)跨进程通信。衣服我绑定的服务运行仅当另一个应用成分绑定到它。多个成分可以立即绑定到服务,但是当他们所有都解绑定之后,服务就被销毁。
即使这篇文档通用地分开讨论了这两种服务,你的服务也可以工作在两个方式下——可以被启动(不确定地运行)也可以被绑定。仅仅是你是否实现了回调方法:onStartCommand()启动它,onBind()来允许绑定。
补录你的应用是否被启动,绑定,或者都执行,任何应用成分可以使用服务(甚至来自一个不同的应用),和成分使用一个活动相同——通过使用一个Intent启动它。然而,你可以声明服务为私有,在manifest文件中,来自其他应用的阻塞式访问。在Declaring the service in the manifest部分里有更多的讨论。
注意!!服务运行在宿主进程的一个主线程中——服务没有生成它的自己的线程并且没有运行在单独的进程中(除非你指定了其他的)。这意味着,如果你的服务准备做任何CPU紧张的工作或者阻塞操作(例如MP3回放或联网),你应该在服务中生成一个新的线程来做这项工作。通过使用一个独立的线程,你将会减少应用不响应的错误风险并且应用的主线程对于用户和活动交互可以保持专用。
基础
为了生成一个服务,你必须生成一个Service的子类(或者是一个它的已存在的子类)。在你的视线中,你需要重载一些处理服务生命周期的关键部分的回调方法并且为成分提供一个绑定服务的机制,如果合适的话。你应该重载的最重要的回调方法有:
onStartCommand()
当另一个成分,如活动,请求服务通过调用startService()被启动的时候调用这个方法。一旦这个方法执行,服务被启动并且可以在后台不确定地运行。如果你实现这个,工作结束后通过调用stopSelf()或者stopService()来停止这个服务也是你的责任。(如果只希望提供绑定,你不需要实现这个方法)。
onBind()
当另一个成分希望通过调用bindService()绑定这个服务(例如执行RPC(远程进程调用,译者注))的时候系统调用这个方法。在你自己的这个方法的实现中,你必须通过返回一个IBinder提供客户用来和服务通信的接口.你总是必须要实现这个方法,但是如果你没有希望允许绑定,你应该返回一个空。
onCreate()
当服务第一次被生成的时候系统调用这个方法来执行一次性的安装过程(在它调用onStartCommand()或者onBind()的时候)。如果服务已经运行,这个方法就不用再被调用。
onDestroy()
服务不再被使用并且被销毁的时候系统调用这个方法。你的服务应该实现这个方法来清理任何资源例如线程,注册坚挺者,接收者,等等。这是服务接收到的最后一个调用。
如果一个成分通过调用startService()(导致一个对onStartCommand()的调用)启动服务,然后服务保持运行指导它使用stopSelf()停止它自己或者另一个成分停止它通过调用stopService().
如果成分调用bindService()生成一个服务(并且onStartCommand()没有被调用),那么服务只在成分绑定到它的期间运行。一旦服务从所有的客户端解绑定,系统销毁它。
安卓系统仅当内存十分低并且他必须为拥有用户焦点的活动恢复资源的时候将会强制停止一个服务。如果服务被绑定到一个拥有用户焦点的活动,那么它不大可能被杀死,而且如果服务被声明运行在前台(在后面讨论),那么它将会几乎永远不会被杀死。反之,如果服务被启动并且长时间运行,那么系统将会随着时间在后台任务降低它的位置并且服务将会变得非常容易被杀死——如果你的服务被启动,那么你必须设计它能够优雅地处理被系统重启。如果系统杀死你的服务,资源只要一变得合适(即便这也依赖于你从onStartCommand()返回的值),它立刻重新启动服务。更多的关于什么时候系统可能销毁一个服务的信息,见Processes and Threading 文档。
补充:你应该使用服务还是线程?
服务仅仅是一个可以运行在后台(甚至在用户和应用没有交互时候)的成分。这样,你应该生成一个服务仅当那是你需要的时候。
如果你需要执行在你的主线程之外工作,但是只是当用户正在和你的应用交互的时候,那么你可能应该生成一个线程。例如,如果你希望播放音乐,但是仅当你的活动正在运行,你可能用onCreate()生成一个线程,用inStart()启动它,然后用onStop()停止它。也可以考虑使用AsyncTask或者HandlerThread,而不是额外的Thread类。见Processes and Threading文档获取更多关于线程的信息。
记住如果你确实使用了一个服务,它将会默认地在你的应用的主线程里运行,所以如果它执行紧张的或者阻塞的操作的时候,你应该仍然在服务里生成一个新的线程。
在下面的部分,你将会看见你如何生成每种类型的服务以及如何从其他的应用成分使用它。
在manifest声明一个服务:
像活动(以及其他的成分)一样,你必须在你的应用的manifest文件中声明所有的服务。
为了声明你的服务,添加一个<service>元素作为<applicaiton>元素的一个孩子。例如:
<manifest ... >
...
<application ... >
<serviceandroid:name=".ExampleService"/>
...
</application>
</manifest>
见<service>元素引用获取更多的关于在manifest文件里声明你的服务的信息。
你可以在<service>元素里添加其他的特征来定义诸如启动服务需要的权限和服务应该运行的进程的特征。android:name特征是仅有的请求的特征——它指定了服务的类名。一旦你出版了你的应用,你不应该改变这个名字,因为如果你这样做了,你可能由于依赖显式的intents启动或者绑定服务而破坏代码(阅读博客,Things That Cannot Change)。
为了确保你的应用是安全的,当启动或者绑定你的服务的时候总是使用一个显式的intent并且不要为服务声明Intent过滤器。如果你在启动哪个服务上允许一些歧义很重要,你可以为你的服务提供Intent过滤器并且从intent中排除成分名字,但是你之后必须使用setPackage()为intent设置包,为目标服务提供足够的确定性。
还有,你可以通过添加android:exported特征并将它设置为“false”确保你的服务仅仅对于你的应用是合适的。这有效地阻止其他的应用启动,甚至当使用一个显式的intent的时候。
生成一个启动的服务
一个启动的服务是另一个成分通过调用startService()启动的成分,导致一个对于服务的onStartCommand()方法的调用。
当服务被启动,它有一个依赖于启动它的成分的生命周期并且服务可以不确定地运行在后台,甚至如果启动它的成分被销毁。这样的情况下,服务应该当工作通过调用stopSelf()完成之后停止自己,或者其他的成分可以通过stopService()停止它。
一个应用成分例如一个活动可以通过调用startService()启动服务并且传递一个指定了服务并包括任何服务要用到的数据的Intent。服务用onStartCommand()方法接受这个Intent,连接到网络并且执行数据库传输。当传输结束,服务停止它自己并且被销毁。
注意!!服务和应用运行在同样的进程中,并且服务在应用中被声明而且是在主线程中声明。所以,如果你的服务在用户和一个来自于同一个应用的活动进行交互的时候执行紧迫的或者阻塞的操作,服务将会减慢活动性能。为了避免影响到应用的性能,你应该启动一个新的服务里的线程。
传统上,有两个你可以拓展来生成一个启动的服务的类:
Service:
这是对于所有的服务的基本的类。当你拓展这个类,你生成一个完成所有服务的工作的新的线程,因为服务使用你的应用的主线程,默认地,这可能会减慢你的应用正在运行的任何活动。
IntentService
这是一个使用一个工作线程来处理所有启动请求(一次一个)的服务的子类。如果你没有要求你的服务同时处理多个请求,这是最好的选择。你所要做的仅仅是实现一个onHandleIntent(),它为每一个启动请求接受intent从而你可以做后台的工作。
下面部分描述你如何可以使用这些类中任何一个来实现你的服务。
扩展IntentService类:
因为大多数启动的服务不需要同时处理多个请求(否则可能就是一个危险的多线程脚本),如果你用IntentService类来实现你的服务可能是最好的。
IntentService()做如下的事:
1、生成一个默认的独立于你的应用的主线程的工作线程来执行所有的传递给onStartCommand()的intents。
2、生成一个一次传递一个intent给你的onHandleIntent()实现的工作队列,使得你不用担心多线程的问题。
3、在所有的启动请求已经被处理之后停止服务,所以你不需要调用stopSelf().
4、提供返回值是空的onBind()的默认的实现。
5、提供一个默认的onStartCommand()实现,发送intent给工作队列后发送给你的onHandleIntent()实现。
所有这些加起来就是你需要做的是实现onHandleIntent()来做客户提供的服务(可能,你也需要为服务提供一个小的构造器)
这里是IntentService实现的一个例子:
public classHelloIntentService extendsIntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
publicHelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent(Intent intent){
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime =System.currentTimeMillis()+ 5*1000;
while (System.currentTimeMillis()< endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch(Exception e){
}
}
}
}
}
这就是所有你需要做的:一个构造器和一个onHandleIntent()的实现。
如果你决定也要重载其他的回调方法,例如onCreate(),onStartCommand(),或者onDestroy(), 确保调用父类实现,以便IntentService可以正确地处理工作线程的生活。
例如,onStartCommand()必须返回默认的实现(intent如何传递给onHandleIntent()):
@Override
public int onStartCommand(Intent intent,int flags, int startId){
Toast.makeText(this,"service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
除了onHandleIntent(),你不需要调用父类的仅有的方法是onBind()(而你只需要实现是否你的服务允许绑定)。
下一个部分,你将会就看到当拓展基本的Service类的时候同样种类的服务如何被实现,这会有更多的代码,但是如果你需要处理同时的启动请求它可能是合适的。
拓展Service类:
正如你在上一部分看到的,使用IntentService是你对于一个启动的服务的实现非常简单。何时如果你请求你的服务执行一’多线程(而不是通过一个工作队列的启动请求进程),那么你可以拓展Service类来处理每一个intent。
为了比较,下面的例子代码式Service的一个实现,执行了和上面使用IntentService完全一样的工作。那就是,对于每一个启动请求,它使用一个工作线程来执行工作并且一次性仅仅处理一个请求。
public classHelloService extendsService {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private finalclass ServiceHandlerextends Handler{
publicServiceHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg){
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime =System.currentTimeMillis()+ 5*1000;
while (System.currentTimeMillis()< endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch(Exception e){
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
@Override
public void onCreate(){
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = newHandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = newServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent,int flags, int startId){
Toast.makeText(this,"service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent){
// We don't provide binding, so return null
return null;
}
@Override
public void onDestroy(){
Toast.makeText(this,"service done", Toast.LENGTH_SHORT).show();
}
}
正如你看到的,比用IntentService需要更多的工作。
可是,由于你处理每个调用来onStartCommand()你自己,你可以同时执行多个请求。这个例子没有这样做,但是如果那是你想做的,那么你可以为每个请求生成一个新的线程并且立即运行他们(而不是等到前一个请求完成)。
注意到onStartCommand()方法必须返回一个整数。整数是一个描述系统应该如何在继续系统杀死的服务(如上面讨论,默认的IntentService实现为你处理这个,即使你能够修正它)。从onStartCommand()返回值必须是下面的常量之一:
START_NOT_STICKY
如果系统在onStartCommand()返回之后杀死服务,不要重新生成服务,除非有需要投递的等待着的intents.这是最安全的选择来避免在不必要的时候运行你的服务而且你的应用可以简单地重启任何没完成的工作。
START_STICKY
如果系统在onStartCommand()方法返回之后杀死了服务,重新生成服务并且调用onStartCommand(),但是不会重新投递最后一个intent。取而代之的是,系统使用一个空intent调用onStartCommand(),除非这里还有要启动服务的等待着的intents,在这种情况下,那些intents被投递。这对于媒体播放器(或者其他的没在执行指令但是正在以非确定的状态运行并且等待一个工作的服务)是合适的。
START_REDELIVER_INTENT
如果系统在onStartCommand()返回之后杀死了服务,重新生成服务并且用被投递给服务的最后一个intent调用onStartCommand()。任何等待着的intents被轮流投递。这对于那些应该被立即继续的活跃地执行一个工作的服务(例如下载一个文件)是合适的。
更多的关于这些返回值的细节,见每个常量的链接引用文档。
启动一个服务:
你可以从一个活动或者其他应用成分通过传递一个Intent(指定了要启动的服务)给startService()来启动一个服务.安卓系统调用服务的onStartCommand()方法并且传递intent给它。(你不应该直接调用onStartCommand())。
例如,一个活动可以使用一个带有显式的intent的startService()启动之前部分的示例服务(HelloService):
Intent intent =new Intent(this,HelloService.class);
startService(intent);
startService()方法立即返回并且安卓系统调用服务的onStartCommand()方法。如果服务没有准备好运行,系统首先调用onCreate(),然后调用onStartCommand().
如果服务没有也提供绑定,用startService()投递的intent是在应用成分和服务之间通信的唯一的模式。可是,如果你希望服务回送一个结果,那么启动服务的客户可以为广播生成一个PendingIntent(用getBroadcast())并且向启动服务的Intent中的服务投递它。服务之后可以使用广播来投递一个结果。
多请求来启动服务导致多个对应的服务的onStartCommand()调用。可是,只有一个请求来停止服务(使用stopSelf()或者stopService())被要求停止它。
停止一个服务:
一个启动的服务必须管理它自己的生命周期。那是,系统没有停止或者销毁服务除非它必须回复系统内存并且服务在onStartCommand()返回之后仍然在运行。所以,服务必须通过调用stopSelf()自己停止或者另一个成分可以通过调用stopService()停止它。
一旦请求用stopSelf()或者stopService()停止,系统尽可能快地销毁服务。
可是,如果你的服务并行处理多个对onStartCommand()的请求,那么你不应该在你完成一个启动请求的处理之后停止服务,因为你可能已经收到一个新的启动请求(在第一个请求的结尾停止会终止第二个)。为了避免这个问题,你可以使用stopSelf(int)来确保你的停止服务请求总是基于最近的启动请求。那就是,当你调用stopSelf(int),你传递启动请求的ID(投递给onStartCommand()的startId)给你的启动请求对应的位置。然后如果服务在你能够调用stopSelf(int)之前接收到一个新的请求,那么ID将不会匹配并且服务将不会停止。
注意!!当你的服务完成的时候,你的应用结束它是很重要的,为了避免浪费系统资源和消耗电池电量。如果必要,其他的成分可以通过调用stopService()结束服务。甚至你可以绑定服务,你必须自己结束服务如果它曾经接收到一个对于onStartCommand()的调用。
对于更多的关于一个服务生命周期的信息,见下面的关于Managing the Lifecycle of a Service部分。
生成一个绑定的服务
绑定服务是一个允许应用成分为了生成一个长期连接通过bindService()绑定到它的成分(通常不允许成分来通过调用startService()启动)。
当你希望与来自应用的服务和其他应用中的成分进行交互或者你希望向其他应用暴露你的应用的功能的时候你应该通过进程间通信(IPC)生成一个绑定的服务。
为了生成一个绑定的服务,你必须实现onBind()回调方法来返回一个为和服务通信定义了一个接口的IBinder。其他应用成分之后可以调用bindService()来恢复接口并开始调用服务上的方法。服务生存只是为了服务绑定到它的应用成分,所以当没有成分绑定的时候,系统把服务销毁(你不需要像停止一个用onStartCommand()启动的服务那样停止一个绑定的服务)。
为了生成一个绑定的服务,你需要做的第一件事就是定义指定了一个客户如何能够和服务通信的一个接口。这个在服务和客户之间的接口必须是一个IBinder的实现和你的服务必须从onBind()回调方法返回的东西。一旦客户接受IBinder,它可以开始通过那个接口和服务进行交互。
多个客户可以一次绑定到一个服务。当一个客户完成了和服务的交互之后,它调用unbindService()来解绑定。一旦没有客户绑定到服务,系统销毁服务。
这里有多重方式来实现一个绑定的服务并且实现比一个启动的服务更加复杂,所以对绑定的服务在Bound Services里。
给用户发送一个通知
一旦运行,服务可以使用Toast Notifications或者Status Bar Notifications通知用户事件发生。
一个toast通知是一个短暂出现在当前窗口表面的消息,当一个状态条通知在状态条提供一个带有信息的图标的时候,用户可以选择做一个动作(例如启动一个活动)。
通常,当一些后台工作完成了(例如一个文件下载完成)以及用户现在可以操作的时候一个状态条通知是最好的技术。当用户从拓展的试图选择通知的时候,通知可以启动活动(例如查看下载的文件)。
从Toast Notifications 或者Status Bar Notifications开发者导论获取更多信息。
在前台运行一个服务
一个前台服务是作为用户知道活跃的服务并且因此它不是系统由于低内存而需要杀死的候选服务。一个前台服务必须为状态条提供一个通知,被放置在“Ongoing”标题处,意味着通知不会被抛弃除非服务已经停止或者从前台移除。
例如,一个音乐播放器从一个服务播放音乐应该被设置运行在前台,因为用户可以显式地知道这个操作。状态条里的通知可能代表着当前的乐曲并且允许用户来启动一个活动来和一月播放器进行交互。
为了请求你的服务运行在前台,调用startForeground().这个方法需要两个参数:独一无二地定义着通知的一个整形变量和为状态条提供的Notification(通知)。例如:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent =new Intent(this,ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0, notificationIntent,0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
注意!!你传递给startForeground()的整数ID不能是0.
为了从前台移除服务,调用stopForeground()。这个方法需要一个布尔形参,标识是否也要移除状态条。方法不会停止服务。可是,如果你在服务仍然在前台运行的时候停止服务,那么通知也会被移除。
更多的关于通知的消息,见Creating Status Bar Notifications.
管理一个服务的生命周期
服务的生命周期比活动的更加简单。可是,注意你的服务如何被生成和销毁更加重要,因为一个服务可以运行在后台而不被用户知道。
服务生命周期——从被生成到被销毁——可以遵循下面两条路径:
1、启动的服务:
当另一个成分调用startService()的时候服务被生成。这个服务之后不确定地运行并需通过调用stopSelf()停止它自己。另一个成分也可以通过调用stopService()停止服务。当服务被停止,系统销毁它。
2、绑定的服务
另一个成分(客户)调用bindService()的时候服务被生成。客户然后和服务通过一个IBinder接口通信。客户可以通过调用unbindService()关闭连接。客户可以通过调用unbindService()关闭连接。多个客户可以绑定同样的服务并且当他们所有都解绑定之后,系统销毁服务。(服务不需要停止自己)。
这两种路径不是完全分离的。也就是,你可以绑定一个已经被startService()启动的服务。例如,一个后台可以通过使用指定要播放的歌曲的Intent调用startService()来启动。之后,可能当用户希望执行一些对播放器的控制或者获得关于当前歌曲的信息,活动可以通过调用bindService()绑定到一个服务。这样的情况下,stopService或者stopSelf()实际上不会停止服务直到所有的客户解绑定。
实现生命周期回调:
像一个活动一样,服务有生命周期回调方法,你可通过实现这些方法来监控改变服务的状态并在合适的时候执行工作。下面的框架服务说明了每一个生命周期的方法。
public classExampleService extendsService {
int mStartMode; // indicates how to behave if the service is killed
IBinder mBinder; // interface for clients that bind
boolean mAllowRebind;// indicates whether onRebind should be used
@Override
public void onCreate() {
// The service is being created
}
@Override
public int onStartCommand(Intent intent,int flags, int startId){
// The service is starting, due to a call tostartService()
return mStartMode;
}
@Override
public IBinder onBind(Intent intent){
// A client is binding to the service withbindService()
return mBinder;
}
@Override
public boolean onUnbind(Intent intent){
// All clients have unbound with unbindService()
return mAllowRebind;
}
@Override
public void onRebind(Intent intent){
// A client is binding to the service withbindService(),
// after onUnbind() has already been called
}
@Override
public void onDestroy() {
// The service is no longer used and is being destroyed
}
}
注意,不像活动的生命周期回调方法,你没有被要求调用这些回调方法的父类实现。
图二:服务的生命周期。左边的图像展示当服务被使用startService()生成时候的生命周期,右边图像展示了服务被bingService生成时候的生命周期。
通过实现这些方法,你可以监控服务生命周期的两个嵌套的循环:
1、一个服务的整个生存时间发生在onCreate()被调用和onDestroy()返回。像一个活动,服务用onCreate()做初始化安装并用onDestroy()释放所有剩余的资源。例如,一个音乐回放的服务可以用onCreate()生成负责播放音乐的线程,然后用onDestroy()停止线程。
onCreate()和onDestroy()方法被所有服务调用,不论是用startService()还是bindService()生成的服务。
2、一个服务的活跃的生存时间开始一个对于onStartCommand()或者onBind()的调用。每个方法被分别提交给传递给startService和bindService()的intent.
如果服务被启动,活跃的生存周期和整个生存周期同时结束(服务甚至在onStartCommand()返回之后仍然活跃)。如果服务被绑定,活跃的生存时间在onUnbind()返回的时候结束。
贴士:即使一个启动的服务通过调用stopSelf()或者stopService()被停止,也没有一个各自的服务的回调(没有onStop()回调)。所以,除非服务被绑定到客户,系统当服务停止的时候销毁它——onDestroy()是唯一被接受的回调。
图二说明了典型的服务的回调方法。即使图片分开了被stratService()生成的服务和那些通过bingService()生成的服务,记住任何服务,无论他是如何被启动的,可以潜在地允许客户绑定到它。所以,一开始用onStartCommand()(被一个客户调用startService())启动的一个服务依然可以接收一个对onBind()的调用(当一个客户调用bindService())。
更多的关于生成一个提供绑定的服务,见Bound Services 文档,包括了更多的关于在Managing the Lifecycle of a Bound Service 部分的的onRebind()回调方法的信息。
- (API GUIDE 5)Service(服务)
- [API GUIDE学习笔记]Service
- Android API Guide for Media Apps(四)—— 构建媒体浏览器服务(Building a Media Browser Service)
- Android API 指南 - 【服务 Service】
- 使用 Amazon Web 服务 API 创建 Office 2003 Research Service
- 【Service】使用有道翻译API构建翻译服务
- Facebook API Developers Guide
- Google Bookmarks API Guide
- API Guide之 Storage
- HBase Clinet API Guide
- HTTP API设计Guide
- Android API guide Notification
- DMA Engine API Guide
- API GUIDE ---- AIDL
- google API Design Guide
- API Guide:<action>
- API Guide:布局资源
- API Guide:自定义组件
- GCC 编译动态库和静态库
- 《Visual Studio 程序员箴言》读书笔记
- pom.xml文件中dependency中optional属性的作用
- 如何判断某个单据是否连续被两个特定人审批
- 异步保存程序运行日志
- (API GUIDE 5)Service(服务)
- JavaScript Table排序
- 随笔(2015.2)
- Android4.4 上层mediaPlayer 是如何调到Stagefright的
- LOG4J.PROPERTIES配置详解(转载)
- iOS面试题7(答案)
- 几种典型的分页sql,下面例子是每页50条,198*50=9900,取第199页数据。
- 桥梁模式
- uWSGI的安装与配置(官网摘录)