• Android--Service之基础


    前言

      本篇博客聊一下Android下的Service组件,对于Service组件,有点类似于Windows下的服务。Service是Android四大组件中与Activity最相似的组件,它们的区别在于:Service一直在后台运行,它没有用户界面。一旦Service被启动起来之后,它就与Activity一样,也具有自己的生命周期。

      在开发过程中,对于Activity与Service的选择标准是:如果某个程序组件需要在运行时向用户呈现某种界面,或者该程序需要与用户交互,就需要使用Activity,否则就应该考虑使用Service。

      本篇博客主要内容:

    1. Service概述
    2. Service清单文件配置
    3. Service开发步骤
    4. startService
    5. bindService
    6. Service的适用场景

    Service概述

      与开发一个Activity类似,它需要继承Service这个抽象类,并在实现类中,需要重写一些回调方法,用于处理Service的生命周期各部分的操作。而Service也是继承自Context,因此它也可以调用Context里定义的如getResource()、getContentResolver()等方法。

      Service中定义的生命周期方法,对Service服务的开发大部分工作就围绕以下几个方法进行操作:

    • void onCreate():当该Service第一次被创建后将立即回调该方法。
    • void onStartCommand(Intent intent,int flags,int startId):每次通过startService()方法启动Service时都会被回调。
    • void onDestroy():当Service被关闭前会被回调。
    • abstract IBinder onBind(Intent intent):该方法是Service子类必须实现的方法,如果不需要通过绑定的方式启动服务,可以返回Null。
    • boolean onUnbind(Intent intent):当Service上绑定的所有客户端都断开连接将回调该方法。

       通过服务的启动方式与适用范围,可将服务分为两类服务:

    • start:启动服务,当一个Android组件(如一个Activity)调用startService()的时候,启动一个服务。服务一旦启动,就可以一直在后台运行下去,即使这个启动它的组件被摧毁。这样的服务模式,通常用于执行一个操作而不需要返回结果给调用者。
    • Bound:绑定服务,当一个Android组件(如一个Activity)调用bindService()。一个绑定服务提供了一个客户端到服务端的接口,允许组件与服务之间进行交互,这样可以实现跨进程的通信。绑定服务的生命周期默认是跟随它的绑定组件的,但是一个绑定服务可以绑定多个Android组件,如果这些Android组件都被销毁,那么这个绑定服务也将被销毁。

      虽然上面提到了服务有两种类别,但是一个服务类所要继承的类是一样的,都是Service类。也就是说,一个服务,可以包含上面两种运行方式的服务,只是与它重载的方法有关,如果重写了onStartCommand()即支持启动服务,如果重写onBiind()即支持绑定服务,所以如果同时重载实现这两个方法即可实现两种服务。

      而对于两种启动方式的服务,生命周期中被回调的方法也不一样,下图明确说明了Service两种情况下的生命周期:

     

    清单文件的配置

      Service是Android四大组件之一,所以它也必须在 AndroidManifest清单文件中进行配置,否则系统将找不到这个服务。与Activity一样,Service的配置也是在<application/>节点下,使用<service/>配置,其中android:name属性为Service类。

      如果开发的服务需要被外部应用操作,还需要配置<intent-filter/>节点,但是如果仅本程序使用,则无需配置它也可以。

      如果这个服务强制仅本应用操作,可以配置<service/>节点的android:exported属性为false,这样即使配置了<intent-filter/>,外部应用也无法操作这个服务,android:exported属性默认为true。

      清单文件配置示例:

     1     <application>
     2          <!-- 普通的服务 -->
     3         <service android:name=".Service1"></service>
     4         <!-- 可被外部应用访问的服务 -->
     5         <service android:name=".Service2">
     6             <intent-filter >
     7                 <action android:name="com.bgxt.Service2"/>
     8             </intent-filter>
     9         </service>
    10         <!-- 无法被外部应用访问的服务 -->
    11         <service android:name=".Service3"  android:exported="false">
    12             <intent-filter >
    13                 <action android:name="com.bgxt.Service3"/>
    14             </intent-filter>
    15         </service>
    16     </application>

    Service的开发步骤

      既然Service是一个与Activity类似的Android组件,所以它的开发步骤大致也为一下几步:

    1. 开发一个服务类,需要继承Service或者IntentService。
    2. 在AndroidManifest清单文件中注册Service组件。
    3. 在一个Android组件中启动这个开发的Service组件。
    4. 服务使用完成之后,需要停止这个服务。

    启动服务

      启动服务必须实现Service.onStartCommond()方法。启动服务使用startService(Intent intent)方法开启一个服务,仅需要传递一个Intent对象即可,在Intent对象中指定需要启动的服务。而使用startService()方法启动的服务,在服务的外部,必须使用stopService()方法停止,在服务的内部可以调用stopSelf()方法停止当前服务。一旦使用startService()或者stopSelf()方法请求停止服务,系统会就会尽快销毁这个服务。

      对于启动服务,一旦启动将与访问它的组件无任何关联,即使访问它的组件被销毁了,这个服务也一直运行下去,直到被销毁!

    启动服务-示例

      下面开发一个简单的使用startService()启动的服务,首先开发一个服务类:

     1 package com.example.intentdemo2;
     2 
     3 import android.app.Service;
     4 import android.content.Intent;
     5 import android.os.IBinder;
     6 import android.util.Log;
     7 
     8 public class StartService extends Service {
     9     private final static String TAG = "main";
    10 
    11     @Override
    12     public IBinder onBind(Intent arg0) {
    13         // 仅通过startService()启动服务,所以这个方法返回null即可。
    14         return null;
    15     }
    16 
    17     @Override
    18     public void onCreate() {
    19         super.onCreate();
    20         Log.i(TAG, "Service is Created");        
    21     }
    22 
    23     @Override
    24     public int onStartCommand(Intent intent, int flags, int startId) {
    25         Log.i(TAG, "Service is started");
    26         return super.onStartCommand(intent, flags, startId);
    27     }
    28 
    29     @Override
    30     public void onDestroy() {
    31         Log.i(TAG, "Service is Destroyed");
    32         super.onDestroy();
    33     }
    34 
    35 }

      虽然这个Service类没有什么处理逻辑,但是它包含了Service框架,在实际开发过程中,只需要在其中回调的方法中加入实际的业务实现代码即可。下面再使用一个Activity来操作这个服务,在这个Activity中有两个按钮,分别用于启动和停止这个服务。

     1 package com.example.intentdemo2;
     2 
     3 import android.app.Activity;
     4 import android.content.ComponentName;
     5 import android.content.Intent;
     6 import android.os.Bundle;
     7 import android.util.Log;
     8 import android.view.View;
     9 import android.widget.Button;
    10 
    11 public class ServiceActivity1 extends Activity {
    12     private Button btnSer1, btnSer2;
    13     private Intent service=null;
    14     @Override
    15     protected void onCreate(Bundle savedInstanceState) {
    16         super.onCreate(savedInstanceState);
    17         setContentView(R.layout.activity_service1);
    18         btnSer1 = (Button) findViewById(R.id.btnSer1);
    19         btnSer2 = (Button) findViewById(R.id.btnSer2);
    20         // 设置服务启动的Intent
    21         service=new Intent(ServiceActivity1.this,StartService.class);
    22         btnSer1.setOnClickListener(new View.OnClickListener() {
    23 
    24             @Override
    25             public void onClick(View v) {
    26                 // 启动服务
    27                 startService(service);
    28             }
    29         });
    30 
    31         btnSer2.setOnClickListener(new View.OnClickListener() {
    32 
    33             @Override
    34             public void onClick(View v) {
    35                 // 停止服务
    36                 stopService(service);
    37             }
    38         });
    39     }
    40 }

      执行结果均在日志中,可以在LogCat中查看,先启动服务,再停止服务:

       

    绑定服务

      如果Service和宿主之间需要进行方法调用或者数据交换,则应该使用Context对象的bindService()和unbindService()方法来绑定和解除绑定服务。

      Context的bindService()方法的完整方法签名为:

        bindService(Intent service,ServiceConnection conn,int flags)

      下面简单介绍一下这个方法的三个参数的意义:

    • service:通过Intent指定要绑定的Service。
    • conn:一个ServiceConnection对象,该对象用于监听访问者与Service对象的onServiceConnected()方法。
    • flags:指定绑定时是否自动创建Service。0不自动创建、BIND_AUTO_CREATE,自动创建。

      从上面的bindService方法可以看出,绑定一个服务于宿主交互,依托于一个ServiceConnection接口,这个接口对象必须声明在主线程中,通过实现其中的两个方法,来实现与Service的交互,下面分别介绍一下这两个方法:

    • void onServiceConnection(ComponentName name,IBinder service):绑定服务的时候被回调,在这个方法获取绑定Service传递过来的IBinder对象,通过这个IBinder对象,实现宿主和Service的交互。
    • void onServiceDisconnected(ComponentName name):当取消绑定的时候被回调。但正常情况下是不被调用的,它的调用时机是当Service服务被意外销毁时,例如内存的资源不足时这个方法才被自动调用。

       在使用绑定的服务的时候,该Service类必须提供一个IBinder onBind(Intent intent)方法,在绑定本地Service的情况下,onBind()方法说返回的IBinder对象会传给宿主的ServiceConnection.onServiceConnected()方法的service参数,这样宿主就可以通过IBinder对象与Service进行通信。实际开发中一般会继承Binder类(IBinder的实现类)的方式实现自己的IBinder对象。

      需要注意的是,如果绑定服务提供的onBind()方法返回为Null,则也可以使用bindService()启动服务,但不会绑定上Service,因此宿主的ServiceConnection.onServiceConnected()方法不会被执行,也就不存在于宿主与服务的交互。

    绑定服务-示例

      说了这么多绑定服务相关的内容,下面通过一个例子来实现Service的绑定与数据交互。

      开发一个Service类,用于进行绑定,在Service类中,做一个简单的数值累加,每秒加一。

     1 package com.example.intentdemo2;
     2 
     3 import android.app.Service;
     4 import android.content.Intent;
     5 import android.os.Binder;
     6 import android.os.IBinder;
     7 import android.util.Log;
     8 
     9 public class BindService extends Service {
    10     private final static String TAG = "main";
    11     private int count;
    12     private boolean quit;
    13     
    14     private Thread thread;
    15     private MyBinder binder=new MyBinder();
    16     public class MyBinder extends Binder
    17     {
    18         // 声明一个方法,把count暴露给外部程序。
    19         public int getCount(){
    20             return count;
    21         }
    22     }
    23     
    24     @Override
    25     public void onCreate() {
    26         super.onCreate();
    27         Log.i(TAG, "Service is Created");
    28         thread=new Thread(new Runnable() {            
    29             @Override
    30             public void run() {
    31                 // 每间隔一秒count加1 ,直到quit为true。
    32                 while(!quit){
    33                     try{
    34                         Thread.sleep(1000);
    35                     }catch(InterruptedException e){
    36                         e.printStackTrace();
    37                     }
    38                     count++;
    39                 }
    40             }
    41         });
    42         thread.start();
    43     }
    44     
    45     @Override
    46     public boolean onUnbind(Intent intent) {
    47         Log.i(TAG, "Service is Unbinded");
    48         return true;
    49     }
    50     
    51     @Override
    52     public int onStartCommand(Intent intent, int flags, int startId) {
    53         Log.i(TAG, "Service is started");
    54         return super.onStartCommand(intent, flags, startId);
    55     }
    56 
    57     @Override
    58     public void onDestroy() {
    59         super.onDestroy();
    60         Log.i(TAG, "Service is Destroyed");
    61         this.quit=true;
    62         
    63     }
    64     
    65     @Override
    66     public IBinder onBind(Intent intent) {
    67         Log.i(TAG, "Service is Binded");
    68         return binder;
    69     }
    70 }

      然后使用一个Activity来绑定上面这个Service类,并且声明一个ServiceConnection对象,用于进行数据交互。 

     1 package com.example.intentdemo2;
     2 
     3 import android.app.Activity;
     4 import android.app.Service;
     5 import android.content.ComponentName;
     6 import android.content.Intent;
     7 
     8 import android.content.ServiceConnection;
     9 import android.os.Bundle;
    10 import android.os.IBinder;
    11 import android.util.Log;
    12 import android.view.View;
    13 import android.widget.Button;
    14 import android.widget.Toast;
    15 
    16 public class ServiceActivity2 extends Activity {
    17     private final String TAG = "main";
    18     Button bind, unbind, getServiceStatus;
    19     BindService.MyBinder binder;
    20     private ServiceConnection conn = new ServiceConnection() {
    21         @Override
    22         public void onServiceDisconnected(ComponentName name) {            
    23             Log.i(TAG, "--Service Disconnected--");
    24         }
    25         @Override
    26         public void onServiceConnected(ComponentName name, IBinder service) {
    27             Log.i(TAG, "--Service Connected--");
    28             // 取得Service对象中的Binder对象
    29             binder = (BindService.MyBinder) service;
    30         }
    31     };
    32 
    33     @Override
    34     protected void onCreate(Bundle savedInstanceState) {
    35         super.onCreate(savedInstanceState);
    36         setContentView(R.layout.activity_bindservice1);
    37         
    38         bind = (Button) findViewById(R.id.bind);
    39         unbind = (Button) findViewById(R.id.unbind);
    40         getServiceStatus = (Button) findViewById(R.id.getServiceStatus);
    41         
    42         final Intent intent = new Intent();
    43         // 指定开启服务的action
    44         intent.setAction("com.bgxt.BindServiceDemo");
    45         
    46         bind.setOnClickListener(new View.OnClickListener() {
    47 
    48             @Override
    49             public void onClick(View v) {
    50                 // 绑定服务到当前activity中
    51                 bindService(intent, conn, Service.BIND_AUTO_CREATE);
    52             }
    53         });
    54         unbind.setOnClickListener(new View.OnClickListener() {
    55 
    56             @Override
    57             public void onClick(View v) {
    58                 // 解除绑定
    59                 binder=null;
    60                 unbindService(conn);
    61             }
    62         });
    63         getServiceStatus.setOnClickListener(new View.OnClickListener() {
    64 
    65             @Override
    66             public void onClick(View v) {
    67                 if(binder!=null)
    68                 {
    69                     // 通过绑定服务传递的Binder对象,获取Service暴露出来的数据
    70                     Toast.makeText(ServiceActivity2.this,
    71                             "Service的Count值为" + binder.getCount(),
    72                             Toast.LENGTH_SHORT).show();
    73                     Log.i(TAG, "Service的Count值为" + binder.getCount());
    74                 }
    75                 else
    76                 {
    77                     Toast.makeText(ServiceActivity2.this,
    78                             "还没绑定呢,先绑定。",
    79                             Toast.LENGTH_SHORT).show();
    80                 }                
    81             }
    82         });
    83     }
    84 }

      执行结果,先绑定服务,然后获取当前服务运行时count的值,最后解除绑定,把执行过程输出到LogCat中。

    Service的选用

      相信看完以上的内容,对Android下Service组件有了一定的了解,但是对于选用startService()还是使用bindService(),有一些原则需要讲明。如果仅仅是想要启动一个后台服务长期驻留在内存中执行某项任务,那么仅使用startService()启动一个服务即可。但是如果想要与正在运行的服务进行交互,一种方式是使用broadcast,这个以后再介绍,另外一种方式就是使用bindService()绑定一个服务到组件上,使用broadcast的缺点是如果数据交互频繁,容易造成性能上的问题,并且BroadcastReceiver本身执行代码的时间不确定,也许代码执行到一半,后面的代码将不被执行,但是使用bindService()绑定服务即没有这些问题。另外,如果一个服务是依托于某项应用的,那么也最好使用绑定服务,在依托的应用启动的时候绑定服务,这样可以在不使用的时候避免浪费系统资源。

      源码下载

    总结

      值得注意的是,Android下的Service组件也是运行在主线程中的,所以一些Android4.0+无法在主线程上进行的操作,在Service中也必须另外开启线程来完成,如访问网络,还可以使用继承Service的子类IntentService来实现,这个内容会在之后的博客中介绍。Android系统本身还提供了大量的Service组件,开发人员可以通过这些系统Service来操作Android系统本身,这不属于本篇博客的范畴,以后再慢慢详解。

  • 相关阅读:
    C++------------------>深浅拷贝的问题
    超越 EfficientNet与MobileNetV3,NeurIPS 2020 微软NAS方向最新研究
    数学之美
    mobilenetV2--->特点
    安装R语言扩展包vegan
    每日积累新知识
    安装生物信息学软件-R
    安装生物信息学软件-MetaPhlAn2
    概率统计&假设检验-1
    Population-based metagenomics analysis reveals markers for gut microbiome composition and diversity
  • 原文地址:https://www.cnblogs.com/plokmju/p/Android_Service1.html
Copyright © 2020-2023  润新知