所有相关示例代码托管到 GitHub 上
1.基本概念
服务是一个在后台长时间执行操作而不需要用户界面的组件。服务可由其他组件启动,如 Activity 和 Service,即时切换到其他应用,服务仍可以在后台运行。此外,其他组件可以绑定到服务来与之交互, 甚至可以进行 IPC 通信。
启动服务:
由其他组件通过 startService() 启动服务,之后即可无限期运行,即使启动服务的组件被销毁也不会影响它的运行。启动服务一般执行的是单一的操作,而且不会将执行结果传递给调用方。
绑定服务
由其他组件通过 bindService() 绑定到服务,由 IBinder 接口实现客户端-服务端的交互。绑定了服务的组件销毁会取消绑定,如果所有绑定了服务的组件取消服务后,那么绑定服务就会被销毁。
可以同时实现两种服务,问题在于是否实现了 onStartCommand() 和 onBind() 方法回调,注意,服务默认运行在其托管进程的主线程上,如需执行耗时的操作,应该开启新线程,避免 ANR。
2.使用服务还是线程?
服务是一种没有与用户交互也可以执行操作的组件。如果需要在主线程外执行工作,而这种工作是用户与应用交互时才需要的,那么此时应该创建新线程而不是服务,比如在一个 Activity 界面运行同时播放音乐。
3创建服务
继承 Service 并实现其必要的回调方法,然后在清单文件声明服务。
一些重要的回调方法:
onCreate(): startService 或 bindService 后回调的第一个方法,进行一些初始化的操作,比如新线程的创建。只要服务没被销毁,只执行一次。
onStartCommand(): 只有通过 startService() 启动的服务才会调用到,一旦调用到此方法服务便开始工作,并可无限执行下去,因此如果服务工作完成后应该主动调用 stopSelf() 或由其他组件调用 stopService() 来停止服务的执行。
onBind():只有通过 bindService() 启动的服务才会调用到,客户端绑定服务,该方法需要返回一个 IBinder接口实现类,完成服务和客户端的交互。
onDestry():服务不在使用将被销毁前调用,这里进行资源的清理工作
使用清单文件声明服务:
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
还可以通过添加 android:exported 属性并将其设置为 “false”,确保服务仅适用于您的应用。
4启动服务扩展类
①Service类:适用于所有服务的基类。默认运行于主线程中,若要执行耗时操作,需要开启新线程来避免 ANR。
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
注意 onStartCommand 返回值:
START_NOT_STICKY: 服务启动后被系统终止(比如服务执行期间从最近使用应用列表中杀死该应用),除非有挂起的 Intent(即PendingIntent)要传递,否则不会重建服务,即执行 onCreate 方法。
START_STICKY:服务启动后被系统终止,一定时间后会重建服务并执行 onStartCommand()方法,有挂起的 intent 传递时就传递挂起 intent,如果没有则会传递一个空的 intent到 onStartCommand() 中。这适用于不执行命令,但需要无限期运行并等待执行操作的媒体播放器,或类似服务。
START_REDELIVER_INTENT:服务启动后被系统终止,一定时间后会重建服务并执行 onStartCommand()方法,并传递之前执行的最后一个 intent 。挂起的 intent 依次传递。适用于主动执行应该立即恢复操作的服务,比如文件下载。
②IntentService类:Service 的子类。内部封装了一个线程逐一去处理所有启动请求。只需实现 onHandleIntent() 回调方法即可,注意此方法使用的是工作线程。如果不要求服务同时处理多个启动服务请求,这是最好的选择,大多数服务都不要求同时处理多个请求
public class HelloIntentService extends IntentService {
public HelloIntentService() {
super("HelloIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
只需要一个构造函数和 onHandleIntent()实现即可。如果真的需要重写其他回调方法,比如 onStartCommand() 等,那么最后必须调用父类的实现,如
@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);
}
③启动服务生命周期
首次启动服务: onCreate() –>onStartCommand()
非首次启动:直接 onStartCommand()
停止服务:一个 stopSelf() 或者 stopService() 即可停止全部请求
④启动服务的通信方式
a. 客户端通过 intent 传递数据到服务器
b.客户端创建一个 广播接收器,在 onReceive() 方法中通过方法参数 intent 获取从服务器传递过来的数据并进行处理。而服务只需要在执行完操作后把结果封装到 intent 并发送相应广播。
5.绑定服务
①生命周期:
客户端取消绑定前,只执行一次生命周期,即绑定后再次绑定是无效操作。而且只有首次被客户端绑定时才会执行 onCreate() 方法,除非被销毁才会再次执行。
bindService()–>onCreate()–> onBind()–> onServiceConnection()
②绑定服务的创建
a.继承 Binder 类
- 继承 Binder 类,里面定义可供客户端调用的公共方法,服务类实例,以及服务类里包含的其他实例。
- 在 onBind() 方法返回 Binder 实例
- 在 onServiceConnected() 方法对 Binder 进行获取,根据获得的 Binder 处理数据。
b.使用Messenger类
1.在服务端(1~3步): 创建一个 Handler 变量
2. 根据 Handler 变量构造生成 Messenger 变量
3. 在 onBind() 方法中返回 Messenger.getBinder() ;
4. 在客户端的 onServiceConnection() 中接收并构造出 new Messenger(IBinder service)
5. 通过 4 中构造的 Messenger 的send() 方法发送 Message 对象。对于 Message 的处理就在 1 中创建的 Handler 的 HandleMessage() 中。
c.使用AIDL
- 创建一个.aidl文件,会在build/generaed/source/aidl/… 生成对应的 .java 文件。
- 服务器端创建 1 中生成的 java 文件的内部类 Stub变量,并对实现相应的接口方法,然后在 onBind() 中返回该变量。
- 把整个main/aidl 文件复制到客户端中的 main 目录下。在 onServiceConnected() 方法通过内部类 Stub 的 asInterface(IBinder) 获得对应的接口实现类,进行方法调用
③Messenger 与 AIDL 的比较
使用 Messenger 比 AIDL 简单,因为 Messenger 的底层是 handler 实现,会把每个请求存入到队列当中,一次处理一个请求。而 AIDL 则会同时发送多个请求,因此在服务端必须对多线程进行处理。
参考资料:
服务相关官方文档,需翻墙
绑定服务的官方介绍,需翻墙
AIDL 官方文档,需翻墙
Android中通过Messenger与Service实现进程间双向通信