安卓四大组件之--Service

Service是Android系统中的四大组件之一,主要有两个应用场景:后台运行和跨进程访问。Service能够在后台执行长时间运行操做而不提供用户界面,除非系统必须回收内存资源,不然系统不会中止或销毁服务。服务可由其余应用组件启动,并且即便用户切换到其余应用,服务仍将在后台继续运行。 此外,组件能够绑定到服务,以与之进行交互,甚至是执行进程间通讯 (IPC),须要注意的是,Service是在主线程里执行操做的,可能会由于执行耗时操做而致使ANRandroid

1、基础知识

Service能够分为如下三种形式:

  • 启动

    当应用组件经过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务便可在后台无限期运行,即便启动服务的组件已被销毁也不受影响。 已启动的服务一般是执行单一操做,并且不会将结果返回给调用方。编程

  • 绑定

    当应用组件经过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,容许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通讯 (IPC) 跨进程执行这些操做。多个组件能够同时绑定服务,服务只会在组件与其绑定时运行,一旦该服务与全部组件之间的绑定所有取消,系统便会销毁它。安全

  • 启动且绑定

    服务既能够是启动服务,也容许绑定。此时须要同时实现如下回调方法:onStartCommand()和 onBind()。系统不会在全部客户端都取消绑定时销毁服务。为此,必须经过调用 stopSelf() 或 stopService() 显式中止服务不管应用是处于启动状态仍是绑定状态,或者处于启动且绑定状态,任何应用组件都可像使用 Activity 那样经过调用 Intent 来使用服务(即便此服务来自另外一应用),也能够经过清单文件将服务声明为私有服务,阻止其余应用访问要使用服务,必须继承Service类(或者Service类的现有子类),在子类中重写某些回调方法,以处理服务生命周期的某些关键方面并提供一种机制将组件绑定到服务。服务器

  • onStartCommand()

    当组件经过调用 startService() 请求启动服务时,系统将调用此方法(若是是绑定服务则不会调用此方法)。一旦执行此方法,服务即会启动并可在后台无限期运行。 在指定任务完成后,经过调用 stopSelf() 或 stopService() 来中止服务多线程

  • onBind()

    当一个组件想经过调用 bindService() 与服务绑定时,系统将调用此方法(若是是启动服务则不会调用此方法)。在此方法的实现中,必须经过返回 IBinder 提供一个回调接口,供客户端用来与服务进行通讯app

  • onCreate()

    首次建立服务时,系统将调用此方法来执行初始化操做(在调用 onStartCommand() 或 onBind() 以前)。若是在启动或绑定以前Service已在运行,则不会调用此方法dom

  • onDestroy()

    当服务再也不使用且将被销毁时,系统将调用此方法,这是服务接收的最后一个调用,在此方法中应清理占用的资源仅当内存太低必须回收系统资源以供前台 Activity 使用时,系统才会强制中止服务。若是将服务绑定到前台 Activity,则它不太可能会终止,若是将服务声明为在前台运行,则它几乎永远不会终止。或者,若是服务已启动并要长时间运行,则系统会随着时间的推移下降服务在后台任务列表中的位置,而服务也将随之变得很是容易被终止。若是服务是启动服务,则必须将其设计为可以妥善处理系统对它的重启。 若是系统终止服务,那么一旦资源变得再次可用,系统便会重启服务(这还取决于 onStartCommand() 的返回值)异步

2、声明Service

  如同其余组件同样,想要使用Service,必须在清单文件中对其进行声明,声明方式是添加 < service > 元素做为 < application > 元素的子元素,例如ide

<application 
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
  <service android:name=".MyService" />
</application>

 

android:name 属性是惟一必需的属性,用于指定服务的类名,还可将其余属性包括在 < service > 元素中以定义一些特性,为了确保应用的安全性,最好始终使用显式 Intent 启动或绑定 Service,且不要为服务声明 Intent 过滤器。 启动哪一个服务存在必定的不肯定性,而若是对这种不肯定性的考量很是有必要,则可为服务提供 Intent 过滤器并从 Intent 中排除相应的组件名称,但随后必须使用 setPackage() 方法设置 Intent 的软件包,这样能够充分消除目标服务的不肯定性;此外,还能够经过添加 android:exported 属性并将其设置为 "false",确保服务仅适用于本应用。这能够有效阻止其余应用启动本应用内的服务,即使在使用显式 Intent 时也是如此,Service包含的属性有函数

 


<service android:description="string resource"
    android:directBootAware=["true" | "false"]
    android:enabled=["true" | "false"]
    android:exported=["true" | "false"]
    android:icon="drawable resource"
    android:isolatedProcess=["true" | "false"]
    android:label="string resource"
    android:name="string"
    android:permission="string"
    android:process="string" >
</service>

 

属性说明

description:对服务进行描述,属性值应为对字符串资源的引用,以便进行本地化

directBootAware:设置是否能够在用户解锁设备以前运行,默认值为“false”

enabled:设置是否能够由系统来实例化服务。< application >元素有本身的enabled属性,适用于包括服务在内的全部应用程序组件。要启用服务,< application >和< service >属性必须都为“true”(默认状况下都为true)。若是其中一个是“false”,则服务被禁用。

exported:

    设置其余应用程序的组件是否能够调用本服务或与其交互,若是能够,则为“true”。当值为“false”时,只有同一个应用程序或具备相同用户ID的应用程序的组件能够启动该服务或绑定到该服务。该属性的默认值取决于服务是否包含Intent filters。没有任何过滤器意味着它只能经过指定其确切的类名来调用,这意味着该服务仅用于应用程序内部使用(由于其余人不知道类名)。因此在这种状况下,默认值为“false”。 另外一方面,若是存在至少一个过滤器,意味着该服务打算供外部使用,所以默认值为“true”

icon:服务的图标,属性值应是对drawable资源的引用。若是未设置,则将使用应用程序图标

isolatedProcess:设置该服务是否做为一个单独的进程运行,若是设置为true,此服务将在与系统其他部分隔离的特殊进程下运行,而且没有本身的权限,与它惟一的通讯是经过服务API(绑定和启动)

label:能够向用户显示的服务的名称,属性值应是对字符串资源的引用

name:服务类的彻底限定名

permission:设定组件必须具备的权限,得以启动服务或绑定服务。若是startService(),bindService()或stopService()的调用者没有被授予此权限,则该方法将不会工做,而且Intent对象不会传递到服务中

process:

    用来运行服务的进程的名称。一般,应用程序的全部组件都运行在应用程序建立的默认进程中,它与应用程序包名具备相同的名称。 < application >元素的process属性能够为全部组件设置不一样的默认值,但组件可使用本身的进程属性覆盖默认值,从而容许跨多个进程扩展应用程序.

3、启动Service

启动服务由组件经过调用 startService() 启动,服务启动以后,其生命周期即独立于启动它的组件,而且能够在后台无限期地运行,即便启动服务的组件已被销毁也不受影响。所以,服务应经过调用 stopSelf() 来自行中止运行,或者由另外一个组件调用 stopService() 来中止

能够经过扩展两个类来建立启动服务:

  • Service

    这是全部服务的父类。扩展此类时,若是要执行耗时操做,必须建立一个用于执行操做的新线程,由于默认状况下服务将运行于UI线程

  • IntentService

    这是 Service 的子类,它使用工做线程逐一处理全部启动请求。若是应用不须要同时处理多个请求,这是最好的选择。IntentService只需实现构造函数与 onHandleIntent() 方法便可,onHandleIntent()方法会接收每一个启动请求的 Intent

3.一、继承Service

这里举一个音乐播放器的例子,继承Service类实现自定义Service,提供在后台播放音乐、暂停音乐、中止音乐的方法.

public class MyService extends Service {

  private final String TAG = "MyService";
  private MediaPlayer mediaPlayer;
  private int startId;
  public enum Control { PLAY, PAUSE, STOP }

  public MyService() { }

  @Override
  public void onCreate() {

    if (mediaPlayer == null) {
       mediaPlayer = MediaPlayer.create(this, R.raw.music);
       mediaPlayer.setLooping(false);
    }

    Log.e(TAG, "onCreate");
    super.onCreate();
  }
  
  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {

    this.startId = startId;
    Log.e(TAG, "onStartCommand---startId: " + startId);
    Bundle bundle = intent.getExtras();
  
    if (bundle != null) {
      Control control = (Control) bundle.getSerializable("Key");
      if (control != null) {
        switch (control) {
        case PLAY:
          play();
          break;
        case PAUSE:
          pause();
          break;
        case STOP:
          stop();
          break;
         }
      }
    }
    return super.onStartCommand(intent, flags, startId);
  }

  @Override
  public void onDestroy() {
  
    Log.e(TAG, "onDestroy");
    if (mediaPlayer != null) {
      mediaPlayer.stop();
      mediaPlayer.release();
    }

     super.onDestroy();
  }

  private void play() {
    if (!mediaPlayer.isPlaying()) {
       mediaPlayer.start();
    }
  }

 private void pause() {
    if (mediaPlayer != null && mediaPlayer.isPlaying()) {
       mediaPlayer.pause();
    }
  }
  
  private void stop() {
    if (mediaPlayer != null) {
      mediaPlayer.stop();
    }
   stopSelf(startId);
  }

  @Override
  public IBinder onBind(Intent intent) {
    Log.e(TAG, "onBind");
    throw new UnsupportedOperationException("Not yet implemented");
  }
}



 

在布局中添加三个按钮,用于控制音乐播放、暂停与中止

public class MainActivity extends AppCompatActivity { 

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }
  
  public void playMusic(View view) {
    Intent intent = new Intent(this, MyService.class);
    Bundle bundle = new Bundle();
    bundle.putSerializable("Key", MyService.Control.PLAY);
    intent.putExtras(bundle);
    startService(intent);
  }

  public void pauseMusic(View view) {
    Intent intent = new Intent(this, MyService.class);
    Bundle bundle = new Bundle();
    bundle.putSerializable("Key", MyService.Control.PAUSE);
    intent.putExtras(bundle); startService(intent);
  }

  public void stopMusic(View view) {
    Intent intent = new Intent(this, MyService.class);
    Bundle bundle = new Bundle();
    bundle.putSerializable("Key", MyService.Control.STOP);
    intent.putExtras(bundle); startService(intent); //或者是直接以下调用 //Intent intent = new Intent(this, MyService.class); //stopService(intent);
  }
}

 

在清单文件中声明Service,为其添加label标签,便于在系统中识别Service

<service android:name=".MyService" android:label="@string/app_name" />

 

这里写图片描述

点击“播放音乐”按钮后,在后台将会运行着名为“Service测试”的服务

 

这里写图片描述:

  1. 经过Log日志能够发现,屡次点击“播放音乐”按钮,“onCreate()”方法只会在初始时调用一次,“onStartCommand(Intent intent, int flags, int startId)”方法会在每次点击时都被调用,点击“中止音乐”按钮,“onDestroy()”方法会被调用当中,每次回调onStartCommand()方法时。

  2. 参数“startId”的值都是递增的,startId用于惟一标识每次对Service发起的处理请求。

  3. 若是服务同时处理多个 onStartCommand() 请求,则不该在处理完一个启动请求以后当即销毁服务,由于此时可能已经收到了新的启动请求,在第一个请求结束时中止服务会致使第二个请求被终止。为了不这一问题,可使用 stopSelf(int) 确保服务中止

   请求始终基于最新一次的启动请求。也就是说,若是调用 stopSelf(int) 方法的参数值与onStartCommand()接受到的最新的startId值不相符的话,stopSelf()方法就会失效,从而避免终止还没有处理的请求

  4. 若是服务没有提供绑定,则使用 startService() 传递的 Intent 是应用组件与服务之间惟一的通讯模式。若是但愿服务返回结果,则启动服务的客户端能够为广播建立一个 PendingIntent (使用 getBroadcast()),并经过启动服务的 Intent 传递给服务。而后,

   服务就可使用广播传递结果

  5. 当中,onStartCommand() 方法必须返回一个整数,用于描述系统应该如何应对服务被杀死的状况,

    返回值必须是如下常量之一:

  • START_NOT_STICKY

   若是系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,不然系统不会重建服务。这是最安全的选项,能够避免在没必要要时以及应用可以轻松重启全部未完成的做业时运行服务

  • START_STICKY

   若是系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但不会从新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种状况下,将传递这些 Intent ),不然系统会经过空 Intent 调用 onStartCommand()。这适用于不执行命令、

   但无限期运行并等待做业的媒体播放器(或相似服务)

  • START_REDELIVER_INTENT

   若是系统在 onStartCommand() 返回后终止服务,则会重建服务,并经过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该当即恢复的做业(例以下载文件)的服务

 

3.二、IntentService

因为大多数启动服务都没必要同时处理多个请求,所以使用 IntentService 类实现服务也许是最好的选择

IntentService 执行如下操做:

  • 建立默认的工做线程,用于在应用的主线程外执行传递给 onStartCommand() 的全部 Intent
  • 建立工做队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样就没必要担忧多线程问题
  • 在处理完全部启动请求后中止服务,所以没必要本身调用 stopSelf()方法
  • 提供 onBind() 的默认实现(返回 null)
  • 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工做队列和 onHandleIntent()

所以,只需实现构造函数与 onHandleIntent() 方法便可

这里举一个关于输出日志的例子

public class MyIntentService extends IntentService { 
  private final String TAG = "MyIntentService";
  public MyIntentService() {
    super("MyIntentService");
  }

  @Override
  protected void onHandleIntent(Intent intent) {
    Bundle bundle = intent.getExtras();
    if (bundle != null) {
      for (int i = 0; i < 5; i++) {
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        Log.e(TAG, bundle.getString("key", "默认值"));
      }
    }
  }
}
public class StartIntentServiceActivity extends AppCompatActivity {
  private int i = 1;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_start_intent_service);
  }

  public void startService(View view) {
    Intent intent = new Intent(this, MyIntentService.class);
    Bundle bundle = new Bundle();
    bundle.putString("key", "当前值:" + i++);
    intent.putExtras(bundle);
    startService(intent);
  }
}

 

当中,startService(View view)方法与一个Button绑定,连续快速地屡次点击Button,验证IntentService当中的日志是否依次输出,仍是交叉着输出

能够看到是依次输出的,即IntentService的工做线程是逐一处理全部启动请求的

这里写图片描述

此外,查看后台,能够看到当先后台应用程序进程中有两个服务

这里写图片描述

 

4、绑定Service

  应用组件(客户端)经过调用 bindService() 绑定到服务,绑定是异步的,系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder。要接收 IBinder,客户端必须提供一个 ServiceConnection 实例用于监控与服务的链接,并将其传递给 bindService()。当 Android 系统建立了客户端与服务之间的链接时,会回调ServiceConnection 对象的onServiceConnected()方法,向客户端传递用来与服务通讯的 IBinder多个客户端可同时链接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),即可将同一 IBinder 传递至其余绑定的客户端。当全部客户端都取消了与服务的绑定后,系统会将服务销毁(除非 startService() 也启动了该服务)。

另外,只有 Activity、服务和内容提供者能够绑定到服务,没法从广播接收器绑定到服务

能够经过如下三种方法定义IBinder接口:

  • 扩展 Binder 类

    若是服务是供本应用专用,而且运行在与客户端相同的进程中,则应经过扩展 Binder 类并从 onBind() 返回它的一个实例来建立接口。客户端收到 Binder 后,可利用它直接访问 Service 中可用的公共方法

  • 使用 Messenger

    如需让接口跨不一样的进程工做,则可以使用 Messenger 为服务建立接口。服务能够这种方式定义对应于不一样类型 Message 对象的 Handler。此 Handler 是 Messenger 的基础,后者随后可与客户端分享一个 IBinder,从而让客户端能利用 Message 对象向服务发送命令。此外,客户端还可定义自有 Messenger,以便服务回传消息。这是执行进程间通讯 (IPC) 的最简单方法,由于 Messenger 会在单一线程中建立包含全部请求的队列,这样就没必要对服务进行线程安全设计

  • 使用 AIDL

    AIDL(Android 接口定义语言)执行全部将对象分解成原语的工做,操做系统能够识别这些原语并将它们编组到各进程中,以执行 IPC。 以前采用 Messenger 的方法其实是以 AIDL 做为其底层结构。 如上所述,Messenger 会在单一线程中建立包含全部客户端请求的队列,以便服务一次接收一个请求。 不过,若是想让服务同时处理多个请求,则可直接使用 AIDL。 在此状况下,服务必须具有多线程处理能力,并采用线程安全式设计。如需直接使用 AIDL,必须建立一个定义编程接口的 .aidl 文件。Android SDK 工具利用该文件生成一个实现接口并处理 IPC 的抽象类,随后可在服务内对其进行扩展

 

4.一、绑定服务的具体步骤:

4.1.一、扩展 Binder 类

  若是服务仅供本地应用使用,不须要跨进程工做,则能够实现自有 Binder 类,让客户端经过该类直接访问服务中的公共方法。此方法只有在客户端和服务位于同一应用和进程内这一最多见的状况下方才有效

如下是具体的设置方法:

  • 在服务中建立一个可知足下列任一要求的 Binder 实例:
    • 包含客户端可调用的公共方法
    • 返回当前 Service 实例,其中包含客户端可调用的公共方法
    • 或返回由服务承载的其余类的实例,其中包含客户端可调用的公共方法
  • 从 onBind() 回调方法返回此 Binder 实例
  • 在客户端中,从 onServiceConnected() 回调方法接收 Binder,并使用提供的方法调用绑定服务

4.1.二、实现 ServiceConnection接口

重写两个回调方法:

  • onServiceConnected()

系统会调用该方法以传递服务的onBind() 方法返回的 IBinder

  • onServiceDisconnected()

Android 系统会在与服务的链接意外中断时,例如当服务崩溃或被终止时调用该方法。当客户端取消绑定时,系统不会调用该方法

4.1.三、调用 bindService(),传递 ServiceConnection 对象

4.1.四、当系统调用了 onServiceConnected() 的回调方法时,就能够经过IBinder对象操做服务了

4.1.五、要断开与服务的链接需调用 unbindService()方法。若是应用在客户端仍处于绑定状态时销毁客户端,会致使客户端取消绑定,更好的作法是在客户端与服务交互完成后当即取消绑定客户端,这样能够关闭空闲服务

示例代码:

public class MyBindService extends Service { 

  private IBinder myBinder;
  private Random mGenerator;
  private final String TAG = "MyBindService";

  public class MyBinder extends Binder {
    MyBindService getService() {
      return MyBindService.this;
    }
  }
  
  @Override
  public void onCreate() {
    Log.e(TAG, "onCreate");
    myBinder = new MyBinder();
    mGenerator = new Random();
    super.onCreate();
  }
  
  @Override
  public IBinder onBind(Intent intent) {
    Log.e(TAG, "onBind");
    return myBinder;
  }

  @Override
  public void onDestroy() {
    Log.e(TAG, "onDestroy");
    super.onDestroy();
  }

  @Override
  public boolean onUnbind(Intent intent) {
    Log.e(TAG, "onUnbind");
    return super.onUnbind(intent);
  }

  @Override
  public void onRebind(Intent intent) {
    Log.e(TAG, "onRebind");
    super.onRebind(intent);
  }

  public int getRandomNumber() {
    return mGenerator.nextInt(100);
  }
}
public class BindServiceActivity extends AppCompatActivity {
  private MyBindService mService;
  private boolean mBound = false;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_bind_service);
  }
  private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      MyBindService.MyBinder binder = (MyBindService.MyBinder) service;
      mService = binder.getService(); mBound = true;
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
      mBound = false;
    }
  };
  
  public void bindService(View view) {
    Intent intent = new Intent(this, MyBindService.class);
    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
  }

  public void unBindService(View view) {
    if (mBound) {
      unbindService(mConnection); mBound = false;
    }
  }

  public void getData(View view) {
    if (mBound) {
      Toast.makeText(this, "获取到的随机数:" + mService.getRandomNumber(), Toast.LENGTH_SHORT).show();
    } else {
      Toast.makeText(this, "服务未绑定", Toast.LENGTH_SHORT).show();
    }
  }   
  @Override
  protected void onDestroy() {
    super.onDestroy();
    if (mBound) {
      unbindService(mConnection);
      mBound = false;
    }
  }
}

 

4.二、绑定服务的生命周期

绑定服务的生命周期在同时启动服务的状况下比较特殊,想要终止服务,除了须要取消绑定服务外,还须要服务经过 stopSelf() 自行中止或其余组件调用 stopService()

其中,若是服务已启动并接受绑定,则当系统调用了onUnbind() 方法,想要在客户端下一次绑定到服务时调用 onRebind() 方法的话,则onUnbind() 方法需返回 true。onRebind() 返回空值,但客户端仍能够在其 onServiceConnected() 回调中接收到 IBinder对象

这里写图片描述

4.三、绑定时机

  • 若是只须要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定
  • 若是但愿 Activity 在后台中止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy() 期间取消绑定。这意味着 Activity 在其整个运行过程当中(包括后台运行期间)都须要使用此服务
  • 一般状况下,切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,由于每一次生命周期转换都会发生这些回调,应该使发生在这些转换期间的处理保持在最低水平。假设有两个Activity须要绑定到同一服务,从Activity A跳转到Activity B,这个过程当中会依次执行A-onPause,B-onCreate,B-onStart,B-onResume,A-onStop。这样系统会在A-onPause的时候销毁服务,又在B-onResume的时候重建服务。当Activity B回退到Activity A时,会依次执行B-onPause,A-onRestart,A-onStart,A-onResume,B-onStop,B-onDestroy。此时,系统会在B-onPause时销毁服务,又在A-onResume时重建服务。这样就形成了屡次的销毁与重建,所以须要选定好绑定服务与取消绑定服务的时机

5、在前台运行Service

前台服务被认为是用户主动意识到的一种服务,所以在内存不足时,系统也不会考虑将其终止。 前台服务必须在状态栏提供通知,放在“正在进行”标题下方,这意味着除非服务中止或从前台移除,不然不能清除通知

要请求让服务运行于前台,要调用 startForeground()方法,两个参数分别是:惟一标识通知的int类型整数和Notification对象

修改MyService当中的play()方法

private void play() { 
  if (!mediaPlayer.isPlaying()) {
    mediaPlayer.start();
    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
    mBuilder.setSmallIcon(R.drawable.bird);
    mBuilder.setContentTitle("这是标题吧~叶应是叶");
    mBuilder.setContentText("http://blog.csdn.net/new_one_object");
    startForeground(1, mBuilder.build());
  }
}

 

点击播放音乐后,状态栏就出现了一个通知

这里写图片描述

当中,提供给 startForeground() 的整型参数不得为 0。要从前台移除服务,需调用 stopForeground()方法,此方法不会中止服务。 可是,若是前台服务被中止,则通知也会被移除

6、Service的生命周期

服务生命周期从建立到销毁能够遵循两条不一样的路径:

  • 启动服务

该服务在其余组件调用 startService() 时建立,而后无限期运行,必须经过调用 stopSelf() 来自行中止运行或经过其余组件调用 stopService() 来中止服务。服务中止后,系统会将其销毁

  • 绑定服务

服务在另外一个组件(客户端)调用 bindService() 时建立。而后,客户端经过 IBinder 接口与服务进行通讯。客户端能够经过调用 unbindService() 关闭链接。多个客户端能够绑定到相同服务,并且当全部绑定所有取消后,系统即会销毁该服务(服务没必要自行中止运行)

这两条路径并不是彻底独立。也就是说,能够绑定到已经使用 startService() 启动的服务。例如,能够经过使用 Intent(标识要播放的音乐)调用 startService() 来启动后台音乐服务。随后,可能在用户须要稍加控制播放器或获取有关当前播放歌曲的信息时,Activity 能够经过调用 bindService() 绑定到服务。在这种状况下,除非全部客户端均取消绑定,不然 stopService() 或 stopSelf() 不会实际中止服务

相关文章
相关标签/搜索