【Android】Service
简介
Service是android 系统中的一种组件,它跟Activity的级别差不多,但是它不能与用户交互,不能自己启动,不能自己运行,只能后台运行,并且可以和其他组件进行交互。Service的启动有两种方式:context.startService() 和 context.bindService()。
1)使用context.startService() 启动Service是会会经历:
context.startService() ->onCreate()- >onStart()->Service running
context.stopService() | ->onDestroy() ->Service stop
如果Service还没有运行,则android先调用onCreate()然后调用onStart();如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次。 stopService的时候直接onDestroy,如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。该Service的调用者再启动起来后可以通过stopService关闭Service。
所以调用startService的生命周期为:onCreate --> onStart(可多次调用) --> onDestroy
(Usually, a started service performs a single operation and does not return a result to the caller. For example, it might download or upload a file over the network. When the operation is done, the service should stop itself.)
2)使用context.bindService()启动Service会经历:
context.bindService()->onCreate()->onBind()->Service running
onUnbind() -> onDestroy() ->Service stop
onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。
所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。
(A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another application component is bound to it. Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed. 和"start" Service不同,bound Service可以和启动它的组件交流(通过Binder返回Service对象),只有当有组件绑定到这个服务的时候bound类型service才会运行,多个组建可以同时绑定到一个服务,但是当所有的组件都不再绑定的时候,服务就停止(所以可以用来做一些公用服务,比如查看天气,可以被多个应用绑定))
If a component starts the service by calling startService()
(which results in a call to onStartCommand()
), then the service remains running until it stops itself with stopSelf()
or another component stops it by callingstopService()
.
If a component calls bindService()
to create the service (and onStartCommand()
is not called), then the service runs only as long as the component is bound to it. Once the service is unbound from all clients, the system destroys it.
在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。如下:
service可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等。
3)startService和bindService并不是完全分离的,根据官方文档:Although this documentation generally discusses these two types of services separately, your service can work both ways—it can be started (to run indefinitely) and also allow binding. It's simply a matter of whether you implement a couple callback methods:onStartCommand()
to allow components to start it and onBind()
to allow binding.(所以只要提供了onStartCommand()和onBind()方法就可以将两者结合在一起)
Caution: A service runs in the main thread of its hosting process—the service does not create its own thread and does not run in a separate process (unless you specify otherwise). This means that, if your service is going to do any CPU intensive work or blocking operations (such as MP3 playback or networking), you should create a new thread within the service to do that work. By using a separate thread, you will reduce the risk of Application Not Responding (ANR) errors and the application's main thread can remain dedicated to user interaction with your activities.
以上来自官方文档:Service跑在主线程里面,所以会堵塞主线程,可能会发生ANR错误,在服务里面开线程会降低这样的风险。
Declaring a service in the manifest
1 <manifest ... > 2 ... 3 <application ... > 4 <service android:name=".ExampleService" /> 5 ... 6 </application> 7 </manifest>
The android:name
attribute is the only required attribute—it specifies the class name of the service. Once you publish your application, you should not change this name, because if you do, you might break some functionality where explicit intents are used to reference your service (read the blog post, Things That Cannot Change).
Additionally, you can ensure that your service is private to your application only if you include theandroid:exported
attribute and set it to "false"
. This is effective even if your service supplies intent filters.(让服务私有化)
startService()创建服务
An application component such as an activity can start the service by calling startService()
and passing an Intent
that specifies the service and includes any data for the service to use. The service receives this Intent
in the onStartCommand()
method.(Intent的接收地点)
1 @Override 2 public int onStartCommand(Intent intent, int flags, int startId) {}
Traditionally, there are two classes you can extend to create a started service:
1)Service:
This is the base class for all services. When you extend this class, it's important that you create a new thread in which to do all the service's work, because the service uses your application's main thread, by default, which could slow the performance of any activity your application is running.(官方文档里面不止一次的提醒说是要在服务中开启新线程做事)
2)IntentService:
This is a subclass of Service
that uses a worker thread to handle all start requests, one at a time. This is the best option if you don't require that your service handle multiple requests simultaneously. All you need to do is implement onHandleIntent()
, which receives the intent for each start request so you can do the background work.
IntentService提供如下实现:
a)将传递到onStartCommand的Intent剥离主线程;
b)创建一个工作队列,每次往onHandleIntent()传递一个Intent,不会产生多线程问题;
c)所有的Intent请求处理完毕的时候,Service停止,所以不需要调用stopSelf();
d)提供OnBind()的缺省实现,返回null;
e)提供onStartCommand的实现,将intent放进工作队列然后传递到onHandleIntent实现;
所以只要实现onHandleIntent的处理代码即可。例子:
1 public class HelloIntentService extends IntentService { 2 3 /** 4 * A constructor is required, and must call the super IntentService(String) 5 * constructor with a name for the worker thread. 6 */ 7 public HelloIntentService() { 8 super("HelloIntentService"); 9 } 10 11 /** 12 * The IntentService calls this method from the default worker thread with 13 * the intent that started the service. When this method returns, IntentService 14 * stops the service, as appropriate. 15 */ 16 @Override 17 protected void onHandleIntent(Intent intent) { 18 // Normally we would do some work here, like download a file. 19 // For our sample, we just sleep for 5 seconds. 20 long endTime = System.currentTimeMillis() + 5*1000; 21 while (System.currentTimeMillis() < endTime) { 22 synchronized (this) { 23 try { 24 wait(endTime - System.currentTimeMillis()); 25 } catch (Exception e) { 26 } 27 } 28 } 29 } 30 }
That's all you need: a constructor and an implementation of onHandleIntent()
.If you decide to also override other callback methods, such as onCreate()
, onStartCommand()
, oronDestroy()
, be sure to call the super implementation, so that the IntentService
can properly handle the life of the worker thread.(注意调用super)
For example, onStartCommand()
must return the default implementation (which is how the intent gets delivered to onHandleIntent()
):(一定要调用,否则无法传递Intent)
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 }
接下来是继承一般的Service:
继承IntentService的后果就是无法实现多线程并行,因为它只在OnHandleIntent()里面开一条线程一个一个的处理Intent,要实现多线程,只能继承一般的Service。下面是使用的一个例子(这个例子是模仿IntentService实现的,也只能一次处理一个线程):
1 public class HelloService extends Service { 2 private Looper mServiceLooper; 3 private ServiceHandler mServiceHandler; 4 5 // Handler that receives messages from the thread 6 private final class ServiceHandler extends Handler { 7 public ServiceHandler(Looper looper) { 8 super(looper); 9 } 10 @Override 11 public void handleMessage(Message msg) { 12 // Normally we would do some work here, like download a file. 13 // For our sample, we just sleep for 5 seconds. 14 long endTime = System.currentTimeMillis() + 5*1000; 15 while (System.currentTimeMillis() < endTime) { 16 synchronized (this) { 17 try { 18 wait(endTime - System.currentTimeMillis()); 19 } catch (Exception e) { 20 } 21 } 22 } 23 // Stop the service using the startId, so that we don't stop 24 // the service in the middle of handling another job 25 stopSelf(msg.arg1); 26 } 27 } 28 29 @Override 30 public void onCreate() { 31 // Start up the thread running the service. Note that we create a 32 // separate thread because the service normally runs in the process's 33 // main thread, which we don't want to block. We also make it 34 // background priority so CPU-intensive work will not disrupt our UI. 35 HandlerThread thread = new HandlerThread("ServiceStartArguments", 36 Process.THREAD_PRIORITY_BACKGROUND);//新建一个线程 37 thread.start();//启动线程 38 39 // Get the HandlerThread's Looper and use it for our Handler 40 mServiceLooper = thread.getLooper();//获得线程的looper 41 mServiceHandler = new ServiceHandler(mServiceLooper);//以这个looper为基础建立handler 42 } 43 44 @Override 45 public int onStartCommand(Intent intent, int flags, int startId) { 46 Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); 47 48 // For each start request, send a message to start a job and deliver the 49 // start ID so we know which request we're stopping when we finish the job 50 Message msg = mServiceHandler.obtainMessage(); 51 msg.arg1 = startId; 52 mServiceHandler.sendMessage(msg); 53 54 // If we get killed, after returning from here, restart 55 return START_STICKY; 56 } 57 58 @Override 59 public IBinder onBind(Intent intent) { 60 // We don't provide binding, so return null 61 return null; 62 } 63 64 @Override 65 public void onDestroy() { 66 Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); 67 } 68 }
Notice that the onStartCommand()
method must return an integer. The integer is a value that describes how the system should continue the service in the event that the system kills it (as discussed above, the default implementation for IntentService
handles this for you, though you are able to modify it). The return value from onStartCommand()
must be one of the following constants:(关于onStartCommand的返回值):
1)START_NOT_STICKY
If the system kills the service after
onStartCommand()
returns, do not recreate the service, unless there are pending intents to deliver. This is the safest option to avoid running your service when not necessary and when your application can simply restart any unfinished jobs.(方法返回后,如果服务被系统终止,那么不去重建服务,除非有新的intent请求服务)2)START_STICKY
If the system kills the service after
onStartCommand()
returns, recreate the service and callonStartCommand()
, but do not redeliver the last intent. Instead, the system callsonStartCommand()
with a null intent, unless there were pending intents to start the service, in which case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job.(方法返回后,如果服务被系统终止,那么重建服务,以一个null 的Intent调用onStartCommand)3)START_REDELIVER_INTENT
If the system kills the service after
onStartCommand()
returns, recreate the service and callonStartCommand()
with the last intent that was delivered to the service. Any pending intents are delivered in turn. This is suitable for services that are actively performing a job that should be immediately resumed, such as downloading a file.- 调用服务
1 Intent intent = new Intent(this, HelloService.class); 2 startService(intent);
停止服务
官方文档里面有这么一段:
However, if your service handles multiple requests to onStartCommand() concurrently, then you shouldn't stop the service when you're done processing a start request, because you might have since received a new start request (stopping at the end of the first request would terminate the second one). To avoid this problem, you can use stopSelf(int) to ensure that your request to stop the service is always based on the most recent start request. That is, when you call stopSelf(int), you pass the ID of the start request (the startId delivered to onStartCommand()) to which your stop request corresponds. Then if the service received a new start request before you were able to call stopSelf(int), then the ID will not match and the service will not stop.