当前位置: 代码迷 >> Android >> Android四大根本组件-Service详解
  详细解决方案

Android四大根本组件-Service详解

热度:31   发布时间:2016-04-27 23:11:06.0
Android四大基本组件-Service详解

一、官方文档

Class Overview
A Service is an application component representing either an application’s desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding declaration in its package’s AndroidManifest.xml. Services can be started with Context.startService() and Context.bindService().
服务是一种应用程序组件代表应用程序的意图,而不是与用户或其他应用程序使用电源的功能执行更长的运行操作。每个服务类必须有一个相应的服务声明在其AndroidManifest.xml。启动服务的方法有startservice(), bindservice()。后面会有具体事例测试这两种方法的使用。

Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Application Fundamentals: Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.
注意,服务是运行在他们的主进程的主线程。这意味着,如果你的服务是要做任何CPU消耗大的操作(如MP3播放)或阻塞(如网络)的操作,它会以它自己的线程去做这项工作。关于这方面的更多信息可以在应用基础:进程和线程。intentservice类可以作为一个标准的服务实现,可以调度自己的线程去工作。

What is a Service?
Most confusion about the Service class actually revolves around what it is not:

  • A Service is not a separate process. The Service object itself does not imply it is running in its own process; unless otherwise specified, it runs in the same process as the application it is part of.
  • A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).
    这里要说的是我们不仅要知道service是什么,还要知道它不是什么.
  • 服务不是一个单独的进程。服务对象本身并不意味着它是在它自己的进程中运行;除非另有规定,它运行在同一进程中的应用就是它的一部分。
  • 服务不是一个线程。这并不意味着主线程去完成相应工作(避免应用程序无响应的错误)

    Thus a Service itself is actually very simple, providing two main features:

  • A facility for the application to tell the system about something it wants to be doing in the background (even when the user is not directly interacting with the application). This corresponds to calls to Context.startService(), which ask the system to schedule work for the service, to be run until the service or someone else explicitly stop it.

  • A facility for an application to expose some of its functionality to other applications. This corresponds to calls to Context.bindService(), which allows a long-standing connection to be made to the service in order to interact with it.
  • 用于告诉系统什么要在后台运行(即用户不直接与应用程序交互),调用Context.startService() 启动服务,这要求系统来管理该服务,该服务会一直运行到知道明确停止它为止,即stopService()。
  • 一个应用程序提供它的一些功能给其他的应用。可以调用Context.bindService()来绑定服务,它允许一个长期的连接是为了服务与它进行交互。(操作中会获得service的binder对象,通过binder来调用相关的服务)

    When a Service component is actually created, for either of these reasons, all that the system actually does is instantiate the component and call its onCreate() and any other appropriate callbacks on the main thread. It is up to the Service to implement these with the appropriate behavior, such as creating a secondary thread in which it does its work.
    Note that because Service itself is so simple, you can make your interaction with it as simple or complicated as you want: from treating it as a local Java object that you make direct method calls on (as illustrated by Local Service Sample), to providing a full remoteable interface using AIDL.
    当一个服务组件被创造时,系统都会调用它的oncreate()和主线程的任何其他适当的回调。它是由服务来实现这些与适当的行为,如创建一个辅助线程来完成它的工作。
    值得注意的是,由于服务本身那么简单,你可以用service与其他应用或其他组件实现一些或简单或复杂的交互:比如要为本地Java对象你直接调用方法,也可以调用AIDL接口进行进程间操作。

Service Lifecycle 服务的生命周期

There are two reasons that a service can be run by the system. If someone calls Context.startService() then the system will retrieve the service (creating it and calling its onCreate() method if needed) and then call its onStartCommand(Intent, int, int) method with the arguments supplied by the client. The service will at this point continue running until Context.stopService() or stopSelf() is called. Note that multiple calls to Context.startService() do not nest (though they do result in multiple corresponding calls to onStartCommand()), so no matter how many times it is started a service will be stopped once Context.stopService() or stopSelf() is called; however, services can use their stopSelf(int) method to ensure the service is not stopped until started intents have been processed.
有两个理由可以说明服务可以被系统运行。如果有人Context.startService()来启动服务。然后系统会检索服务(创造它,如果需要调用它的oncreate()方法)然后调用onStartCommand(Intent, int, int) 方法。然后该服务将一直运行,直到stopservice()或stopself()被调用。请注意,当服务启动后,再调用Context.startService() 方法时,(onstartcommand()会被调用oncreate()方法不会再次被调用,也就是说服务只会被创建一次,然调用stopservice()或stopself()会停止服务;然而,服务可以用stopSelf(int)来保证服务直到成功打开意图之后才停止。

For started services, there are two additional major modes of operation they can decide to run in, depending on the value they return from onStartCommand(): START_STICKY is used for services that are explicitly started and stopped as needed, while START_NOT_STICKY or START_REDELIVER_INTENT are used for services that should only remain running while processing any commands sent to them.
为启动的服务,有两个主要的操作模式,他们可以决定运行,取决于他们的回报onstartcommand()价值:start_sticky用于显式启动和停止所需要的服务,而start_not_sticky或start_redeliver_intent用于服务,应运行在处理任何命令发送给他们。

Clients can also use Context.bindService() to obtain a persistent connection to a service. This likewise creates the service if it is not already running (calling onCreate() while doing so), but does not call onStartCommand(). The client will receive the IBinder object that the service returns from its onBind(Intent) method, allowing the client to then make calls back to the service. The service will remain running as long as the connection is established (whether or not the client retains a reference on the service’s IBinder). Usually the IBinder returned is for a complex interface that has been written in aidl.
客户端也可以使用Context.bindService() 获得持久连接到服务。这同样造成服务如果它没有运行(虽然这样叫oncreate()),但不调用onstartcommand()。客户端将从onBind(Intent)方法得到IBinder对象,让绑定者可以调用相关服务。该服务被创建后将和绑定者保持连接(不管客户端是否仍保留服务的IBinder)。通常返回的是一个复杂的接口,这个已经在AIDL中实现。

A service can be both started and have connections bound to it. In such a case, the system will keep the service running as long as either it is started or there are one or more connections to it with the Context.BIND_AUTO_CREATE flag. Once neither of these situations hold, the service’s onDestroy() method is called and the service is effectively terminated. All cleanup (stopping threads, unregistering receivers) should be complete upon returning from onDestroy().
对于一个服务的绑定者,服务既可以被开启,也可以被连接。在这种情况下,系统会保持服务的运行只要是开始或有一个或多个连接到它的context.bind_auto_create标识。一旦这些情况都存在,服务的ondestroy()方法被调用后,服务才会有效终止。这样,所有的清理(停止线程,注销接收器)等方法都可以卸载ondestroy()中。

Process Lifecycle 进程周期

The Android system will attempt to keep the process hosting a service around as long as the service has been started or has clients bound to it. When running low on memory and needing to kill existing processes, the priority of a process hosting the service will be the higher of the following possibilities:
Android系统会试图让进程托管服务当这个服务被开启或被绑定时。在低内存的时候需要杀死现有的进程,一个托管了服务将的进程优先级更高:

If the service is currently executing code in its onCreate(), onStartCommand(), or onDestroy() methods, then the hosting process will be a foreground process to ensure this code can execute without being killed.
如果服务是目前正在执行oncreate()、onstartcommand()或ondestroy()方法,然后宿主进程将变成前台进程以确保这些方法能被执行而不被杀死。

If the service has been started, then its hosting process is considered to be less important than any processes that are currently visible to the user on-screen, but more important than any process not visible. Because only a few processes are generally visible to the user, this means that the service should not be killed except in extreme low memory conditions.
如果此服务已被启动,任何可见进程都比它的宿主进程更重要,但比那些不可见的进程都要重要。因为只有很少的进程是可见进程,这表明服务应该不会在极端低内存情况下被杀死。

If there are clients bound to the service, then the service’s hosting process is never less important than the most important client. That is, if one of its clients is visible to the user, then the service itself is considered to be visible.
如果有客户端绑定到服务,那么服务的宿主进程比这些重要的客户端还重要。也就是说,如果绑定它的一个客户端是用户可见的,那么服务本身是可见的。

A started service can use the startForeground(int, Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory. (It is still theoretically possible for the service to be killed under extreme memory pressure from the current foreground application, but in practice this should not be a concern.)
一个启动的服务可以使用startForeground(int, Notification) API把服务设置为前台状态,系统认为是用户的主动意识,所以在内存不足时,该服务并不是被杀候选人。(这仍然是理论上可能的,在内存极端不足时,即便是前台service也有可能被杀。)

Note this means that most of the time your service is running, it may be killed by the system if it is under heavy memory pressure. If this happens, the system will later try to restart the service. An important consequence of this is that if you implement onStartCommand() to schedule work to be done asynchronously or in another thread, then you may want to use START_FLAG_REDELIVERY to have the system re-deliver an Intent for you so that it does not get lost if your service is killed while processing it.
注意,大部分时间你的服务一直在运行,在内存压力非常大的时候它也会被杀死。如果发生这种情况,系统将尝试重新启动服务。这是一个重要的结果,如果你实现onstartcommand()安排工作要在另一个线程异步完成,那么你可能想使用start_flag_redelivery让系统为你提供一个意图使它不会丢失之前的服务。

二、具体实例

1.最基本的用法

package com.servicedetail;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void StartService(View v) {        Intent startIntent = new Intent(this, MyService.class);        startService(startIntent);    }    public void StopService(View v) {        Intent startIntent = new Intent(this, MyService.class);        stopService(startIntent);    }}
package com.servicedetail;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.util.Log;public class MyService extends Service {    @Override    public void onCreate() {        super.onCreate();        Log.i("--","onCreate");    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i("--","onStartCommand");        return super.onStartCommand(intent, flags, startId);    }    @Override    public IBinder onBind(Intent intent) {        Log.i("--","onBind");        return null;    }    @Override    public void onDestroy() {        Log.i("--","onDestroy");        super.onDestroy();    }    @Override    public boolean onUnbind(Intent intent) {        Log.i("--","onUnbind");        return super.onUnbind(intent);    }    @Override    public void onRebind(Intent intent) {        Log.i("--","onRebind");        super.onRebind(intent);    }}

—–>最简单的startService, stopService
第一次点击startService,stopService:
这里写图片描述
在destroy之前点击两次startService,只会调用一次onCreate方法
这是由于onCreate()方法只会在Service第一次被创建的时候调用,如果当前Service已经被创建过了,不管怎样调用startService()方法,onCreate()方法都不会再执行。因此你可以再多点击几次StartService按钮试一次,每次都只会有onStartCommand()方法中的打印日志
这里写图片描述

2.Service和Activity通信

上面我们学习了Service的基本用法,启动Service之后,就可以在onCreate()或onStartCommand()方法里去执行一些具体的逻辑了。不过这样的话Service和Activity的关系并不大,只是Activity通知了Service一下:“你可以启动了。”然后Service就去忙自己的事情了。那么有没有什么办法能让它们俩的关联更多一些呢?比如说在Activity中可以指定让Service去执行什么任务。当然可以,只需要让Activity和Service建立关联就好了。
观察MyService中的代码,你会发现一直有一个onBind()方法我们都没有使用到,这个方法其实就是用于和Activity建立关联的,修改MyService中的代码,如下所示:

package com.servicedetail;import android.app.Activity;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.util.Log;import android.view.View;public class MainActivity extends Activity {    private MyService.MyBinder myBinder;    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.i("--", "MainActivity-->onServiceConnected");            myBinder = (MyService.MyBinder) service;            myBinder.conServer();        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.i("--", "MainActivity-->onServiceDisconnected");        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void StartService(View v) {        Intent startIntent = new Intent(this, MyService.class);        startService(startIntent);    }    public void StopService(View v) {        Intent startIntent = new Intent(this, MyService.class);        stopService(startIntent);    }    public void BindService(View v) {        Intent bindIntent = new Intent(this, MyService.class);        bindService(bindIntent, connection, BIND_AUTO_CREATE);    }    public void UnbindService(View v) {        if (connection != null)            unbindService(connection);    }}

运行效果:
(1):点击BindService
这里写图片描述
再点击UnbindService
这里写图片描述
—>可以看到activity和service建立连接了,而且这种步骤会destroy掉service。

(2):点击startService,在点击BindService ,再点击UnbindService,可以看到不会destroy掉service
这里写图片描述
如果再点击stopService就会destroy service。

3.如何销毁Service

在Service的基本用法这一部分,我们介绍了销毁Service最简单的一种情况,点击Start Service按钮启动Service,再点击Stop Service按钮停止Service,这样MyService就被销毁了,可以看到打印日志如下所示:

Service-->onCreateServiceService-->onStartCommandService-->onDestroy

那么如果我们是点击的Bind Service按钮呢?由于在绑定Service的时候指定的标志位是BIND_AUTO_CREATE,说明点击Bind Service按钮的时候Service也会被创建,这时应该怎么销毁Service呢?其实也很简单,点击一下Unbind Service按钮,将Activity和Service的关联解除就可以了。
先点击一下Bind Service按钮,再点击一下Unbind Service按钮,打印日志如下所示:

Service-->onCreateService-->onBindService-->onUnbindService-->onDestroy

以上这两种销毁的方式都很好理解。那么如果我们既点击了Start Service按钮,又点击了Bind Service按钮会怎么样呢?这个时候你会发现,不管你是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将两个按钮都点击一下,Service才会被销毁。也就是说,点击Stop Service按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。

4.创建前台Service

Service几乎都是在后台运行的,一直以来它都是默默地做着辛苦的工作。但是Service的系统优先级还是比较低的,当系统出现内存不足情况时,就有可能会回收掉正在后台运行的Service。如果你希望Service可以一直保持运行状态,而不会由于系统内存不足的原因导致被回收,就可以考虑使用前台Service。前台Service和普通Service最大的区别就在于,它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。当然有时候你也可能不仅仅是为了防止Service被回收才使用前台Service,有些项目由于特殊的需求会要求必须使用前台Service,比如QQ或微信等聊天的会在通知栏提示收到信息。

在onCreate()方法中或onBind()方法中添加如下代码:

Notification notification = new Notification(R.drawable.ic_launcher,    "有通知到来", System.currentTimeMillis());        Intent notificationIntent = new Intent(this, MainActivity.class);        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,                notificationIntent, 0);        notification.setLatestEventInfo(this, "通知标题", "通知内容", pendingIntent);        startForeground(1, notification);

在开启服务要调用startService()后,在通知栏会保持一个通知(只要不调用stopService(),就会一直处在通知栏);,这是即使程序关闭,都不会消失。 如果调用bindService,程序关闭,服务就会关闭。
这里写图片描述

最后需要注意的是:
上面的官方文档已经说了,service不是一个单独的线程,也就是说service就在主线程中,所以如果要处理耗时操作的话,主线程肯定会卡。所以,在service中处理耗时操作也要新开一个线程。那为什么不直接在activity中开一个线程来处理呢?

原因有:
1.activity难以控制线程,一旦activity销毁后,之前创建的进程就无法再次获得。
2.在一个activity中创建的线程无法被另一个activity操作。
3.Service可以很好的管理线程,即使activity销毁了,只要再次绑定service还可以通过service操作处理事务的线程。

源码

  相关解决方案