Android-Service组件

1 Service概述

        Service服务是一个应用程序组件,它可以在后台执行长时间运行的操作。它不提供用户界面。一旦启动,即使是在用户切换到另一个应用程序之后,一个服务可能会继续运行一段时间。此外,其他组件可以绑定到Service,并与其交互,甚至执行进程间通信(IPC)。Service在其托管进程的主线程中运行,该服务不会创建自己的线程,在程序员没有另行指定的情况下,Service也不会在单独的某一进程中运行。因此,对于耗时操作的Service需要建立一个单独的线程,防止ANR。

1.1 Service的分类

        主要分:前台服务(Foreground),后台服务(Background),绑定服务(Bound)三种。

1.1.2 Bound绑定服务

        当应用程序组件通过调用bindService()绑定到服务时,服务被绑定。

        绑定服务提供了一个客户机-服务器接口(IBinder接口),允许组件与Service进行发送请求、接收结果,甚至跨进程间通信(IPC)等交互。绑定服务仅在另一个应用程序组件绑定到它时才运行。多个组件可以同时绑定到服务,但当所有组件都解除绑定时,服务将被销毁。

        虽然本文档通常分别讨论已启动服务和绑定服务,但您的服务可以以两种方式工作:它可以启动(立即运行),也可以允许绑定,也可以两者同时运行。这只是看是否实现了一些回调方法的问题:onStartCommand()和onBind()。

        无论服务是启动或绑定或两者都启动,任何应用程序组件都可以通过使用Intent来使用该服务

1.2 在服务和线程之间进行选择

        如果必须在主线程之外执行工作,但只有在用户与应用程序交互时,才应该在另一个应用程序组件的上下文中创建一个新线程。例如,如果您想播放一些音乐,但只有在您的活动正在运行时,您就可以在onCreate()中创建一个线程,在onStart()中启动线程,在onStop()中停止线程。

1.3 基本概念

        要创建Service,您必须创建Service的子类或使用其现有的子类。在实现中,您必须重写一些处理Service生命周期关键方面的回调方法,并提供一种机制允许组件绑定到Service上。

1.3.1 onStartCommand()

public int onStartCommand(Intent intent, int flags, int startId)

        当另一个组件请求启动服务startService (Intent service)时,系统OS将调用此方法。当此方法执行时,服务将启动,并且可以在后台运行。如果您实现了这个功能,那么您有责任在服务工作完成时通过调用stopSelf()或stopService()来停止服务

        如果您只想提供绑定,则不需要实现此方法

1.3.2 onBind()

public abstract IBinder onBind(Intent intent)

        当另一个组件请求启动服务bindService()时,系统OS将调用此方法。在此方法的实现中,必须提供一个接口,客户端通过此方法返回的IBinder与Service进行通信。

        必须始终实现此方法;如果不允许绑定,则应该返回null

1.3.3 onCreate()

public void onCreate()

        在最初创建服务时,系统会调用此方法来执行一次性设置过程。这个过程早于onStartCommand()和onBind()如果该服务已在运行,则不调用此方法。不要直接调用此方法。

1.3.4 onDestory()

public void onDestroy()

        当服务不再使用并正在被销毁时,系统将调用此方法。服务应该实现这个功能来清理任何资源,如线程、注册的侦听器或接收器这是该服务接收到的最后一次调用

1.4 流程图

Android-Service组件_第1张图片

2 Bound services概述

        绑定服务是客户机-服务器接口模型中的服务器部分。它允许组件绑定到服务,执行发送请求、接收响应,等进程间通信(IPC)。绑定服务通常只在另一个应用程序组件绑定服务时运行,并且不在后台运行

2.1 基本概念

        绑定服务是Service类的实现,它允许其他应用程序绑定到它并与之交互。要为服务提供绑定,必须实现onBind()回调方法。此方法返回一个IBinder对象,该对象定义了客户端可用于与服务交互的编程接口

        如果允许启动和绑定服务两种形式同时存在,那么当服务已经启动时,系统不会在所有客户端解除绑定时销毁服务。相反,必须通过调用stopSelf()或stopService()来显式地停止该服务。

        客户端通过调用bindService()来绑定到服务,当它实现时,它必须提供一个服务连接ServiceConnection()的实现,以监视与服务的连接。

        bindService()的返回值指示请求的服务是否存在以及是否允许客户端访问它。当安卓系统创建客户端和服务之间的连接时,它会在ServiceConnection()中调用在onServiceConnected()。

public abstract boolean bindService(Intent service, ServiceConnection conn, int flags) 
public interface ServiceConnection; 
public abstract void onServiceConnected(ComponentName name, IBinder service)

        bindService()是异步的,其操作会立即返回,不会将IBinder返回到客户端。客户端为了拿到IBinder,必须要实现ServiceConnection,并将其作为参数传递给bindService()

        ServiceConnection接口中的onServiceConnected()方法包括一个IBinder参数,客户端使用该参数与绑定的服务进行通信。

        绑定服务实现中最重要的部分是onBind()回调方法

2.2 创建绑定服务

        在创建提供绑定的服务时,必须提供一个IBinder,它提供客户端可用于与服务交互的编程接口。有三种方法可以定义接口。

2.2.1 继承Binder类

        如果服务是应用程序的私有服务,并且与客户端在相同的进程中运行(这是常见的),那么应该通过继承Binder类并从onBind()返回它的实例来创建接口。

        当服务只为自己的应用程序在台工作时,这是首选技术。不以这种方式创建接口的唯一原因是,服务被其他应用程序或跨不同的进程使用

2.2.2 使用Message类

        如果需要接口跨不同的进程工作,可以使用Message为服务创建一个接口。这是执行进程间通信(IPC)的最简单的方法,因为Message将所有请求队列到一个线程中,这样就不必将Service设计为线程安全的

2.2.3 使用AIDL

        Android Interface Definition Language (AIDL)将对象分解为操作系统可以理解的原语,并跨进程封送它们以执行IPC。Message类的底层实现实际上也是AIDL。

        Message在一个线程中创建一个包含所有客户端请求的队列,因此服务一次接收一个请求。但是,如果您希望您的服务同时处理多个请求,则可以直接使用AIDL。在这种情况下,服务必须是线程安全的,并且能够使用多线程。

        要直接使用AIDL,必须创建一个编程接口的.aidl文件。AndroidSDK工具使用这个文件生成一个抽象类,该类实现接口并处理IPC,然后可以在您的服务中继承它。

3 AIDL概述

        在开始设计AIDL接口之前,请注意,对AIDL接口的调用是直接的函数调用。不应该对调用发生的线程进行假设。本地进程中的线程和远程进程,在调用AIDL时会有所区别:

    • 从本地进程进行的调用将在进行调用的同一线程中执行。如果是主UI线程,该线程继续在AIDL接口中执行。如果是另一个线程,则是在服务中执行代码的线程。因此,如果只有本地线程访问服务,可以完全控制哪些线程在其中执行(如果是这样的话,那么根本不应该使用AIDL,而应该通过实现Binder来创建接口)
    • 来自远程进程的调用是从维护平台进程中的线程池发出。必须为来自同时发生多个、未知线程的传入调用做好准备。换一名话说,AIDL必须是线程安全的。从同一远程对象上的一个线程发出的调用按顺序到达接收端。
    • oneway关键字修改了远程调用的行为。当使用oneway时,远程调用不会阻塞,它只是发送事务数据并立即返回(异步的)。其行为与使用变通Binder调用行为一样。如果oneway与本地调用一起使用,则不会产生影响,调用仍然是同步的。

3.1 定义AIDL接口

        必须使用Java编程语言语法在.aidl文件中定义AIDL接口,然后将其保存在托管该服务的应用程序(服务端)和绑定到该服务的任何其他应用程序(客户端)的源代码(src/)目录中。即.aidl文件是成对出现于服务-客户端

        当构建每个包含.aidl文件的应用程序时,AndroidSDK工具会基于.aidl文件生成一个IBinder接口,并将其保存在项目的gen/目录中(.aidl文件变成.java文件)。服务端必须实现适当的IBinder接口,客户端应用程序可以从IBinder接口绑定到服务和调用方法,以执行IPC。

        要使用AIDL创建绑定服务,请执行以下步骤:

    • 创建.aidl文件
      • 这个文件定义具有方法签名的编程接口。
    • 实现接口
      • AndroidSDK工具基于您的.aidl文件,用Java编程语言生成一个接口。这个接口有一个名为Stub的内部抽象类,它继承了Binder,并实现了来自AIDL接口的方法。必须继承Stub类并实现这些方法。

        注意:Stub 还定义了一些辅助方法,最值得注意的是 asInterface()它接受一个 IBinder(通常是传递给客户端的 onServiceConnected() 回调方法的那个)并返回一个 Stub 接口的实例

    • 服务端向客户端暴露接口
      • 实现一个Service(继承Service类),并重写onBind()方法,返回Stub类的实现

        在发布第一个AIDL版本之后,对AIDL接口所做的任何更改都必须保持向后兼容,以避免破坏使用服务的其他应用程序。也就是说,因为新的.aidl版本必须复制到其他应用程序,以便它们访问您的服务接口,所以必须保持对原始接口的支持。

Android-Service组件_第2张图片

3.2 通过IPC传递对象

        在Android10(API级别29)中,可以直接在AIDL中定义Parcelable对象。也支持作为AIDL接口参数和其他Pacelable的类型。这样避免了手动编写编组代码和自定义类等额外工作。但是,这也将创建一个裸结构。

        如果需要自定义访问器或其他功能,则应实现Parcelable。

package android.graphics; 
// Declare Rect so AIDL can find it and knows that it implements 
// the parcelable protocol. 
parcelable Rect 
{ 
    int left; 
    int top; 
    int right; 
    int bottom;
}

        上面的代码示例自动生成一个带有left/top/right/bottom字段的Java类。所有相关的编组代码都是自动实现的,并且该对象可以直接使用,而无需添加任何实现

        还可以通过IPC接口将自定义类从一个进程发送到另一个进程。但是,必须确保类的代码对IPC通道的另一边是可用的,并且类必须支持Parcelable接口。支持Parcelable接口是至关重要的,因为它允许Android系统将对象分解为可以跨进程封送的原语

        要创建支持Parcelable协议的自定义类,必须执行以下操作:

      • 类实现Parcelable接口;
      • 实现writeToParcel()方法。它获取对象的当前状态,并将其写入一个Parcel;
      • 向类中添加一个名为“ CREATOR”的静态字段该字段是实现Parcelable.Creator接口的对象;
      • 最后,创建一个.aidl文件(如下所示);
package android.graphics; 
// Declare Rect so AIDL can find it and knows that it implements 
// the parcelable protocol. 
parcelable Rect;

        下面示例,表示 Rect 类如何实现 Parcelable 协议的示例:

import android.os.Parcel; 
import android.os.Parcelable; 
public final class Rect implements Parcelable 
{ 
    public int left; 
    public int top; 
    public int right; 
    public int bottom; 
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator()
    { 
        public Rect createFromParcel(Parcel in) {
            return new Rect(in); 
        } 
        public Rect[] newArray(int size) { 
            return new Rect[size]; 
        } 
    }; 
    public Rect() { } 
    private Rect(Parcel in) 
    {
        readFromParcel(in); 
    } 
    public void writeToParcel(Parcel out, int flags)
    { 
        out.writeInt(left); 
        out.writeInt(top);
        out.writeInt(right); 
        out.writeInt(bottom); 
    } 
    public void readFromParcel(Parcel in)
    { 
        left = in.readInt(); 
        top = in.readInt(); 
        right = in.readInt(); 
        bottom = in.readInt(); 
    } 
    public int describeContents() 
    { 
        return 0; 
    } 
}

一个AIDL示例:

// ILearningAIDL.aidl
package com.example.learningaidl;

// Declare any non-default types here with import statements

interface ILearningAIDL {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

        通过Android SDK,将ILearningAIDL.aidl文件变成ILearningAIDL.java文件

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.example.learningaidl;
// Declare any non-default types here with import statements

public interface ILearningAIDL extends android.os.IInterface
{
  /** Default implementation for ILearningAIDL. */
  public static class Default implements com.example.learningaidl.ILearningAIDL
  {
    /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
    @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                                     double aDouble, java.lang.String aString)
            throws android.os.RemoteException
    {
    }
    @Override
    public android.os.IBinder asBinder() {
      return null;
    }
  }
  /** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements
          com.example.learningaidl.ILearningAIDL
  {
    private static final java.lang.String DESCRIPTOR = "com.example.learningaidl.ILearningAIDL";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.example.learningaidl.ILearningAIDL interface,
     * generating a proxy if needed.
     */
    public static com.example.learningaidl.ILearningAIDL asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.learningaidl.ILearningAIDL))) {
        return ((com.example.learningaidl.ILearningAIDL)iin);
      }
      return new com.example.learningaidl.ILearningAIDL.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
      return this;
    }
    @Override public boolean onTransact(int code, android.os.Parcel data,
                                        android.os.Parcel reply, int flags)
            throws android.os.RemoteException
    {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_basicTypes:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          long _arg1;
          _arg1 = data.readLong();
          boolean _arg2;
          _arg2 = (0!=data.readInt());
          float _arg3;
          _arg3 = data.readFloat();
          double _arg4;
          _arg4 = data.readDouble();
          java.lang.String _arg5;
          _arg5 = data.readString();
          this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
          reply.writeNoException();
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.example.learningaidl.ILearningAIDL
    {
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote)
      {
        mRemote = remote;
      }
      @Override public android.os.IBinder asBinder()
      {
        return mRemote;
      }
      public java.lang.String getInterfaceDescriptor()
      {
        return DESCRIPTOR;
      }
      /**
           * Demonstrates some basic types that you can use as parameters
           * and return values in AIDL.
           */
      @Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                                       double aDouble, java.lang.String aString)
              throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeInt(anInt);
          _data.writeLong(aLong);
          _data.writeInt(((aBoolean)?(1):(0)));
          _data.writeFloat(aFloat);
          _data.writeDouble(aDouble);
          _data.writeString(aString);
          boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      public static com.example.learningaidl.ILearningAIDL sDefaultImpl;
    }
    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    public static boolean setDefaultImpl(com.example.learningaidl.ILearningAIDL impl) {
      // Only one user of this interface can use this function
      // at a time. This is a heuristic to detect if two different
      // users in the same process use this function.
      if (Stub.Proxy.sDefaultImpl != null) {
        throw new IllegalStateException("setDefaultImpl() called twice");
      }
      if (impl != null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.example.learningaidl.ILearningAIDL getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  /**
       * Demonstrates some basic types that you can use as parameters
       * and return values in AIDL.
       */
  public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble,
                         java.lang.String aString) throws android.os.RemoteException;
}

你可能感兴趣的:(深入理解AndroidⅠ和Ⅱ,android,service_mash)