Dev Guide/Framework Topics/Services

 官网地址:http://developer.android.com/guide/topics/fundamentals/services.html


Services

Service是一个可以在后台执行长时间操作的程序组件,它不提供用户接口。一个应用程序可以启动一个service,即使用户切换到其它应用程序,它也会继续在后台运行。此外一个组件可以绑定到一个service来与它交互甚至是用户进程之间的通信(IPC)。比如说,一个service或许处理网络事务,播放音乐,执行文件I/O或者与一个content provider相互作用,所有的都在后台进行。

Service通常采用两种方式:

被启动(started):

当一个应用程序组件通过调用startService()启动它时,这个service称为”started”。一旦被启动,一个service就可以在后台独立的运行,即使启动它的组件已经被destory。通常一个被启动的service执行一项单独的操作并为呼叫者(caller)返回结果。比如说,它或许通过网络下载或上传一个文件。当操作完成时,这个service将自动停止。

被绑定(bound):

当一个应用程序组件通过调用bindService()绑定到它上面时,这个service为” bound”。一个绑定的service提供了一个客户-服务接口来允许这个组件与service交互,发送请求,获得结果甚至使用进程间通信来跨进程做这些。一个绑定的service一直运行直到有其他的组件绑定到它上面。多个组件可以一次性绑定到一个service,但是当它们所有的解绑定时,service将被销毁。

尽管本文档常常单独的讨论这两种方式,但是你的service都可以用这两种方式。它可以被启动(无限期运行)或者亦可以绑定。只取决于你是否实现了一对回调函数:onStartCommand()来允许组件启动它以及onBind()允许绑定。

无论你的应用程序是采用启动,绑定还是两者都有,任何程序组件都可以通过Intent使用这个service(甚至从一个单独的程序),如同任何组件都可使用activity。然而你可以在manifest文件将这个service声明为私有并阻止其他程序的访问。

Caution一个service在它的主进程中运行---service不会创建它自己的线程并且不会运行在单独的进程(除非用别的方法指定)。这意味着,如果你的service将做一些CPU密集工作或者模块化的操作(比如MP3重放或联网),你应该为service创建一个新的线程来做这些工作。通过使用单独的线程,你可以减少Application Not Responding (ANR)错误的风险并且应用程序的主线程可以依然专注于用户与你的activity的交互。

 


Service基础

为了创建一个service,你必须创建一个Service(或已存的子类)的子类。你还需要重写一些回调函数,这些函数常处理service关键点以及实现绑定的机制。你需要重写的几个重要的回调函数是:

onStartCommand()

当别的组件,如一个activity,调用startService()来请求的service被启动时由系统调用。一旦该方法执行,service将启动并在后台无限期的运行。如果你实现它,当在service工作完成后使用stopSelf()stopService()停掉它是你的责任。(如果你想使用绑定的方式,你无须实现这个方法。)

onBind()

当一个组件通过调用bindService()想要与service(比如执行RPC)绑定时由系统调用。在你的实现中,你必须提供一个接口让客户端使用来与service通讯,并返回一个IBinder。你必须总是实现这个方法,但是如果你不想绑定时可以返回null

onCreate()

service被第一次创建时由系统调用,来执行一次性的设置步骤(在调用onStartCommand()onBind()之前)。如果service已经运行,该方法不会被调用。

onDestory()

service不再使用并被销毁时由系统调用。你的service必须实现这个方法来清空资源(比如线程,注册的监听器,receiver等)。如果一个组件通过调用startService()(这将导致调用onStartCommand())来启动一个service,这个service将一直运行,直到它自己调用stopSelf()来停止或者别的组件调用stopService()来停止它。

如果一个组件调用bindService()来创建这个service(onStartCommand()不会被调用),这个service一直运行直到有其他的组件绑定到它上面。一旦没有客户端绑定到这个service,系统就会destory它。

当内存过低,系统必须为用户关注的activity回收资源时,Android系统可以强行停止一个service。如果这个service被绑定到用户关注的activity时,它可能不会被kill。如果这个service被声明为run in the foreground,它将永远都不会被kill。否则如果service是长时间操作,系统会随着时间的推移降低其在task列表里的位置并且这个service将变的容易被kill---如果你的service被启动,你必须设计好,能使系统很好的处理restart。如果系统kill了你的service,在资源变的可用时,系统会restart你的service(尽管这依然取决于你从onStartCommand()中返回的值),

在下面的章节,你将看到如何创建各种类型的service以及如何从其他程序组件使用。

 

在manifest中声明一个Service

如同activity,你必须在manifest文件中声明所有的service。为了声明你的service,为<application>元素添加一个<service>元素,例如:

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. <manifest ... >  
  2.   ...  
  3.   <application ... >  
  4.       <service android:name=".ExampleService" />  
  5.       ...  
  6.   </application>  
  7. </manifest>  
 

还有一些其它的属性你可以包含到<service>元素来声明一些特性,比如启动service的权限,service应在哪个进程里运行。

正如一个activity,一个service可以定义intent filter来允许其它的组件通过implicit intent激活(invoke)service。通过声明intent filter,在设备上安装的其他程序的组件可以启动你的service,只要你的service声明的intent filter与其他应用程序传递给startService()intent匹配。

如果你打算局部使用你的service(其他应用程序不能使用),这时你不需要也不应该提供intent filter。没有intent filter,你必须通过一个指定了service类名称的intent来启动该service

另外,如果你包含一个android:exported属性并将其值设为false,你可以确保你的service对你的程序是私有的。即使你的service提供了intent filter,这个属性也会发挥作用。

 


创建一个 Started Service

一个start service是指由其它组件通过调用startService()启动的service,调用该方法会触发serviceonStartCommand()函数的调用。

当一个service被启动,它就有了独立于启动它的组件的生命周期,service可以在后台无限期的运行,即使启动它的组件被销毁。因此这个service必须在工作结束时自行调用stopSelf()结束自己,或者有其他的组件调用stopService()来停止它。

一个应用程序比如activity可以通过调用startService()启动service,需要传递给方法一个指明了serviceIntent,并在其内部包含一些service使用的数据。这个serviceonStartCommand()方法内接受到这个Intent

例如,设想一个activity需要将一些数据存储到online数据库。这个activity可以启动一个companion service并通过传递给startService()方法的Intent将数据递交给serviceserviceonStartCommand()方法内接受到这个Intent,连接到互联网并执行数据库事务。当事务完成,service停止它自己并被销毁。

Caution: 在默认情况下service与其声明所在的应用程序程序在相同的进程中,且都在程序的主线程运行。所以在用户与在同一程序中的activity交互的时候,如果你的service执行密集或者中断性的操作,service将会降低activity的执行速度。为了避免影响程序运行,你该为service另起一个新的线程。

通常有两个类可以让你来继承来创建一个started service

Service

这是所有service的基类。当你继承这个类,你该创建一个新的线程来做所有的service工作,因为默认情况下下这个service使用你应用程序的主线程,这将降低你应用程序中运行的activity的效率。

IntentService

这个是Service的子类,它使用一个worker 线程逐个处理所有start请求。如果你不需要你的service同时处理多个请求,这将是最好的选择。你所要做的就是实现onHandleIntent()方法,该方法为每个启动请求接收Intent,所以你可以做后台工作。

下面的章节描述如何是用上面的类实现service

 

继承IntentService类:

因为大多数started service不需要处理同时处理多个请求(这实际上是一个危险的多线程方案),你使用IntentService的子类来实现你的service可能是最好的选择。

IntentService将做以下内容:

  •  创建一个默认的worker线程来执行传递给onStartCommand()的所有intent,以与主线程分离。
  •  创建一个工作队列用来逐个传递intent给你的onHandleIntent()实现,所以你永远不用担心多线程。
  •  当所有start请求处理后,stop所有的service,所以你无须调用stopSelf()
  •  提供一个对onBind()的默认实现,并返回null
  •  提供一个对onStartCommand()的默认实现来发送intent到工作队列然后传递给onHandleIntent()实现。

所有这些事实表明你所需要做的所有事是实现onHandleIntent()来做由Intent提出的工作。(虽然你也需要为service提供一个构造方法)

下面是一个IntentService的实现:

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. public class HelloIntentService extends IntentService {  
  2.   /**  
  3.    * 需要一个构造方法,并调用父类的IntentService(String),传入worker 线程的名字. 
  4.    */  
  5.   public HelloIntentService() {  
  6.       super("HelloIntentService");  
  7.   }  
  8.   /** 
  9.    * IntentService 从启动Service的intent的默认的worker线程调用这个方法,当这个方法返回时IntentService 
  10.     * 恰当的时候stops 这个 service 
  11.    */  
  12.   @Override  
  13.   protected void onHandleIntent(Intent intent) {  
  14.       // 通常我们要在这里做一些工作, 比如下载一个文件.  
  15.       // 举例, 只是 sleep 5 秒钟.  
  16.       long endTime = System.currentTimeMillis() + 5*1000;  
  17.       while (System.currentTimeMillis() < endTime) {  
  18.           synchronized (this) {  
  19.               try {  
  20.                   wait(endTime - System.currentTimeMillis());  
  21.               } catch (Exception e) {  
  22.               }  
  23.           }  
  24.       }  
  25.   }  
  26. }  

 

我们要做的只是提供一个构造方法并实现onHandleIntent()方法。

如果你决定重写其它的回调函数,比如onCreate()onStartCommand()或者onDestory(),确保调用父类的实现,以便IntentService可以适当的处理worker线程的声明。

比如,onStartCommand()必须返回默认的实现:

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. @Override  
  2. public int onStartCommand(Intent intent, int flags, int startId) {  
  3.     Toast.makeText(this"service starting", Toast.LENGTH_SHORT).show();  
  4.     return super.onStartCommand(intent,flags,startId);  
  5. }  

 

除了onHandleIntent(),你不需要调用父类的方法的是onBind()(但是如果你的service允许绑定,你只需要实现它)。

在下一节,你将看到在继承Service类时,相同类的service如何实现,它将需要更多的代码,但是如果你需要同时处理start请求,这也许将是必须的。

 

继承Service类:

如果你需要service来执行多线程(而不是通过一个工作队列处理start请求),这时你可以继承Service类来处理每个Intent

为了比较,下面的例子代码是一个继承Service类实现的方式,该方式与前面使用IntentService类的例子执行了相同工作。换句话说,为每一个start请求,它使用一个worker线程来执行工作并一次只处理一个。

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1.     public class HelloService extends Service {  
  2.        private Looper mServiceLooper;  
  3.     private ServiceHandler mServiceHandler;  
  4.      // 从线程接收消息的Handler   
  5.      private final class ServiceHandler extends Handler {  
  6.         public ServiceHandler(Looper looper) {  
  7.             super(looper);  
  8.         }  
  9.         @Override  
  10.         public void handleMessage(Message msg) {  
  11.           //通常我们要在这里做一些工作, 比如下载一个文件.  
  12.           //举例, 只是 sleep 5 秒钟.  
  13.           long endTime = System.currentTimeMillis() + 5*1000;  
  14.           while (System.currentTimeMillis() < endTime) {  
  15.               synchronized (this) {  
  16.                   try {  
  17.                       wait(endTime - System.currentTimeMillis());  
  18.                   } catch (Exception e) {  
  19.                   }  
  20.               }  
  21.           }  
  22.           // 用startId stop这个service, so that we don't stop  
  23.           // service在处理其他期间  
  24.           stopSelf(msg.arg1);  
  25.       }  
  26.   }  
  27.   @Override  
  28.   public void onCreate() {  
  29.     // 启动一个线程来运行service.注意我们创建了一个单独的线程,因为Service通常运行在主线程  
  30.     // 我们也将优先将其运行在后台,这样CPU密集工作将不会阻断我们的UI    
  31.     HandlerThread thread = new HandlerThread("ServiceStartArguments",  
  32.             Process.THREAD_PRIORITY_BACKGROUND);  
  33.     thread.start();  
  34.       
  35.     // Get the HandlerThread's Looper and use it for our Handler   
  36.     mServiceLooper = thread.getLooper();  
  37.     mServiceHandler = new ServiceHandler(mServiceLooper);  
  38.   }  
  39.   @Override  
  40.   public int onStartCommand(Intent intent, int flags, int startId) {  
  41.       Toast.makeText(this"service starting", Toast.LENGTH_SHORT).show();  
  42.       // 对每一个start请求,发送一个消息来启动一项工作并传递一个start ID来标识当我们完成工作时哪个请求要被stop   
  43.       Message msg = mServiceHandler.obtainMessage();  
  44.       msg.arg1 = startId;  
  45.       mServiceHandler.sendMessage(msg);  
  46.         
  47.       // If we get killed, after returning from here, restart  
  48.       return START_STICKY;  
  49.   }  
  50.   @Override  
  51.   public IBinder onBind(Intent intent) {  
  52.       // We don't provide binding, so return null  
  53.       return null;  
  54.   }  
  55.     
  56.   @Override  
  57.   public void onDestroy() {  
  58.     Toast.makeText(this"service done", Toast.LENGTH_SHORT).show();   
  59.   }  
  60. }  

 

如你所见,比起IntentService,你要做更多的事。然而因为你自己处理每个onStartCommand()的调用,你可以同时执行多个请求。本例中没有实现,但是如果你愿意,你可以为每个请求创建一个新的线程并立即运行它们(而不是等先前的请求结束)。

注意onStartCommand()方法必须返回一个整型。这个整型是用来描述系统在kill Service时如何继续这个service的值(如前面讨论的,IntentService的默认实现为你处理这些).onStartCommand()的返回值必须是下面中的一个:

START_NOT_STICKY:

如果在onStartCommand()返回之后系统kill这个service不要重新创建这个service,除非有intent提交。在不需要或你的应用程序可简单restart任何未完成的工作时这是避免运行你的service的最佳选择。

START_STICKY:

如果在onStartCommand()返回之后系统kill这个service,重新创建service并调用onStartCommand(),但是不要传递最后一个Intent。系统调用传入null实参的onStartCommand(),除非有待定(pendingIntentstart service,在此情况下,这些intent将被传递。

这适合于媒体播放器(或类似的Service),它们没有在执行命令,但是无限期的运行并等待工作。

START_REDELIVER_INTENT

如果在onStartCommand()返回之后系统kill这个service,重新创建service,调用onStartCommand()并传递给它递交给service的最后一个intent。任何待定(pendingintent依次被递交。这适合于积极的执行需要马上返回的工作的service,比如下载一个文件。

启动Service

你可以在activity或其它应用程序组件中通过传递Intent(指明要启动的service)给startService来启动一个serviceAndroid系统会调用serviceonStartCommand()方法并传递一个Intent

比如,一个activity可以使用一个explicit intent 来启动先前例子的service

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. Intent intent = new Intent(this, HelloService.class);  
  2. startService(intent);  
 

 

startService()方法立即返回,Android系统调用serviceonStartCommand()方法。如果service还没有运行,系统会先调用onCreate()方法,然后调用onStartCommand()

如果这个service没有提供绑定,这个intent是应用程序组件和service之间沟通的唯一模式。然而如果你想service返回结果,启动service的客户端可以为broadcast(用getBroadcast())创建一个PendingIntent并将其放在启动serviceIntent中传递给service。这时service可以使用broadcast来传递结果。

多个start service的请求将导致多次service的相应onStartCommand()的调用。然而,只需要一个请求来stop service(使用stopSelf()stopService()

Stop一个service

一个被启动的service必须自己管理它的生命周期。换句话说,系统不会stop或者destory service,除非必须回收系统内存。所以service必须通过调用stopSelf()停止自己或让其他组件调用stopService()stop它。

一旦使用stopSelf()stopService()提交了请求,系统会尽快destroy它。

然而,如果你的service同时处理多个提交给onStartCommand()的请求,当你处理完一个start请求时,你不应该stop这个service,因为你也许已经接收了一个新的start请求(在第一个请求结束时stop将会终止第二个请求)。为了避免这个问题,你可以调用stopSelf(int)来确保你对servicestop请求总是基于最近的start请求。也就是说,当你调用stopSelf(int),你传递一个start请求的ID给对应的stop请求。然后如果service在你能够调用stopSelf(int)之前接收到一个新的start请求,这时这个ID将不会匹配,service也将不会stop

Caution重要的一点是:当你的应用程序的service完成工作后,为了避免系统资源的浪费以及电池的消耗,应该将其stop。如

果需要,其他的组件可以通过调用stopService()stop这个service。虽然你可以为你的service绑定,但是如果它曾接收

到一个onStartCommand()调用,你必须亲自stop这个service

 

 


创建一个Bound Service

一个bound service是允许程序组件用bindService()绑定到它上面,来创建一个长期存在的联系(并通常不允许组件通调用startService()start它)。

当你想要与来自你的应用程序中的activity和其它的组件的service相互作用或者通过进程间通信(IPC)对其他程序公开一些应用程序的功能,你可以创建一个bound service

为了创建一个bound service,你必须实现onBind()回调函数返回一个IBinder来定义与这个service通信的接口。其它的组件就可以通过调用bindServiceIO来检索这个接口并在service中调用方法。这个service只为绑定到它上的组件存在,所以当没有组件绑定到service上时,系统就会destory它(不同于通过onStartCommand()启动的service,你不需要自己stop这个service)。

为了创建一个bound service,你首先要做的是声明客户端如何与service通信的接口。在service和客户端之间的接口必须是一个IBinder的实现,并且必须由你的serviceonBind()回调函数返回。一旦客户端获得这个IBinder,它就可以通过接口与service通信。

多个客户端可以一次性绑定到一个service。当一个service完成了与service的联系,它调用unbindServie()来解除绑定。一旦没有客户端绑定到service上时,系统就会destory这个service

有很多方法实现bound service并且这些实现都比started service实现复杂,所以bound service的讨论将在另一个单独的文档。

 


发送一个通知给用户

一旦运行,service就可以通过Toast NotificationStatus Bar Notification来通知事件的用户。

toast notification是一个信息,该信息出现在当前窗口的表面,显示一会就会消失。而一个status bar notification为含有信息的状态栏提供了一个图标,用户通过选择来执行一个action(比如启动一个activity)。

通常在一些后台工作完成后(比如文件下载完成),status bar 通知是通知用户的最好的手法了。当用户选择了展开的视图的通知时,这个通知可以启动一个activity(比如显示下载好的文件)。

 


在前台运行一个Service 

前台service是用户需要知道并且在系统缺乏内存时不作为候选的service。前台service必须提供一个为status bar使用的notification(通知)。该通知被置于”Ongoing”标题下,这意味着这个通知不能被解除,除非这个service stop或从前台移除。

比如说,一个音乐播放器利用一个可以设置为前台运行的service播放音乐,因为用户需要确定自己的操作。status bar上的通知或许指明当前播放的歌曲并允许用户启动一个activity来与音乐播放器交互。

为了要求你的service运行在前台,可以调用startForeground()。这个方法有两个参数:一个整数独一无二的标识通知,一个为status bar准备的Notification。比如说:

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),  
  2.                                     System.currentTimeMillis());  
  3. Intent notificationIntent = new Intent(this, ExampleActivity.class);  
  4. PendingIntent pendingIntent = PendingIntent.getActivity(this0, notificationIntent, 0);  
  5. notification.setLatestEventInfo(this, getText(R.string.notification_title),  
  6.                      getText(R.string.notification_message), pendingIntent);  
  7. startForeground(ONGOING_NOTIFICATION, notification);  

 

为了将service从前台移除,可以调用stopForeground()。这个方法有一个boolean参数来指明是否同样移除status bar通知。这个方法不会stop这个service。然而如果你在service运行在前台时stop了它,这个通知同样会被移除。

Note :startForeground()stopForeground()方法是在Android2.0API Level5)引入的。为了能在之前的版本运行前台service,你必须使用setForeground()方法。

 


管理Service的生命周期

Service的生命周期比起activity要简单。然而,密切注意你的service如何创建和销毁将更为重要,因为一个service可以在用户不知的情况下在后运行。

Service的生命周期—从创建到销毁—可以遵循两个途径:

  • started service

当其他组件调用startService()时创建这个service。然后这个service无限期的运行并必须调用stopSelf() stop自己。别的组件也可以通过调用stopService()stop这个service。当servicestop,系统就会destroy它。

  • bound service

当另一个组件(客户端)调用bindService()时,service被创建。然后客户端通过一个IBinder接口与service通信。这

个客户端可以通过调用unbindService()关闭通信。多个客户端可以绑定到service,当所有的客户端解除绑定后,系统会destroy这个service。(这个service不需要自己stop

这两个途径并不是完全独立的。也就是说,你可以绑定到一个已由startService()启动的service。比如说,一个后台的音乐service已经通过startService() 启动,并传递一个Intent来指明要播放的音乐。之后,或许用户想对播放器执行一些控制或者获得当前播放的音乐的一些信息,可以通过调用bindService()来将activity绑定到service。在这种情况下,stopService()stopSelf()在客户端解除绑定前不能stop这个service

实现生命周期回调函数:

如同一个activityservice也有生命周期函数,你可以实现它们来监听service状态的变化并在合适的时候执行工作。下面的框架service展示了每个生命周期函数:

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. public class ExampleService extends Service {  
  2.     int mStartMode;       // 指明service被kill时如何反应  
  3.     IBinder mBinder;      // 为绑定的客户端使用的接口  
  4.     boolean mAllowRebind; // 指明onRebind是否可用   
  5.     @Override  
  6.     public void onCreate() {  
  7.         // The service is being created  
  8.     }  
  9.     @Override  
  10.     public int onStartCommand(Intent intent, int flags, int startId) {  
  11.         // The service is starting, due to a call to startService()  
  12.         return mStartMode;  
  13.     }  
  14.     @Override  
  15.     public IBinder onBind(Intent intent) {  
  16.         // A client is binding to the service with bindService()  
  17.         return mBinder;  
  18.     }  
  19.     @Override  
  20.     public boolean onUnbind(Intent intent) {  
  21.         // All clients have unbound with unbindService()  
  22.         return mAllowRebind;  
  23.     }  
  24.     @Override  
  25.     public void onRebind(Intent intent) {  
  26.         // A client is binding to the service with bindService(),  
  27.         // after onUnbind() has already been called  
  28.     }  
  29.     @Override  
  30.     public void onDestroy() {  
  31.         // The service is no longer used and is being destroyed  
  32.     }  
  33. }  
 

 

Note:不同于activity的生命周期函数,你不需要调用这些回调函数的超类实现。

通过实现这些方法,你可以监听service生命周期的两个嵌套的循环:

  •  serviceentire lifetime发生在onCreate()被调用和onDestroy()返回之间。如同activity,一个serviceonCreate()中做初始化工作,在onDestroy()中释放所有剩余的资源。比如说,一个音乐重放service可以在onCreate()中音乐播放的地方创建一个线程,然后在onDestroy()stop这个线程。

无论是使用startService()还是binfService()创建serviceonCreate()onDestroy()函数都会被调用。

 

  •  serviceactive lifetime开始于onStartCommand()onBind()的调用。每个方法都获得一个传递给startService()bindService()Intent

如果servicestartedservice的生命周期随着activity的生命周期的结束而结束(即使在onStartCommand()返回后这个service仍然活跃)。如果这个serviceboundservice的声明周期在onUnbind()返回时结束。

Note:虽然一个started service通过调用stopSelf()stopService()stop,却没有service各自的回调函数(没有onStop()回调函数)。所以,除非这个service绑定了一个客户端,否则在service stop的时候,系统就会销毁它。

 

下图显示了service的特有的回调函数。虽然本图将由startService()创建和bindService()创建的service分开来说,请记住任何service,无论其如何被创建,都可以允许客户端来绑定它。所以,一个由onStartCommand()启动的service仍可以接收一个onBind()的调用。

 

  

 

使用service还是使用一个线程:

一个service是即使用户没有与你的应用程序交互也可以运行在后天的组件。然而你只有在需要的时候才应创建一个service

如果在主线程外执行工作,但是只是在用户与应用程序交互时执行,你应该创建一个新的线程而不是service。比如说,如果你想播放音乐,但是只有在你的activity运行时播放,你应该在onCreate()创建一个线程,在onStart()启动运行,并在onStop()结束它的运行。

请记住,如果你确实要使用一个service,它默认情况下在你的主线程运行,所以如果这个service执行密集性或者中断性工作,你应该创建一个新的线程来供这个service使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值