Service简介
Service是什么
Service是一个应用组件,它用来在后台完成一个时间跨度比较大的工作,且没有关联任何界面。
Service的生命周期方法在主线程运行。
使用场景
service用于在后台完成用户指定的操作。
- 访问网络;
- 播放音乐;
- 文件IO操作;
- 大数据量的数据库操作;
特点
- Service在后台运行,不用于用户进行交互。
- 即使应用退出,服务也不会停止。
- 在默认情况下,Service运行在应用程序的主线程/UI线程中,如果需要在Service中处理一些网络连接等耗时的操作,需要将这些任务放在子线程中处理,避免阻塞用户界面。
分类
Local Service(本地服务)
Service对象与Service的启动者在同个进程中运行,两者的通信是进程内通信。
Remote Service(远程服务)
Service对象与Service的启动者不在同一个进程中运行,这时存在一个进程间通信的问题,Android专门为此设计了AIDL来实现进程间通信。
Service的生命周期
Service继承了Context
public class MyService extends Service {
private static String TAG = "JJWorld.MyService";
public MyService() {
Log.i(TAG,"MyService..");
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
Log.i(TAG,"onBind...");
return new Binder();
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"onCreate..");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG,"onDestroy..");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG,"onStartCommand...");
return super.onStartCommand(intent, flags, startId);
}
}
注意:
如果当前service已经启动,再次调用startService,service生命周期重走onStartCommand;
- 多个服务启动请求会导致多次对服务的 onStartCommand() 进行相应的调用。但是,如要停止服务,只需一个服务停止请求(使用stopSelf() 或 stopService())即可。
Android链接可见。
Service使用
使用清单文件声明服务
如同对 Activity 及其他组件的操作一样,您必须在应用的清单文件中声明所有服务。
如要声明服务,请添加 元素作为 元素的子元素。下面是示例:
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
启动与停止
一般启停
startService与stopService
startService(new Intent(this,MyService.class));
stopService(new Intent(this,MyService.class));
绑定启停
bindService与unbindService
if (serviceConnection == null){
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG,"onServiceConnected..");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG,"onServiceConnected..");
}
};
bindService(new Intent(this, MyService.class), serviceConnection,BIND_AUTO_CREATE);
}else {
Log.i(TAG,"you have already bind service");
}
if (serviceConnection!=null){
unbindService(serviceConnection);
serviceConnection = null;
}else {
Log.i(TAG,"you have not bind service");
}
停止服务
启动服务必须管理自己的生命周期。换言之,除非必须回收内存资源,否则系统不会停止或销毁服务,并且服务在 onStartCommand() 返回后仍会继续运行。服务必须通过调用 stopSelf() 自行停止运行,或由另一个组件通过调用 stopService() 来停止它。
为避免浪费系统资源和消耗电池电量,请确保应用在工作完成之后停止其服务。如有必要,其他组件可通过调用 stopService() 来停止服务。即使为服务启用绑定,如果服务收到对 onStartCommand() 的调用,您始终仍须亲自停止服务。
创建绑定服务
绑定服务允许应用组件通过调用 bindService() 与其绑定,从而创建长期连接。此服务通常不允许组件通过调用 startService() 来启动它。
如要创建绑定服务,您需通过实现 onBind() 回调方法返回 IBinder,从而定义与服务进行通信的接口。然后,其他应用组件可通过调用bindService()来检索该接口,并开始调用与服务相关的方法。服务只用于与其绑定的应用组件,因此若没有组件与该服务绑定,则系统会销毁该服务。您不必像通过onStartCommand() 启动的服务那样,以相同方式停止绑定服务。
多个客户端可以同时绑定到服务。完成与服务的交互后,客户端会通过调用 unbindService() 来取消绑定。如果没有绑定到服务的客户端,则系统会销毁该服务。
创建启动服务
IntentService
当不考虑让服务同时处理多个任务时,可以使用扩展 IntentService 类
IntentService 类会执行以下操作:
- 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。
- 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。
- 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。
- 提供 onBind() 的默认实现(返回 null)。
- 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。
IntentService示例
public class HelloIntentService extends IntentService {
private String TAG = "JJWorld.HelloIntentService";
public HelloIntentService() {
super("HelloIntentService");
}
@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.
try {
Thread.sleep(5000);
Log.i(TAG,"onHandleIntent...");
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"onCreate....");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.i(TAG,"onStartCommand....");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG,"onDestroy....");
}
}
调用代码
Log.i(TAG,"testService...");
//线程工厂
ThreadFactory factory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
return thread;
}
};
Activity activity = this;
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(5),
factory, new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 10; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
startService(new Intent(activity,HelloIntentService.class));
}
});
}
表现
2023-02-10 19:55:45.973 24630-24630/cn.jj.autostart I/JJWorld.MyServiceActivity: testService...
2023-02-10 19:55:45.991 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onCreate....
2023-02-10 19:55:45.992 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onStartCommand....
2023-02-10 19:55:45.993 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onStartCommand....
2023-02-10 19:55:45.994 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onStartCommand....
2023-02-10 19:55:45.997 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onStartCommand....
2023-02-10 19:55:46.007 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onStartCommand....
2023-02-10 19:55:46.011 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onStartCommand....
2023-02-10 19:55:46.014 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onStartCommand....
2023-02-10 19:55:46.017 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onStartCommand....
2023-02-10 19:55:46.020 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onStartCommand....
2023-02-10 19:55:46.036 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onStartCommand....
2023-02-10 19:55:50.992 24630-12566/cn.jj.autostart I/JJWorld.HelloIntentService: onHandleIntent...
2023-02-10 19:55:55.995 24630-12566/cn.jj.autostart I/JJWorld.HelloIntentService: onHandleIntent...
2023-02-10 19:56:01.003 24630-12566/cn.jj.autostart I/JJWorld.HelloIntentService: onHandleIntent...
2023-02-10 19:56:06.010 24630-12566/cn.jj.autostart I/JJWorld.HelloIntentService: onHandleIntent...
2023-02-10 19:56:11.018 24630-12566/cn.jj.autostart I/JJWorld.HelloIntentService: onHandleIntent...
2023-02-10 19:56:16.024 24630-12566/cn.jj.autostart I/JJWorld.HelloIntentService: onHandleIntent...
2023-02-10 19:56:21.033 24630-12566/cn.jj.autostart I/JJWorld.HelloIntentService: onHandleIntent...
2023-02-10 19:56:26.042 24630-12566/cn.jj.autostart I/JJWorld.HelloIntentService: onHandleIntent...
2023-02-10 19:56:31.048 24630-12566/cn.jj.autostart I/JJWorld.HelloIntentService: onHandleIntent...
2023-02-10 19:56:36.056 24630-12566/cn.jj.autostart I/JJWorld.HelloIntentService: onHandleIntent...
2023-02-10 19:56:36.066 24630-24630/cn.jj.autostart I/JJWorld.HelloIntentService: onDestroy....
Remote Service(远程服务)
进程间通信AIDL
每个应用程序都运行在自己的独立进程中,并且可以启动另一个应用进程的服务,而且经常需要在不同的进程间传递数据对象。
Android平台上,一个进程不能直接访问另一个进程的内存空间,所以想要对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。
AIDL(Android Interface Definition Language)
用于生成可以在Android设备上两个进程之间进行进程间通信的代码。
远程服务客户端
绑定
Intent intent = new Intent("cn.jj.autostart.service.MyService.action");
if (serviceConnection == null){
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Log.i(TAG,"bindRemoteService....");
bindService(intent, serviceConnection,BIND_AUTO_CREATE);
}else {
Log.i(TAG,"bindRemoteService but serviceConnection is null....");
}
解绑
if (serviceConnection!=null){
unbindService(serviceConnection);
serviceConnection = null;
Log.i(TAG,"unbindRemoteService....");
}else {
Log.i(TAG,"unbindRemoteService but serviceConnection not build....");
}
可能遇到错误
java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=cn.jj.autostart.service.MyService.action }
解决
对隐式意图添加包名
Intent intent = new Intent("cn.jj.autostart.service.MyService.action").setPackage("cn.jj.autostart");