Android API Guides 阅读笔记(8)----Service

来源:互联网 发布:wms软件 编辑:程序博客网 时间:2024/05/16 10:18

Service是一个可以长时间在后台执行并且没有用户界面的应用程序组件,Service同Activity一样,也可以被设备上的其他应用程序中的组件调用。Service是运行在主线程(UI Thread)中的,所以,如果要做长时间的操作(比如下载,播放音乐等)应该在Service中新建线程或者使用IntentService,通过阅读这节内容,将会了解到如下:

申明一个Service:

和创建Activity类似,需要在清单文件中(AndroidManifest.xml)申明。

  • 为了保证Service只在自己的应用程序中运行,需要在< service >节点设置属性 android:exported的值为false

  • 为了应用程序的安全,请一定要用显示的Intent启动或绑定Service

在其他组件中调用Service的两种方式:

  • 通过startService()启动一个Service:
    一旦启动,便拥有其独立的生命周期,并且可独立的在后台无限运行(即使调用它的组件被销毁)
    使用这种方式创建的Service类会继承Service.class或者IntentService.class。

    • Service.class:继承这个类的Service,所有的操作默认都是在主线程(main/UI Thread)中进行,继承它的子类Service在被启动时会首先调用其中的onStartCommand()方法

    • IntentService:IntentService是Service的子类,通过继承这个类的Service是在其他线程中执行操作,继承它的子类Service在被启动时会首先调用其中的onHandleIntent()方法

    如果调用时需要传递一些数据,则通过Intent,像在Activity中一样添加数据,然后在onStartCommand()方法或者onHandleIntent()方法中获取Intent

  • 通过bindService()绑定一个Service:
    只要有组件与其绑定,就开始运行,可以同时和多个组件绑定,直到所有绑定它的组件都解绑(unbind)了才销毁,通过这种方式启动的Service会调用其中的onBind()

其实,一个Service既可以通过启动(start)运行,也可以通过绑定(bind)运行,这两种方式并不是完全分离的,有时候会结合着使用,也就是说,一个启动的Service,仍然可以被绑定,比如,通过startService()启动一个音乐播放器的Service,如果后来用户想控制这个音乐,比如获取正在播放的音乐的信息,就可以通过bindService()实现

Service的生命周期:

Service的生命周期是独立于调用它的组件的,也就是说一个组件一旦调用了这个Service,Service将自生自灭,不受那个组件的生命周期控制。其生命周期图如下:
Service的生命周期

通过生命周期图可以看到,创建一个Service时,根据调用的方式不同,其中执行的方法也不同。

  • 通过startService()启动Service:通过Intent调用startService()可以启动一个Service,系统会调用指定Service中的onStartCommand()方法(当然,如果这个Service之前没有运行,将会首先调用Service中的onCreate()方法,然后再调用onStartCommand()方法)

  • 停止启动的Service:通常Service是自己管理它的生命周期(而不是由系统管理)也就是说系统没有办法停止一个Service只有在Service中自己停止自己,这就需要在onStartCommand()中使用stopSelf()方法来停止一个Service,必要的时候也可以在其他组件中通过调用指定Service的stopService()方法来停止一个Service

  • 通过bindService()绑定Service:其他组件通过调用bindService()方法来绑定一个Service,为了维持一个长久的连接,通常,绑定连接了,就不运行其他组件通过startService()方法来启动这个已通过绑定连接的Service。客户端(绑定这个Service的组件)和Service之间同连接通过IBinder维持,通过绑定,客户端就可以调用Service中的方法,一个Service可以允许多个组件同时绑定它

  • 停止绑定Service:当所有的组件都与之解绑了,这个Service也就自动被系统销毁

两种Service类的区别(Service和IntentService):

  • 继承IntentService.class:系统默认在其他线程中执行任务,而且只能执行单线程任务,如果传入多个请求,需要通过队列排队执行,也就是虽然是在其他线程中执行任务,但是不能通过多线程执行,需要排队执行。因为大多数的Service不会同时执行多种操作(即,同时开多个线程,当然这样做是危险的),所以官方推荐使用IntentService,使用它的好处是:

    • 继承这个类的Service默认是在其他线程中执行

    • 将会创建一个工作队列接收传来的Intent,并通过onHandleIntent()方法来执行

    • 当所有请求都通过onHandleIntent()被执行完,Service就会自动停止(不需要调用stopSelf())默认的onBind(),并返回null

    • 提供的onStartCommand()接收Intent,然后发送给工作队列,交给onHandleIntent()处理

    注意,在使用IntentService时通常只需要实现构造方法和onHandleIntent()方法,如果还想要实现其他的方法(如 onCreate(),onStartCommand(),onDestory()等)都需要返回父类的构造方法,也就是比如:

@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();    return super.onStartCommand(intent,flags,startId);}
  • 继承Service.class:可以执行多线程任务(因为你可以自定义创建Thread,如果需要同时处理多个请求,可以创建多个Thread来分别执行)

关于onStartCommand()方法:

onStartCommand()的返回值是一个integer类型的数据,表示:当系统销毁当前Service后应该怎样恢复(很重要的!!)

  • START_NOT_STICKY:当系统销毁当前Service后,不会重新创建这个Service,除非有别的组件通过Intent重新启动这个Service

  • START_STICKY:当系统销毁当前Service后,将会重新创建这个Service,并调用其onStartCommand()方法,但是不会处理其中的Intent,因为这个Intent是从别的组件启动这个Service时用的,而重新启动不是从别的组件启动,所以不会处理这个Intent;

  • START_REDELIVER_INTENT:当系统销毁当前Service后,会重新创建这个Service并调用其onStartCommand()方法,会处理上次传入的Intent,所以的pending Intent(延迟Intent)将会被循环处理一遍(这里不太理解。。。)

系统销毁一个Service:

Android系统只有在内存资源严重的不足的时候才会强行停止Service

  • 如果一个Service绑定在一个正在前台运行的Activity,则不太可能被系统销毁;

  • 如果一个Service申明为“运行在前台”,则也不太可能被销毁。

  • 如果一个Service启动并长时间在后台运行,则有可能被系统销毁

前台Service(foreground service):

前台运行的Service**基本不可能被系统销毁,它会在状态栏创建一个常驻的通知条**(想想360在手机状态栏的那个),下面是一个启动前台Service的例子:

//创建通知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);//启动一个前台Service,需要一个通知作为参数        startForeground(ONGOING_NOTIFICATION_ID, notification);
0 0