一.Service 的概念及作用
概念
由于ANR
对Activity
和BroadcastReceiver
响应时间的限制(Activity对事件响应不超过5秒
,BroadcastReceiver执行不超过10秒
),使得在其中都不适合执行较耗时操作,这样像网络、数据库、复杂计算这类耗时操作的执行就需要一个组件来承担。Service作为Android四大组件之一,其功能之一就是执行的耗时操作。
功能
- 执行需要长时间运行的操作,这个操作不与用户进行交互,如网络下载、大文件I/O、复杂计算。
- 应用内或应用间数据通信。Android每个应用程序都在自己的dalvik虚拟机中运行,一个应用是不允许访问其他应用的内存信息的,为此Android引入了Content Provider在不同应用间共享数据,BroadcastReceiver广播信息给不同应用程序,但Content Provider更多用于数据的共享,BroadcastReceiver广播的信息会被所有应用接收较耗费系统资源,对于两个应用间动态的进行交互还需要通过Service来完成
二、声明Service
如同其他组件一样,想要使用Service,必须在清单文件中对其进行声明
声明方式是添加 < service > 元素作为 < application > 元素的子元素
例如:
<service
android:name="string"
android:description="string resource"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:permission="string"
android:exported=["true" | "false"]
android:isolatedProcess=["true" | "false"]
>
</service>
属性 | 说明 |
name | 服务类的完全限定名 |
description | 对服务进行描述,属性值应为对字符串资源的引用,以便进行本地化 |
directBootAware | 设置是否可以在用户解锁设备之前运行,默认值为“false” |
enabled | 设置是否可以由系统来实例化服务。< application >元素有自己的enabled属性,适用于包括服务在内的所有应用程序组件。要启用服务,< application >和< service >属性必须都为“true”(默认情况下都为true)。如果其中一个是“false”,则服务被禁用 |
icon | 服务的图标,属性值应是对drawable资源的引用。如果未设置,则将使用应用程序图标 |
label | 可以向用户显示的服务的名称,属性值应是对字符串资源的引用 |
permission | 设定组件必须具有的权限,得以启动服务或绑定服务。如果startService(),bindService()或stopService()的调用者没有被授予此权限,则该方法将不会工作,并且Intent对象不会传递到服务中 |
process | 用来运行服务的进程的名称。通常,应用程序的所有组件都运行在应用程序创建的默认进程中,它与应用程序包名具有相同的名称。 < application >元素的process属性可以为所有组件设置不同的默认值,但组件可以使用自己的进程属性覆盖默认值,从而允许跨多个进程扩展应用程序 |
isolatedProcess | 设置该服务是否作为一个单独的进程运行,如果设置为true,此服务将在与系统其余部分隔离的特殊进程下运行,并且没有自己的权限,与它唯一的通信是通过服务API(绑定和启动 |
exported | 设置其他应用程序的组件是否可以调用本服务或与其交互,如果可以,则为“true”。当值为“false”时,只有同一个应用程序或具有相同用户ID的应用程序的组件可以启动该服务或绑定到该服务。该属性的默认值取决于服务是否包含Intent filters。没有任何过滤器意味着它只能通过指定其确切的类名来调用,这意味着该服务仅用于应用程序内部使用(因为其他人不知道类名)。所以在这种情况下,默认值为“false”。 另一方面,如果存在至少一个过滤器,意味着该服务打算供外部使用,因此默认值为“true” |
三.启动服务
启动服务的俩种方式
- startService
- bindService
startService
定义
使用startService()方法启用服务后,调用者与服务之间没有关连
。调用者直接退出而没有调用stopService的话,Service会一直在后台运行。
使用场景:
因为这种方式Service
无法与外部进行方便的动态交互,所以适合做后台服务,如网络下载(用户通过Intent传入Url到Service,推荐使用IntentService).
启动流程
- 首次启动
Service
调用onCreate
->onStartCommand
- 重复调用
startServer()
,只会引起onStartCommand
被多次调用
结束方式
调用Context.stopService()结束服务
bindService
定义
使用bindService()方法启用服务,调用者与服务绑定在一起了
,调用者一旦退出,服务也就自动终止。
使用场景:
应用内通信,如音乐播放器,在服务中控制播放器的播放、暂停、停止,在Activity中通过对服务操作控制播放器。
启动流程
- 如果调用前服务没有被创建,则会引起onCreate()->onBind();
- 如果已被创建但没有被绑定,则会引起onBind();
- 如果服务已被绑定,则多次调用bindService并不会引起onCreate()和onBind()被多次调用。
结束方式
- 调用者退出,系统会自动调用服务到
onUnbind()
->onDestroy()
方法。 - 如果调用者希望与正在绑定的服务解除绑定,可以调用
context.unbindService()
,该方法会导致系统调用服务的onUnbind()
–>onDestroy()
方法。
注意:
- 如果第一次调用
startService()
, 系统会onCreate()
->onStartCommand()
;此时调用bindService
,只会onBind()
,如果第一次调用bindService()
, 系统会onCreate()
->onBind()
;此时调用startService
, 只会onStart()
; - 如果
start
和bind
都调用了, 那么一旦服务被绑定(只要成功调用了bindService
, 不管是先start
后bind
还是先bind
后start
, 就必须调用unbindService
和stopService
才能终止服务
4.异步服务IntentService
IntentService简介
IntentService是Service的子类,比普通的Service增加了额外的功能。先看Service本身存在两个问题:
- Service不会专门启动一条单独的进程,Service与它所在应用位于同一个进程中;
- Service也不是一个新线程,因此不应该在Service中直接处理耗时的任务,为此我们可以在Service中自己新建线程去执行耗时操作,不过Android系统引入了IntentService方便的解决了这个问题
IntentService特征
- 会创建子线程来处理所有的Intent请求;
- 会创建子线程来处理onHandleIntent()方法实现的代码,无需处理多线程问题
- 当操作完成时,IntentService会自动停止,我们不用手动停止 Service
- 为Service的onBind()提供默认实现,返回null;
- 省去了在 Service 中手动开线程的麻烦
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService() {
super("MyIntentService");
}
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate: 创建了 ");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand: 启动 ");
/*
* 这里返回状态有三个值,分别是:
* 1、START_STICKY:当服务进程在运行时被杀死,
* 之后, 系统会尝试重新创建服务;
*
* 2、START_NOT_STICKY:当服务进程在运行时被杀死,
* 并且没有新的Intent对象传递过来的话,系统将会把它置为started状态,
* 但是系统不会重新创建服务,直到startService(Intent intent)方法再次被 调用;
*
* 3、START_REDELIVER_INTENT:当服务进程在运行时被杀死,
* 它将会在隔一段时间后自动创建,
* 并且最后一个传递的Intent对象将会再次传递过来。
*/
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind: 绑定服务 ");
return new MyBind();
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
try {
Thread.sleep(20000);
Log.e(TAG, "onHandleIntent: 执行耗时操作");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy: 关闭服务");
}
public String getRiQi() {
return "今天8月6号";
}
class MyBind extends Binder {
public MyIntentService getMyService1() {
return MyIntentService.this;
}
}
}
bt_fw.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
intent=new Intent(MyServiceActivity.this,MyIntentService.class);
startService(intent);
bindService(intent,serviceConnection,BIND_AUTO_CREATE);
}
});
注意:一定要用startService 的方式启动服务,否则不会执行onHandleIntent 方法。