Services

来源:互联网 发布:知寨相当于什么职务 编辑:程序博客网 时间:2024/04/29 05:54

Service是Android中的一个类,他是Android四大组件之一,使用Service可以在后台执行长时间的操作,Service并不与用户产生UI交互,即便用户切换了应用,启动的Service仍可在后台运行。一个组件可以与Service绑定和交互,甚至跨进程交互。


1、Service启动形式

1)Stared:其他组件调用StartService()启动一个Service,一旦启动,Service将会一直运行在后台,即便开启Service的组件已经被destory。通常一个被start的Service会在后台执行单独的操作,也不给启动它的组件返回结果,比如一个启动的Service在后台下载完任务后应自动停止


2)Bound:其他组件调用bindService方法绑定一个Service,通过绑定的方式启动的Service是一个c/s结构,该Service可以与绑定他的组件进行交互。一个bound Service仅在有组件与其绑定的情况下才会运行,多个组件可与一个Service绑定,service不再与任何组件绑定时,该Service会被destory。


3)当然,Service也可以同时在上述两种方式运行,这涉及到Service中两个回调方法的执行:onStartCommand()(通过start方式启动一个Service时回调的方法),onBInd()(通过bind方式启动一个Service的回调方法)


无论通过那种方式启动Service,任何组件都可以使用Service,并能通过Intent传递参数,当然,你也可以将Service在清单文件中设置为私有,不允许其他应用访问


请注意:Service运行在主线程中,Service并不是一个新的线程,也不是新的进程,也就是说如果在Service中需要执行较为耗时的操作,需要在Service中创建一个新线程,这可以防止ANR的发生,同时主线程可以执行正常的UI操作


选择Service还是Thread?

Service是一个运行在后台的组件,并不与用户交互,你仅在需要的时候创建Service,当用户正在与UI进行操作时,需要执行一些主线程无法完成的操作,应当创建一个线程,例如在onCreate()中创建进程,在onStart()开启,最后在onStop()终止,你也可以考虑用AsyncTask或者HanderThread来创建进程


2、Service基础

为了创建Service,必须继承Service类,并重写他的回调方法,这些回调方法反映了Service的生命周期,并提供绑定Service的机制,最重要的生命周期回调方法如下:

1)onStartCommand():当其他组件调用startService()方法请求Service时,该方法被回调,一旦Service启动,他会在后台独立运行,当Service执行完之后,需要调用stopSelf()或者stopService()

2)onBind():当其他组件调用bindService()时,该方法被回调,该方法返回一个IBinder接口,该接口是Service与绑定的组件进行交互的桥梁,若Service未绑定其他组件,该方法返回null

3)onCreate():当Service第一次创建时,回调该方法,该方法只被回调一次,并在onStartCommand()或者onBind()方法被回调之前执行,若Service处于运行,该方法不会回调

4)onDestory():当Service被销毁时回调这个方法,在该方法中应清楚一些占用的资源、如停止线程,解除绑定的监视器或者broadcast Receiver


如果某个组件通过调用startService()启动来Service(系统会回调onStartCommand),那么直到Servic中手动调用stopSelf(),或者在其他组件中调用startService,该Service才会停止


如果某个组件通过调用bindService()绑定了Service(系统不会回调onStartCommand),只要该组件与Service处于绑定状态,Service就会一直运行,当没有绑定时,该Service将被destory


当系统内存低时,系统可能会停止Service的运行,若Service绑定了正在与用户交互的activity,那么该Service不大可能被系统停止,所以在开发Service时,需要使Service容易被重启,因为一旦Service被停止,再重启时需要其他资源,当然这也取决于onStartCommand()返回的值。


3、在清单文件中声明Service

1) <service android:name=".ExampleService" />

2)除此之外<service>还可以配置其他属性,比如,需要启动该Service的权限、该Service运行在哪一个进程中等,android:name是必须的,他制定了Service的全限定类名,在发布应用之后,该类名将不可更改

请注意:为了保证应用的安全,请显式启动或者绑定一个Service,请不要再<service>中配置intent-filter

如果不确定该启动哪个Service,那么可以在<Service>中配置intent-filter,但必须调用setPackage()方法,来为启动的Service消除歧义。

注:setPackage()传入一个String值,代表一个包名,该方法表示该intent对象只能在传入的包名下寻找符合条件的组件,若传入null,则表示可以在任意包下寻找

         将android:export属性设置为false,表示不允许其他组件启动本应用的组件,即使是显式intent也不行


4、start方式启动Service

其他组件调用startService()可以启动一个Service,接着,Service会回调onStartCommand()生命周期方法。startService()方法中需要一个intent参数,用于显式指定目标Service的名字,并携带data供Service使用,接着,该intent参数将回传至onStartCommand()。比如,Activity需要向在线数据库上传数据,那么可以调用startService()启动一个Service,并将数据传入intent的data中,接着onStartCommand()方法会接受到这个intent并开启一个线程将数据上传至网络,当数据上传完后,该Service将被停止并且destory.


一般使用如下 两种方式创建一个startService

1)继承Service类,请务必在Service中开启线程来执行耗时操作,因为Service运行在主线程中

2)继承IntentService类,IntentService继承于Service,若Service不需要同时处理多个请求,那么IntentService将是最好的选择,你只需要重写onHandleIntent()方法,该方法接收一个回传的Intent参数,你可以在方法内进行耗时操作,因为他默认开启了一个子线程,操作执行完后也不需要手动调用stopSelf()方法,onHandleIntent()会自动调用该方法


3)继承Intent Service类

在大多数情况下,Service并不会同时处理多个请求,因为处理多线程比较危险,所以继承intentService是一个不错的选择。

使用IntentService要点如下:

1)默认在子线程中处理回传到onStartCommand()方法中的intent

2)在重写的onHandleIntent()方法中处理按时间排序的intent队列,所以不用担心多线程问题

3)当所有请求完成后自动调用stopSelf()方法

4)默认实现onBind()方法并返回null

5)默认实现了onStartCommand()方法,并将回传的Intent以序列的形式发送给onHandleIntent(),你只需要重写该方法即可

如果你还希望在IntentService()中重写其他生命周期方法,如onCreate()、onDestory()等,那么请先调用各自的父类方法保证子线程可以正常启动

除了onHandleIntent()以外,onBind()也无需调用其父类方法


4)继承Service类

如果你需要在Service执行多线程而不是处理一个请求队列,那么需要继承Service,分别处理每个intent。在Service中执行操作时,处理每个请求都要开启一个线程,并且同一时刻一个线程只能处理一个intent


public class HelloService extends Service {    private final class ServiceHandle extends Handler {        public ServiceHandle(Looper looper) {            super(looper);        }        @Override        public void handleMessage(Message msg) {            try {                Thread.sleep(3000);            } catch (InterruptedException e) {                //恢复中断状态                Thread.currentThread().interrupt();//中断线程,但他不会中断一个正在运行的线程,其作用是线程受到阻塞时,抛出一个中断信号,那么线程就可以推出阻塞            }        }    }    ServiceHandle mHandler;<pre name="code" class="java">Intent intent = new Intent(this, HelloService.class);startService(intent);

@Override public void onCreate() { //启动线程运行服务,注意,我们创建了一个独立的线程,因为服务通常都运行在主线程中,我们不想造成阻塞,并设置了优先级 HandlerThread thread = new HandlerThread("ServiceStartArgument", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); //获得HandleThread的looper并用于我们的Handler Looper looper = thread.getLooper(); mHandler = new ServiceHandle(looper); } public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(HelloService.this, "service starting", Toast.LENGTH_SHORT).show(); //对于每个开始请求,发送消息来开始工作并且传递开始ID以便于当我们要结束工作时知道应该结束那个服务 Message msg = mHandler.obtainMessage(); msg.arg1 = startId; mHandler.sendMessage(msg); return START_STICKY;// }}
注意到onStartCommand()返回一个int变量:

1)START_NOT_STICKY:若执行完onStartCommand()方法后,系统就杀死了Service,不要再重新创建Service,除非系统回传了一个pending intent,这避免在不必要的时候运行Service,你的应用也可以restart任何未完成的操作

2)START_STICKY:若系统在onStartCommand()执行后杀死了Service,那么Service会被recreate并且回调startCommand()并将最后一个intent回传至该方法,重新传递最后一个intent是非常危险的,相反的,系统回调onStartCommand()时回传一个空的intent,除非有pengding Intent传递,否则intent将会为空,该模式适合做一些类似播放音乐的工作

3)START_REDELIVER_INTENT:若系统在onStartCommand()后杀死了服务,那么Service会被recreate并回调startCommand(),并将最后一个intent回传至该方法,任务pengding intent都会被轮流传递,该模式适合做一些类似下载文件的操作

5、启动服务

Intent intent = new Intent(this, HelloService.class);startService(intent);
启动服务后,并startCommand(),若该Service处于未运行状态,系统将首先回调onCreate(),接着回调onStartCommand(),若你希望Service可以返回结果,那么需要调用getBroadcast()返回的pengding Intent启动Service,Service可使用broadcast传递结果。


多个启动Service的请求可能导致onStartCommand()多次调用,但只需调用stopSelf()、stopService()其中之一就能停止该服务


6、停止服务

一个启动的Service必须管理自己的生命周期,系统不会主动stop或者destory一个运行的Service,除非系统内存不足,停止Service必须手动调用stopSelf()(在Service中)或调用stopSerivce()(在启动组件中),一旦调用了这两种方法,系统会尽快停止Service。


若系统正在处理多个调用onStartCommand()请求,那么在启动一个请求时,你不应当在此时停止该Service,为了避免这个问题,你可以调用stopSelf(int),当调用stopSelf(int)时,传入的id代表启动请求,该id与请求停止的id一致,在调用stopSelf(int)之前,Service又接收到一个新的start请求,但是id不一致该Service并不会停止


7、创建绑定Service

其他组件通过调用bindService()方法可以绑定一个Service以保持长连接,这时一般不允许其他组件调用StartService()启动Service

当其他组件需要与Service交互或者跨进程通信时,可以创建一个Bound Service。


为创建一个bound Service,必须重写onBind()回调,该方法返回一个IBinder接口,该接口是组件与Service通信的桥梁,组件调用bindService()与Service绑定,该组件可获取IBinder()接口,一旦获取该接口,就可以调用Service中的方法,一旦没有组件与Service绑定,系统就会销毁它,你不用手动停止他


为创建一个bound Service,必须定义一个接口,该接口指定组件与Service如何进行通信,定义的接口在组件与Service之间,且必须实现IBinder接口,这正是onBind()的返回值,一旦组件接收了IBinder,组件就可以开始通信。


多个组件可以同时与Service绑定,当组件与Service交互结束后,可调用unbindService()解绑。


8、向用户发送通知

运行中的Service可以通过Toast或者Status Bar向用户发送通知。Toast是短时间弹出的提醒框,Status Bar是顶部状态栏中出现的带有图标的信息,用户可以通过下拉状态栏获得具体信息并执行某些操作。


9、运行前台Service

前台Service用于动态通知消息,如天气预报等,该Service不容易被销毁,前台Service必须提供status bar,只有前台Service被销毁后,status bar才会消息。


举例来说,一个播放音乐的Service必须是一个前台Service,只有这样用户才能知道其运行状态


调用startForeground()可以启动前台Service,该方法接受两个参数,参数一是一个int变量,用户指定该通知的唯一性标识,而另一个参数是一个用于配置status bar的Notification

注意:为startForeground()设置的id必须为0

调用stopService()来移除前台Service,该方法必须传入一个boolean变量,表示是否也一起清除status bar上的通知,该方法并不停止服务,但是如果你停止服务的话,那么这个通知也将移除


10、Service生命周期

从Service的启动到销毁,有两种途径

1)start Service:需手动停止

2)bound Service:自动停止

如下图所示:




这两条路径不是毫不相干的,当调用startService()启动一个Service后,你仍然可以bind该Service,比如播放音乐时,须调用startService()启动指定播放的音乐,当需要获取该音乐的播放进度时,需要调用bindService(),在这种情况下,直到Service被unbind,调用stopService()或stopSelf()都不能停止该Service。


实现Service的生命周期回调


  int mStartMode;       // 表示如果Service被销毁应该做出怎样的行为    IBinder mBinder;      // 绑定的客户端界面    boolean mAllowRebind; // 表明onRebind是否应该被调用    @Override    public void onCreate() {        // 服务被创建    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        // 因为调用了startService(),服务开始运行        return mStartMode;    }    @Override    public IBinder onBind(Intent intent) {        // 通过bindService()把客户端绑定到服务上        return mBinder;    }    @Override    public boolean onUnbind(Intent intent) {        // 所有客户端都解绑        return mAllowRebind;    }    @Override    public void onRebind(Intent intent) {        // 客户端正在绑定Service,但是unbind方法被调用    }    @Override    public void onDestroy() {        // The service is no longer used and is being destroyed    }}

这些生命周期方法在调用时无需调用各自的父类方法


在两条生命周期路线中,都包含了两个完整的生命周期

完整生命周期:从onCreate()被调用到onDestory()返回,与Activity类似,一般在onCreate()中做一些初始化工作,而在onDestory()中做一些资源释放工作。

无论是startService()还是bindService()启动Service,onCreate()和onDestory()都会被回调


活动生命周期:从onStartCommand()或者从onBind()开始,由相应的startService()或bindService()调用,若是Start Service,那么Service的活动生命周期结束就是其完整生命周期结束。若是onUnbind()返回时,Service的活动生命周期结束


注意:针对Start Service,由于Service没有类似的onStop()回调,所以在调用stopSelf()或stopService()后,只有onDestory()标志着Service停止


0 0
原创粉丝点击