Service服务是一个应用程序组件,它可以在后台执行长时间运行的操作。它不提供用户界面。一旦启动,即使是在用户切换到另一个应用程序之后,一个服务可能会继续运行一段时间。此外,其他组件可以绑定到Service,并与其交互,甚至执行进程间通信(IPC)。Service在其托管进程的主线程中运行,该服务不会创建自己的线程,在程序员没有另行指定的情况下,Service也不会在单独的某一进程中运行。因此,对于耗时操作的Service需要建立一个单独的线程,防止ANR。
主要分:前台服务(Foreground),后台服务(Background),绑定服务(Bound)三种。
当应用程序组件通过调用bindService()绑定到服务时,服务被绑定。
绑定服务提供了一个客户机-服务器接口(IBinder接口),允许组件与Service进行发送请求、接收结果,甚至跨进程间通信(IPC)等交互。绑定服务仅在另一个应用程序组件绑定到它时才运行。多个组件可以同时绑定到服务,但当所有组件都解除绑定时,服务将被销毁。
虽然本文档通常分别讨论已启动服务和绑定服务,但您的服务可以以两种方式工作:它可以启动(立即运行),也可以允许绑定,也可以两者同时运行。这只是看是否实现了一些回调方法的问题:onStartCommand()和onBind()。
无论服务是启动或绑定或两者都启动,任何应用程序组件都可以通过使用Intent来使用该服务。
如果必须在主线程之外执行工作,但只有在用户与应用程序交互时,才应该在另一个应用程序组件的上下文中创建一个新线程。例如,如果您想播放一些音乐,但只有在您的活动正在运行时,您就可以在onCreate()中创建一个线程,在onStart()中启动线程,在onStop()中停止线程。
要创建Service,您必须创建Service的子类或使用其现有的子类。在实现中,您必须重写一些处理Service生命周期关键方面的回调方法,并提供一种机制允许组件绑定到Service上。
public int onStartCommand(Intent intent, int flags, int startId)
当另一个组件请求启动服务startService (Intent service)时,系统OS将调用此方法。当此方法执行时,服务将启动,并且可以在后台运行。如果您实现了这个功能,那么您有责任在服务工作完成时通过调用stopSelf()或stopService()来停止服务。
如果您只想提供绑定,则不需要实现此方法。
public abstract IBinder onBind(Intent intent)
当另一个组件请求启动服务bindService()时,系统OS将调用此方法。在此方法的实现中,必须提供一个接口,客户端通过此方法返回的IBinder与Service进行通信。
必须始终实现此方法;如果不允许绑定,则应该返回null。
public void onCreate()
在最初创建服务时,系统会调用此方法来执行一次性设置过程。这个过程早于onStartCommand()和onBind()。如果该服务已在运行,则不调用此方法。不要直接调用此方法。
public void onDestroy()
当服务不再使用并正在被销毁时,系统将调用此方法。服务应该实现这个功能来清理任何资源,如线程、注册的侦听器或接收器。这是该服务接收到的最后一次调用。
绑定服务是客户机-服务器接口模型中的服务器部分。它允许组件绑定到服务,执行发送请求、接收响应,等进程间通信(IPC)。绑定服务通常只在另一个应用程序组件绑定服务时运行,并且不在后台运行。
绑定服务是Service类的实现,它允许其他应用程序绑定到它并与之交互。要为服务提供绑定,必须实现onBind()回调方法。此方法返回一个IBinder对象,该对象定义了客户端可用于与服务交互的编程接口。
如果允许启动和绑定服务两种形式同时存在,那么当服务已经启动时,系统不会在所有客户端解除绑定时销毁服务。相反,必须通过调用stopSelf()或stopService()来显式地停止该服务。
客户端通过调用bindService()来绑定到服务,当它实现时,它必须提供一个服务连接ServiceConnection()的实现,以监视与服务的连接。
bindService()的返回值指示请求的服务是否存在以及是否允许客户端访问它。当安卓系统创建客户端和服务之间的连接时,它会在ServiceConnection()中调用在onServiceConnected()。
public abstract boolean bindService(Intent service, ServiceConnection conn, int flags)
public interface ServiceConnection;
public abstract void onServiceConnected(ComponentName name, IBinder service)
bindService()是异步的,其操作会立即返回,不会将IBinder返回到客户端。客户端为了拿到IBinder,必须要实现ServiceConnection,并将其作为参数传递给bindService()。
ServiceConnection接口中的onServiceConnected()方法包括一个IBinder参数,客户端使用该参数与绑定的服务进行通信。
绑定服务实现中最重要的部分是onBind()回调方法。
在创建提供绑定的服务时,必须提供一个IBinder,它提供客户端可用于与服务交互的编程接口。有三种方法可以定义接口。
如果服务是应用程序的私有服务,并且与客户端在相同的进程中运行(这是常见的),那么应该通过继承Binder类并从onBind()返回它的实例来创建接口。
当服务只为自己的应用程序在台工作时,这是首选技术。不以这种方式创建接口的唯一原因是,服务被其他应用程序或跨不同的进程使用。
如果需要接口跨不同的进程工作,可以使用Message为服务创建一个接口。这是执行进程间通信(IPC)的最简单的方法,因为Message将所有请求队列到一个线程中,这样就不必将Service设计为线程安全的。
Android Interface Definition Language (AIDL)将对象分解为操作系统可以理解的原语,并跨进程封送它们以执行IPC。Message类的底层实现实际上也是AIDL。
Message在一个线程中创建一个包含所有客户端请求的队列,因此服务一次接收一个请求。但是,如果您希望您的服务同时处理多个请求,则可以直接使用AIDL。在这种情况下,服务必须是线程安全的,并且能够使用多线程。
要直接使用AIDL,必须创建一个编程接口的.aidl文件。AndroidSDK工具使用这个文件生成一个抽象类,该类实现接口并处理IPC,然后可以在您的服务中继承它。
在开始设计AIDL接口之前,请注意,对AIDL接口的调用是直接的函数调用。不应该对调用发生的线程进行假设。本地进程中的线程和远程进程,在调用AIDL时会有所区别:
必须使用Java编程语言语法在.aidl文件中定义AIDL接口,然后将其保存在托管该服务的应用程序(服务端)和绑定到该服务的任何其他应用程序(客户端)的源代码(src/)目录中。即.aidl文件是成对出现于服务-客户端。
当构建每个包含.aidl文件的应用程序时,AndroidSDK工具会基于.aidl文件生成一个IBinder接口,并将其保存在项目的gen/目录中(.aidl文件变成.java文件)。服务端必须实现适当的IBinder接口,客户端应用程序可以从IBinder接口绑定到服务和调用方法,以执行IPC。
要使用AIDL创建绑定服务,请执行以下步骤:
注意:Stub 还定义了一些辅助方法,最值得注意的是 asInterface(),它接受一个 IBinder(通常是传递给客户端的 onServiceConnected() 回调方法的那个)并返回一个 Stub 接口的实例。
在发布第一个AIDL版本之后,对AIDL接口所做的任何更改都必须保持向后兼容,以避免破坏使用服务的其他应用程序。也就是说,因为新的.aidl版本必须复制到其他应用程序,以便它们访问您的服务接口,所以必须保持对原始接口的支持。
在Android10(API级别29)中,可以直接在AIDL中定义Parcelable对象。也支持作为AIDL接口参数和其他Pacelable的类型。这样避免了手动编写编组代码和自定义类等额外工作。但是,这也将创建一个裸结构。
如果需要自定义访问器或其他功能,则应实现Parcelable。
package android.graphics;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect
{
int left;
int top;
int right;
int bottom;
}
上面的代码示例自动生成一个带有left/top/right/bottom字段的Java类。所有相关的编组代码都是自动实现的,并且该对象可以直接使用,而无需添加任何实现。
还可以通过IPC接口将自定义类从一个进程发送到另一个进程。但是,必须确保类的代码对IPC通道的另一边是可用的,并且类必须支持Parcelable接口。支持Parcelable接口是至关重要的,因为它允许Android系统将对象分解为可以跨进程封送的原语。
要创建支持Parcelable协议的自定义类,必须执行以下操作:
package android.graphics;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;
下面示例,表示 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 CREATOR = new Parcelable.Creator()
{
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, int flags)
{
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();
}
public int describeContents()
{
return 0;
}
}
一个AIDL示例:
// ILearningAIDL.aidl
package com.example.learningaidl;
// Declare any non-default types here with import statements
interface ILearningAIDL {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
通过Android SDK,将ILearningAIDL.aidl文件变成ILearningAIDL.java文件
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.example.learningaidl;
// Declare any non-default types here with import statements
public interface ILearningAIDL extends android.os.IInterface
{
/** Default implementation for ILearningAIDL. */
public static class Default implements com.example.learningaidl.ILearningAIDL
{
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, java.lang.String aString)
throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
com.example.learningaidl.ILearningAIDL
{
private static final java.lang.String DESCRIPTOR = "com.example.learningaidl.ILearningAIDL";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.learningaidl.ILearningAIDL interface,
* generating a proxy if needed.
*/
public static com.example.learningaidl.ILearningAIDL asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.learningaidl.ILearningAIDL))) {
return ((com.example.learningaidl.ILearningAIDL)iin);
}
return new com.example.learningaidl.ILearningAIDL.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_basicTypes:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.learningaidl.ILearningAIDL
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, java.lang.String aString)
throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.example.learningaidl.ILearningAIDL sDefaultImpl;
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.example.learningaidl.ILearningAIDL impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.learningaidl.ILearningAIDL getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble,
java.lang.String aString) throws android.os.RemoteException;
}