zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

Android创建服务之startedservice详细介绍

Android服务 详细 介绍 创建
2023-06-13 09:15:17 时间

创建startedservice

      应用组件(例如Activity)调用startService()来启动一个Service,将需要的参数通过Intent传给Service,Service将会在onStartCommand函数中获得Intent。

有两种方式可以创建startedservice,一种是扩展Service类,另外一种是扩展IntentService类

扩展Service
      这是所有服务的基类。扩展这个类的时候,特别重要的一点是,需要创建一个新的线程来做服务任务,因为service默认是运行在你的主线程(UI线程)中的,它会使你的主线程运行缓慢。

扩展IntentService
      这是一个service的子类,他可以在一个工作线程中处理所有的启动请求。如果你不需要同一时刻出来所有的服务请求,使用IntentService是一个很好的选择。你需要做的仅仅是实现onHandlerIntent()方法,通过这个函数处理接受的每一个启动请求。


下面我们学习如何扩展IntentService类和Service类

扩展IntentService类

IntentService做了什么?
1.创建一个独立于主线程的工作线程,用于执行通过onStartCommand()传来的所有的intent。
2.创建一个工作队列,将接受的所有intent一个一个的传给onHandlerIntent(),所以同一时间内你只处理一个intent,不用担心多线程的问题。
3.当所有的请求都处理完后,停止服务,所以你不需要手动调用stopSelf()。
4.提供onBind()函数的默认实现,返回null
5.提供onStartCommand()函数的默认实现,它把intent发送到工作队列中去,然后工作队列再发送到你的onHandlerIntent()函数中。

有了上面的基础,你仅仅要做的就是实现onHandlerIntent()。并且实现一个小小的构造函数。

参考下面的例子:

复制代码代码如下:


publicclassHelloIntentServiceextendsIntentService{

 /**
  *Aconstructorisrequired,andmustcallthesuperIntentService(String)
  *constructorwithanamefortheworkerthread.
  */
 publicHelloIntentService(){
     super("HelloIntentService");
 }

 /**
  *TheIntentServicecallsthismethodfromthedefaultworkerthreadwith
  *theintentthatstartedtheservice.Whenthismethodreturns,IntentService
  *stopstheservice,asappropriate.
  */
 @Override
 protectedvoidonHandleIntent(Intentintent){
     //Normallywewoulddosomeworkhere,likedownloadafile.
     //Foroursample,wejustsleepfor5seconds.
     longendTime=System.currentTimeMillis()+5*1000;
     while(System.currentTimeMillis()<endTime){
         synchronized(this){
             try{
                 wait(endTime-System.currentTimeMillis());
             }catch(Exceptione){
             }
         }
     }
 }
}

如果你要实现其他的回调函数,例如onCreate,onStartCommand,onDestroy一定记得调用父类相应的函数,这样IntentService才能正确的处理工作线程。

例如,你需要在onStartCommand函数中弹出一个提示,那么你可以这样写:

复制代码代码如下:

@Override
publicintonStartCommand(Intentintent,intflags,intstartId){
   Toast.makeText(this,"servicestarting",Toast.LENGTH_SHORT).show();
   returnsuper.onStartCommand(intent,flags,startId);
}

有一点例外的是,如果你需要其他组件绑定服务,那么你的onBind函数不需要调用父类的onBind。

在下一节中,你将会看到通过扩展service类来实现与本节相同的服务,所不同的是代码会更多,但是同样意味着更灵活,特别是你需要同时处理多个请求时,比较适合直接扩展Service。

扩展Service类

如同你在上一节看到的一样,使用IntentService来实现一个startedservice非常简单。如果你需要你的service来执行多个线程,那么你需要扩展Service类去处理每个intent。

作为对比,下面的例子用Service类实现了和上一节中一样功能的服务。

复制代码代码如下:
publicclassHelloServiceextendsService{
 privateLoopermServiceLooper;
 privateServiceHandlermServiceHandler;

 //Handlerthatreceivesmessagesfromthethread
 privatefinalclassServiceHandlerextendsHandler{
     publicServiceHandler(Looperlooper){
         super(looper);
     }
     @Override
     publicvoidhandleMessage(Messagemsg){
         //Normallywewoulddosomeworkhere,likedownloadafile.
         //Foroursample,wejustsleepfor5seconds.
         longendTime=System.currentTimeMillis()+5*1000;
         while(System.currentTimeMillis()<endTime){
             synchronized(this){
                 try{
                     wait(endTime-System.currentTimeMillis());
                 }catch(Exceptione){
                 }
             }
         }
         //StoptheserviceusingthestartId,sothatwedon"tstop
         //theserviceinthemiddleofhandlinganotherjob
         stopSelf(msg.arg1);
     }
 }

 @Override
 publicvoidonCreate(){
   //Startupthethreadrunningtheservice. Notethatwecreatea
   //separatethreadbecausetheservicenormallyrunsintheprocess"s
   //mainthread,whichwedon"twanttoblock. Wealsomakeit
   //backgroundprioritysoCPU-intensiveworkwillnotdisruptourUI.
   HandlerThreadthread=newHandlerThread("ServiceStartArguments",
           Process.THREAD_PRIORITY_BACKGROUND);
   thread.start();

   //GettheHandlerThread"sLooperanduseitforourHandler
   mServiceLooper=thread.getLooper();
   mServiceHandler=newServiceHandler(mServiceLooper);
 }

 @Override
 publicintonStartCommand(Intentintent,intflags,intstartId){
     Toast.makeText(this,"servicestarting",Toast.LENGTH_SHORT).show();

     //Foreachstartrequest,sendamessagetostartajobanddeliverthe
     //startIDsoweknowwhichrequestwe"restoppingwhenwefinishthejob
     Messagemsg=mServiceHandler.obtainMessage();
     msg.arg1=startId;
     mServiceHandler.sendMessage(msg);

     //Ifwegetkilled,afterreturningfromhere,restart
     returnSTART_STICKY;
 }

 @Override
 publicIBinderonBind(Intentintent){
     //Wedon"tprovidebinding,soreturnnull
     returnnull;
 }

 @Override
 publicvoidonDestroy(){
   Toast.makeText(this,"servicedone",Toast.LENGTH_SHORT).show();
 }
}

如同你看到的,比IntentService的例子多了好多代码。

因为你自己处理每次的onStartcommand(),所以你可以在同一时间处理多个请求。这个例子没有这样做,但是如果你愿意,你完全可以每收到一个请求的时候,创建一个新线程并且立即运行,而不是等到上一个请求处理完成后再运行。

注意,onStartCommand()函数必须返回一个整数。这个整数描述了当系统杀死该服务时将要如何处理该服务。返回值必须是下面几个值:
START_NOT_STICKY
在onStartCommand()返回后,系统杀死服务,服务将不会重启,除非有pendingintents(目前笔者还不懂什么是pendingIntents)要投递。这比较适合非常容易就可以重新启动未完成任务的情况。

START_STICKY
       在系统杀死服务后,重启服务并且调用onStartCommand(),但是不重新投递上一次的intent。系统会传给onStartCommand()函数null,除非有pendingintent来启动服务,会传给onStartCommand()相应的intent。这种做法比较适合媒体播放服务,不需要执行命令,但是独立运行,常处于等待任务的服务。

START_REDELIVER_INTENT
在系统杀死服务后,重启服务并且调用onStartCommand(),参数传入上一次的intent,然后接下来是pendingintent传入onStartCommand()。这比较适合于正在执行一项工作,它不能被立刻恢复,例如下载文件。

启动Service

你可以从一个Activity或者其他组件调用startService(intent)来启动服务。Android系统会调用服务的onStartCommand()函数并且传给它intent。

用上一节的HelloService来做个例子:
Intentintent=newIntent(this,HelloService.class);
startService(intent);
startService()会立刻返回,Android系统会调用服务的onStartCommand()函数。如果这是服务没有在运行,系统会首先调用onCreate()函数,然后会紧接着调用onStartCommand()函数。

如果服务没有提供绑定,那么通过startService()函数传入的intent就是应用组件和服务进行交互的唯一途径。如果你想服务发送结果给客户组件,那么客户组件需要在启动服务时,创建一个用于广播的PendingIntent,通过intent投递给服务。这样,服务就可以利用广播把结果发送给客户组件。

多个请求会导致服务的onStartCommand()被调用多次。但是,只需要一个停止请求(stopSelf()或stopService())就会使服务停止。

停止服务

一个启动的服务必须管理自己的生命周期。因为除非系统需要回收资源的时候,系统不会停止或者销毁服务。所以,服务必须通过调用stopSelf()来停止自己,或者有其他组件调用stopService()。

一担收到stopSelf()或stopService()的停止请求,系统会立刻销毁服务。

如果你的服务同时处理多个服务请求,当某个请求完成时就不能马上停止服务,因为你可能已经又接受了一个新的请求(在第一个请求完成时结束服务会终止第二个请求)。为了解决这个问题,你可以调用stopSelf(int)函数来保证你的停止请求总是基于最近的一次服务请求。这是因为,调用stopSelf(int)函数
会把onStartCommand()函数传入的startId传给停止请求。当你的startId和最近一次接受的服务请求不匹配时,服务不会结束。

注意:stopSelf(int)函数只是简单的与最近一次收到的startId比较,如果你的服务处理过程是多线程的,可能后收到的服务请求先完成,那stopSelf(int)的方案不适合你,你应该手动管理接受到的服务请求和完成的服务,比如在onStartCommand()函数中把startId记录到一个表中,在完成服务任务时在表中记录完成状态,在确保表中任务都完成的情况下直接调用stopSelf()来停止服务。

在前台运行服务

一个前台服务是用户能够很明显意识到的服务,当可用内存比较低的时候,系统不会杀掉前台服务。一个前台服务必须提供一个状态栏通知,放在正在运行条目里,这意味着只要服务没有停止或者一直是前台服务,这个通知就不会被消失。

举个例子,一个音乐播放器服务应该被设置为前台运行,因为用户非常明显知道他在运行。这个状态栏通知可以显示正在播放的歌曲,并且可以允许用户点击进入音乐播放界面。

要使服务运行在前台只要调用startForeground()。这个方法有两个参数:一个通知的整数ID和一个状态栏通知。代码片段:

复制代码代码如下:
Notificationnotification=newNotification(R.drawable.icon,getText(R.string.ticker_text),
       System.currentTimeMillis());
IntentnotificationIntent=newIntent(this,ExampleActivity.class);
PendingIntentpendingIntent=PendingIntent.getActivity(this,0,notificationIntent,0);
notification.setLatestEventInfo(this,getText(R.string.notification_title),
       getText(R.string.notification_message),pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID,notification);

使用stopForeground()函数可以把服务从前台移除。这个函数带一个布尔值参数,来表示是否从状态栏移除通知。这个函数不会使服务停止。如果把前台服务停止掉,那么通知也会同时被移除。