Android开发——Service的学习(上)
Service是Android的重要组件之一,能够在后台,并且不需要用户界面的组件。其他的应用程序组件可以启动一个服务,即使用户切换到另一个应用程序,服务依然可以运行。服务运行在主线程中,如果要完成一些耗时的或者阻塞的工作,开发人员可以在服务中创建一个新的线程来完成这些工作。
Service从本质上分为两种类型:Started(启动)和Bound(绑定)。
Started:
当应用程序组件通过startService()方法启动服务时,那么服务是started状态。一旦启动,服务就会在后台一直运行下去。 通常,started的服务执行单一的操作并且不会向调用者返回结果。 比如,它可以通过网络下载或上传文件。 当操作完成后,服务应该自行终止。
Bound:
当应用程序组件通过bindService()方法绑定到服务时,服务处于bound状态。bound服务提供了一个客户端/服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至可以利用进程间通信(IPC)跨进程执行这些操作。 绑定服务的生存期和被绑定的应用程序组件一致。 多个组件可以同时与一个服务绑定,不过所有的组件解除绑定后,服务也就会被销毁。
服务可以只属于一种类型,也可以同时属于两种类型,既可以启动,也可以绑定。关键在于是否实现两个回调方法:onStartCommand()方法允许组件启动服务,onBind()方法允许组件绑定服务。
一:Service的重要方法
为了创建服务,必须创建Service类(或其存在的子类)的子类。在实现代码中需要重写一些回调方法,用于处理服务生命周期的一些关键节点,并且为组件提供绑定服务的机制。最重要的需要重写的回调方法如下:
onStartCommand():
当其他组件,比如一个activity,通过调用startService()请求started类型的服务时,系统将会调用这个方法。一旦这个方法执行,服务就会启动并且在后台一直运行下去。如果你的代码实现了本方法,你就有责任在完成工作后通过调用 stopSelf() 或 stopService() 终止服务(如果你只想提供bind方式,那就不需要实现本方法)。
onBind():
当其它组件需要通过 bindService() 绑定服务时(比如执行RPC),系统会调用这个方法。 在这个方法的实现代码中,你必须返回 IBinder 来提供一个接口,客户端用它来和服务进行通信。 你必须确保实现本方法,不过假如你不需要提供绑定,那就返回null即可。
onCreat():
当服务第一次被创建时,系统会调用此方法,用于执行一次性的创建过程(在系统调用onStartCommand()和onBind()前,调用此方法)。如果服务依然运行,那么不会再调用此方法。
onDestroy():
当服务不再被使用并且将要被销毁时调用此方法。服务应该调用此方法去清理一些资源,比如线程,注册监听,接受者等等。如果服务已经运行,此方法不会被调用。
如果组件调用onStart()方法启动服务(这会导致onStartCommand()被调用), 那么服务将一直保持运行,直至自行用 stopSelf() 终止或由其它组件调用 stopService() 来终止它。
如果组件调用 bindService() 来创建服务(那 onStartCommand() 就不会被调用),则服务的生存期就与被绑定的组件一致。一旦所有客户端都对服务解除了绑定,系统就会销毁该服务。
二:在Manifest中声明Service
与activity(及其它组件)类似,你必须在应用程序的manifest文件中对所有的服务进行声明。要声明你的服务,把 <service> 元素作为子元素加入到 <application> 元素中去即可。例如:
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application> </manifest>
在 <service> 元素中可以包含很多其它属性,比如定义启动服务所需权限、服务运行的进程之类的属性。 android:name 是唯一必需的属性——它定义了服务的类名。应用程序一经发布,就不得再修改这个类名。因为这么做可能会破坏某些显式引用该服务的intent功能。
三:创建Started服务
Started Service 是由其他组件调用startService()方法启动的,这会导致onStartCommand()方法被调用。当服务是started状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行。因此服务需要自行用 stopSelf() 终止或由其它组件调用 stopService() 来终止它。
诸如activity之类的应用程序组件,可以通过调用 startService() 启动服务,并传入一个给出了服务和服务所需数据的 Intent 对象。服务将在 onStartCommand() 方法中接收到该 Intent 对象。
例如,假定某activity需要把一些数据保存到在线数据库中。此activity可以启动一个companion service 并通过传递一个intent到 startService() 的方法把需要保存的数据发送给该服务。该服务在 onStartCommand() 内接收intent,连接Internet,再进行数据库事务处理。当事务完成后,服务自行终止,并被系统销毁。
传统做法,你可以扩展两个类来创建started服务:
Service类:
这是所有服务的基类。如果你要扩展该类,则很重要的一点是:请在其中创建一个新的线程来完成所有的服务工作。 因为服务默认是使用应用程序的主线程的,这会降低应用程序中activity的运行性能。
IntentService类:
这是 Service 类的子类,它每次启动一个工作(worker)线程来处理所有的启动请求。 如果服务不需要同时处理多个请求的话,这是最佳的选择。 所有你要做的工作就是实现 onHandleIntent() 即可,它会接收每个启动请求的intent,然后就可在后台完成工作。
继承IntentService类
因为大多数started服务都不需要同时处理多个请求(实际上在多线程一下是危险的),所以最佳方式也许就是用 IntentService 类来实现上述的服务。
IntentService 将执行以下步骤:
- 创建一个默认的工作(worker)线程,它独立于应用程序主线程,来执行所有发送到 onStartCommand() 的intent。
- 创建一个工作队列,每次向实现的 onHandleIntent() 传入一个intent,这样你就永远不必担心多线程问题了。
- 在处理完所有的启动请求后,终止服务,因此你就永远不需调用 stopSelf() 了。
- 提供默认的 onBind() 实现代码,它返回null。
- 提供默认的 onStartCommand() 实现代码,它把intent送入工作队列,稍后会再传给onHandleIntent() 实现代码。
以上说明:要做的全部工作就是实现 onHandleIntent() 的代码,来完成客户端提交的任务。(由于IntentService类没有提供控参数的构造方法,所以你还需要为服务提供一小段构造方法)。以下是例子:
public class HelloIntentService extends IntentService { public HelloIntentService() { super("HelloIntentService"); } @Override protected void onHandleIntent(Intent intent) { // 通常可以在这里添加任务代码, 比如下载一个文件. // 作为小例子,我们让他休眠5秒钟. long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } } }
继承IntentService类
如上所述,利用 IntentService 来实现一个started服务非常简单。 不过,假如你的服务需要多线程运行(而不是通过一个工作队列来处理启动请求),那你可以扩展 Service 类来完成每个intent的处理。作为对照,以下例程实现了 Service 类,它执行的工作与上述使用 IntentService 的例子相同。确切地说,对于每一个启动请求,它都用一个工作线程来完成处理工作,并且每次只处理一个请求。
public class HelloService extends Service { private Looper mServiceLooper; private ServiceHandler mServiceHandler; // 处理从线程接受到的消息 private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // 通常可以在这里添加任务代码, 比如下载一个文件. // 作为小例子,我们让他休眠5秒钟. long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } // 根据startId停止服务,这样就确保不会在处理任务中间停止服务了 stopSelf(msg.arg1); } } @Override public void onCreate() { // 启动运行服务的线程. // 创建一个单独的线程,因为服务通常运行于进程的主线程中,可我们不想阻塞主线程。 // 我们还要赋予它后台运行的优先级,以便计算密集的工作不会干扰我们的UI。 HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // 获得线程的Looper,用来创建Handler mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); // 对于每一个启动请求,发送一个信息去启动一个任务,并且传递startID,这样就会知道在任务结束时,停止哪一个请求服务。 Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; mServiceHandler.sendMessage(msg); // 如果服务被杀死,在这里重启服务 return START_STICKY; } @Override public IBinder onBind(Intent intent) { // 因为并不提供绑定,所以 return null return null; } @Override public void onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); } }
四:启动服务
从activity或其它应用程序组件中可以启动一个服务,调用 startService() 并传入一个 Intent (指定所需启动的服务)即可。Android系统将调用服务的 onStartCommand() 方法,并传入该 Intent。例如,一个activity可以用一个显式的intent通过 startService() 启动上一节的示例服务(HelloSevice):
Intent intent = new Intent(this, HelloService.class); startService(intent);
startService() 方法会立即返回,Android系统会去调用服务的 onStartCommand() 方法。如果服务还未运行,系统会首先调用 onCreate() ,然后再去调用 onStartCommand() 。
如果服务不同时支持绑定,那么通过 startService() 传入的intent将是应用程序组件与服务进行交互的唯一途径。 当然,如果你期望服务能返回结果,那启动服务的客户端可以创建一个 PendingIntent 来获得一个广播broadcast(利用 getBroadcast() ),并把它放入启动服务的 Intent 并传到服务中去。然后服务就会用这个broadcast来传递结果。
多个启动服务的请求将会引发服务 onStartCommand() 方法的多次调用。不过,仅仅需要一个终止服务的方法(用 stopSelf() 或 stopService() )来停止服务。
五:停止服务
一个started服务必须自行管理生命周期。也就是说,系统不会终止或销毁这类服务,除非必须恢复系统内存并且服务返回后一直维持运行。 因此,服务必须通过调用 stopSelf() 自行终止,或者其它组件可通过调用 stopService() 来终止它。
用 stopSelf() 或 stopService() 的终止请求一旦发出,系统就会尽快销毁服务。
不过,如果你的服务要同时处理多个 onStartCommand() 请求,那在处理启动请求的过程中,你就不应该去终止服务,因为你可能接收到了一个新的启动请求(在第一个请求处理完毕后终止服务将停止第二个请求的处理。 为了避免这个问题,你可以用 stopSelf(int) 来确保终止服务的请求总是根据最近一次的启动请求来完成。 也就是说,当你调用 stopSelf(int) 时,同时将启动请求的ID(发送给 onStartCommand() 的startId)传给了对应的终止请求。这样,如果服务在你可以调用 stopSelf(int) 时接收到了新的启动请求,会会因为ID不一样,将不会终止服务。
注:主要是翻译官网的内容,因为最好的学习资料还是在官网上啊
http://developer.android.com/guide/components/services.html