Android Service

Android Service

Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。

此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。

服务的类型

android中有三种不同的服务类型

  • 前台服务
    前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。

    前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。

  • 后台服务
    后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。

    注意:在API 级别 26 或更高版本,当应用本身未在前台运行时,系统会对运行后台服务施加限。在诸如此类的大多数情况下,应改为使用计划作业

  • 绑定服务
    当应用组件通过调用 bindService() 绑定到服务时,服务即处于绑定状态。绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

服务的生命周期

创建服务,必须创建 Service 的子类(或使用它的一个现有子类)。在实现中,必须重写一些回调方法,从而处理服务生命周期的某些关键方面,并提供一种机制将组件绑定到服务(如适用)。重要的回调方法:

  • onStartCommand()

    当另一个组件(如 Activity)请求启动服务时,系统会通过调用 startService() 来调用此方法。执行此方法时,服务即会启动并可在后台无限期运行。如果您实现此方法,则在服务工作完成后,您需负责通过调用 stopSelf()stopService() 来停止服务。(如果您只想提供绑定,则无需实现此方法。)

  • onBind()

    当另一个组件想要与服务绑定(例如执行 RPC)时,系统会通过调用 bindService() 来调用此方法。在此方法的实现中,您必须通过返回 IBinder 提供一个接口,以供客户端用来与服务进行通信。请务必实现此方法;但是,如果您并不希望允许绑定,则应返回 null。

  • onCreate()

    首次创建服务时,系统会(在调用 onStartCommand()onBind() 之前)调用此方法来执行一次性设置程序。如果服务已在运行,则不会调用此方法。

  • onDestroy()

    当不再使用服务且准备将其销毁时,系统会调用此方法。服务应通过实现此方法来清理任何资源,如线程、注册的侦听器、接收器等。这是服务接收的最后一个调用。

如果组件通过调用 startService() 启动服务(这会引起对 onStartCommand() 的调用),则服务会一直运行,直到其使用 stopSelf() 自行停止运行,或由其他组件通过调用 stopService() 将其停止为止。

如果组件通过调用 bindService() 来创建服务,且调用 onStartCommand(),则服务只会在该组件与其绑定时运行。当该服务与其所有组件取消绑定后,系统便会将其销毁。

Service的生命周期有两种。一种是通过startService启动, 另一种是通过bindService启动。无论哪种启动模式onCreate方法只会执行一次。

通过startService创建服务
.
.
stopService
startService
onCreate
onStartCommand
onDestroy
通过bindServcie创建服务
.
.
unbindService
no bind client
bindService
onCreate
onBind
onUnbind
onDestroy
生命周期startServicebindService
onCreat()会执行会执行
onStartCommand()会执行
onBind()会执行
onUnbind()会执行
onRebind()会执行
onDestroy()会执行会执行

生命周期的回调

public class ExampleService extends Service {
    int startMode;       // indicates how to behave if the service is killed
    IBinder binder;      // interface for clients that bind
    boolean allowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return mAllowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

服务的运行

服务在其托管进程的主线程中运行,它既创建自己的线程,也在单独的进程中运行(除非另行指定)

如果服务将执行任何 CPU 密集型工作或阻止性操作(例如 MP3 播放或联网),则应通过在服务内创建新线程来完成这项工作。

通过使用单独的线程,您可以降低发生“应用无响应”(ANR) 错误的风险,而应用的主线程仍可继续专注于运行用户与 Activity 之间的交互。

Service默认运行在主线程

服务的Android Manifest文件配置

像Activity一样,Service服务必须在 Android Manifest文件中进行配置

声明服务,需要添加 <service>元素作为<application>元素的子元素。

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

service 标签配置语法

<service android:description="string resource"
         android:directBootAware=["true" | "false"]
         android:enabled=["true" | "false"]
         android:exported=["true" | "false"]
         android:foregroundServiceType=["camera" | "connectedDevice" |
                                        "dataSync" | "location" | "mediaPlayback" |
                                        "mediaProjection" | "microphone" | "phoneCall"]
         android:icon="drawable resource"
         android:isolatedProcess=["true" | "false"]
         android:label="string resource"
         android:name="string"
         android:permission="string"
         android:process="string" >
    . . .
</service>
属性描述
android:description向用户描述服务的字符串。
android:directBootAware是否可以在用户解锁设备之前运行。默认值为 "false"
android:enabled系统是否可以实例化服务。默认值为“true
android:exported其他应用的组件是否能调用服务或与之交互。默认值取决于服务是否包含 Intent 过滤器。
android:foregroundServiceType阐明服务是满足特定用例要求的前台服务。
android:icon服务的图标。
android:isolatedProcess如果设置为 true,则此服务将在与系统其余部分隔离的特殊进程下运行。此服务自身没有权限,只能通过 Service API 与其进行通信(绑定和启动)。
android:label可向用户显示的服务名称。
android:name实现服务的 Service 子类的名称。
android:permission实体启动服务或绑定到服务所必需的权限的名称。
android:process将运行服务的进程的名称。
  • android:process 属性
    通常,应用的所有组件都会在为应用创建的默认进程中运行。它与应用软件包的名称相同。<application>元素的process 属性可以为所有组件设置不同的默认值。不过,组件可以使用其自己的 process 属性替换默认属性,从而允许您跨多个进程分布应用。
    如果为该属性分配的名称以冒号(“:”)开头,则系统会在需要时创建应用专用的新进程,并且服务会在该进程中运行。如果进程名称以小写字符开头,则服务将在采用该名称的全局进程中运行,前提是它具有相应权限。这样,不同应用中的组件就可以共享进程,从而减少资源使用量。

  • android:exported
    Android 12开始,android:exported属性必须显示的的指定

继承Service

Android中可以使用两个类来实现服务,ServiceIntentService都可以

Service

这是适用于所有服务的基类。扩展此类时,您必须创建用于执行所有服务工作的新线程,因为服务默认使用应用的主线程,这会降低应用正在运行的任何 Activity 的性能。

要求服务执行多线程(而非通过工作队列处理启动请求),则可通过扩展 Service 类来处理每个 Intent。

public class HelloService extends Service {
    private Looper serviceLooper;
    private ServiceHandler serviceHandler;

    // Handler that receives messages from the thread
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            // Normally we would do some work here, like download a file.
            // For our sample, we just sleep for 5 seconds.
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                // Restore interrupt status.
                Thread.currentThread().interrupt();
            }
            // Stop the service using the startId, so that we don't stop
            // the service in the middle of handling another job
            stopSelf(msg.arg1);
        }
    }

    @Override
    public void onCreate() {
        // Start up the thread running the service. Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block. We also make it
        // background priority so CPU-intensive work doesn't disrupt our UI.
        HandlerThread thread = new HandlerThread("ServiceStartArguments",
                                                 Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        // Get the HandlerThread's Looper and use it for our Handler
        serviceLooper = thread.getLooper();
        serviceHandler = new ServiceHandler(serviceLooper);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        Message msg = serviceHandler.obtainMessage();
        msg.arg1 = startId;
        serviceHandler.sendMessage(msg);

        // If we get killed, after returning from here, restart
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // We don't provide binding, so return null
        return null;
    }

    @Override
    public void onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
    }
}

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

  • START_NOT_STICKY

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

  • START_STICKY

    如果系统在 onStartCommand() 返回后终止服务,则其会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务,否则系统会调用包含空 Intent 的 onStartCommand()。在此情况下,系统会传递这些 Intent。此常量适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。

  • START_REDELIVER_INTENT

    如果系统在 onStartCommand() 返回后终止服务,则其会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。所有挂起 Intent 均依次传递。此常量适用于主动执行应立即恢复的作业(例如下载文件)的服务。

IntentService

这是 Service 的子类,其使用工作线程逐一处理所有启动请求。如果您不要求服务同时处理多个请求,此类为最佳选择。实现 onHandleIntent(),该方法会接收每个启动请求的 Intent,以便您执行后台工作。

由于大多数启动服务无需同时处理多个请求(实际上,这种多线程情况可能很危险),因此最佳选择是利用 IntentService 类实现服务

IntentService 类会执行以下操作:

  • 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。
  • 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。
  • 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()
  • 提供 onBind() 的默认实现(返回 null)。
  • 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。

如要完成客户端提供的工作,请实现 onHandleIntent()。不过,您还需为服务提供小型构造函数。

public class HelloIntentService extends IntentService {

    /**
   * A constructor is required, and must call the super <code><a href="/reference/android/app/IntentService.html#IntentService(java.lang.String)">IntentService(String)</a></code>
   * constructor with a name for the worker thread.
   */
    public HelloIntentService() {
        super("HelloIntentService");
    }

    /**
   * The IntentService calls this method from the default worker thread with
   * the intent that started the service. When this method returns, IntentService
   * stops the service, as appropriate.
   */
    @Override
    protected void onHandleIntent(Intent intent) {
        // Normally we would do some work here, like download a file.
        // For our sample, we just sleep for 5 seconds.
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // Restore interrupt status.
            Thread.currentThread().interrupt();
        }
    }
}

优点:需要一个构造函数和一个 onHandleIntent() 实现即可。

控制服务

启动服务

可以通过将 Intent 传递给 startService()startForegroundService(),从 Activity 或其他应用组件启动服务。Android 系统会调用服务的 onStartCommand() 方法,并向其传递 Intent,从而指定要启动的服务。

注意:如果应用面向 API 级别 26 或更高版本,除非应用本身在前台运行,否则系统不会对使用或创建后台服务施加限制。如果应用需要创建前台服务,则其应调用 startForegroundService()。此方法会创建后台服务,但它会向系统发出信号,表明服务会将自行提升至前台。创建服务后,该服务必须在五秒内调用自己的 startForeground() 方法。

Activity 可以结合使用显式 Intent 与 startService(),从而启动服务

Intent intent = new Intent(this, HelloService.class);
startService(intent);

startService() 方法会立即返回,并且 Android 系统会调用服务的 onStartCommand() 方法。如果服务尚未运行,则系统首先会调用 onCreate(),然后调用 onStartCommand()

如果服务亦未提供绑定,则应用组件与服务间的唯一通信模式便是使用 startService() 传递的 Intent。但是,如果您希望服务返回结果,则启动服务的客户端可以为广播(通过 getBroadcast() 获得)创建一个 PendingIntent,并将其传递给启动服务的 Intent 中的服务。然后,服务便可使用广播传递结果。

多个服务启动请求会导致多次对服务的 onStartCommand() 进行相应的调用。但是,如要停止服务,只需一个服务停止请求(使用 stopSelf()stopService())即可。

停止服务

启动服务必须管理自己的生命周期。换言之,除非必须回收内存资源,否则系统不会停止或销毁服务,并且服务在 onStartCommand() 返回后仍会继续运行。服务必须通过调用 stopSelf() 自行停止运行,或由另一个组件通过调用 stopService() 来停止它。

一旦请求使用 stopSelf()stopService() 来停止服务,系统便会尽快销毁服务。

如果服务同时处理多个对 onStartCommand() 的请求,则您不应在处理完一个启动请求之后停止服务,因为您可能已收到新的启动请求(在第一个请求结束时停止服务会终止第二个请求)。为避免此问题,您可以使用 stopSelf(int) 确保服务停止请求始终基于最近的启动请求。换言之,在调用 stopSelf(int) 时,您需传递与停止请求 ID 相对应的启动请求 ID(传递给 onStartCommand()startId)。此外,如果服务在您能够调用 stopSelf(int) 之前收到新启动请求,则 ID 不匹配,服务也不会停止。

绑定服务

绑定服务允许应用组件通过调用 bindService() 与其绑定,从而创建长期连接。此服务通常不允许组件通过调用 startService()启动它。

如需与 Activity 和其他应用组件中的服务进行交互,或需要通过进程间通信 (IPC) 向其他应用公开某些应用功能,则应创建绑定服务。

如要创建绑定服务,您需通过实现 onBind() 回调方法返回 IBinder,从而定义与服务进行通信的接口。然后,其他应用组件可通过调用 bindService() 来检索该接口,并开始调用与服务相关的方法。服务只用于与其绑定的应用组件,因此若没有组件与该服务绑定,则系统会销毁该服务。您不必像通过 onStartCommand() 启动的服务那样,以相同方式停止绑定服务。

如要创建绑定服务,您必须定义指定客户端如何与服务进行通信的接口。服务与客户端之间的这个接口必须是 IBinder 的实现,并且服务必须从 onBind() 回调方法返回该接口。收到 IBinder 后,客户端便可开始通过该接口与服务进行交互。

多个客户端可以同时绑定到服务。完成与服务的交互后,客户端会通过调用 unbindService() 来取消绑定。如果没有绑定到服务的客户端,则系统会销毁该服务。

启动前台服务
Context context = getApplicationContext();
Intent intent = new Intent(...); // Build the intent for the service
context.startForegroundService(intent);

如要请求让服务在前台运行,请调用 startForeground()。此方法采用两个参数:唯一标识通知的整型数和用于状态栏的 Notification。此通知必须拥有 PRIORITY_LOW 或更高的优先级。

在服务内部,通常在onStartCommand()中,可以请求在前台运行服务。

Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent =
    PendingIntent.getActivity(this, 0, notificationIntent, 0);

Notification notification =
    new Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)
    .setContentTitle(getText(R.string.notification_title))
    .setContentText(getText(R.string.notification_message))
    .setSmallIcon(R.drawable.icon)
    .setContentIntent(pendingIntent)
    .setTicker(getText(R.string.ticker_text))
    .build();

startForeground(ONGOING_NOTIFICATION_ID, notification);

如要从前台移除服务,请调用 stopForeground()。此方法采用布尔值,指示是否需同时移除状态栏通知。此方法不会停止服务。但是,如果您在服务仍运行于前台时将其停止,则通知也会随之移除。

**注意:**如果应用面向 Android 9(API 级别 28)或更高版本并使用前台服务,则其必须请求 FOREGROUND_SERVICE 权限。这是一种普通权限,因此,系统会自动为请求权限的应用授予此权限。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

    <application ...>
        ...
    </application>
</manifest>

如果面向 API 级别 28 或更高版本的应用试图创建前台服务但未请求 FOREGROUND_SERVICE,则系统会抛出 SecurityException

服务的ANR

Service的在一定时间内没有执行完操作,则会发生ANR(Android Not Response),不同类型的Service,其ANR时间是不同的。

  • 普通服务 20s

  • 前台服务 10s

  • 后台服务 200s

// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;

// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

// How long the startForegroundService() grace period is to get around to
// calling startForeground() before we ANR + stop it.
static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;

绑定服务onBind的实现

绑定服务分为两种情况,一种是绑定到已启动的服务,另一种是绑定新创建服务。

如果绑定已经启动的服务,那干不会在所有客户端与服务取消绑定后销毁服务,必须通过调用 stopSelf()stopService() 显式停止服务。

客户端通过调用 bindService() 绑定到服务。调用时,它必须提供 ServiceConnection 的实现,后者会监控与服务的连接。bindService() 的返回值指示所请求的服务是否存在,以及是否允许客户端访问该服务。Android 系统创建客户端与服务之间的连接时,会对 ServiceConnection 调用 onServiceConnected()onServiceConnected() 方法包含一个 IBinder 参数,客户端随后会使用该参数与绑定服务通信。

可以将多个客户端同时连接到某项服务。但是,系统会缓存 IBinder 服务通信通道。换言之,只有在第一个客户端绑定服务时,系统才会调用服务的 onBind() 方法来生成 IBinder。然后,系统会将该 IBinder 传递至绑定到同一服务的所有其他客户端,无需再次调用 onBind()

当最后一个客户端取消与服务的绑定时,系统会销毁该服务(除非还通过 startService() 启动了该服务)。

只有在onBind()方法中不返回null对象,客户端才能收到onServiceConnected回调。

注意:只有 Activity、服务和内容提供程序可以绑定到服务,您无法从广播接收器绑定到服务。

可以通过三种方式实现onBind方法

扩展 Binder 类

如果服务是供自有应用专用,并且在与客户端相同的进程中运行(常见情况),应当通过扩展 Binder 类并从 onBind() 返回该类的实例来创建接口。客户端收到 Binder 后,可利用它直接访问 Binder 实现或 Service 中提供的公共方法。

如果服务只是自有应用的后台工作器,应优先采用这种方式。不使用这种方式创建接口的唯一一种情况是:其他应用或不同进程占用了服务。

注意:只有当客户端和服务处于同一应用和进程内(最常见的情况)时,此方式才有效。

服务和客户端必须在同一应用内,这样客户端才能转换返回的对象并正确调用其 API。服务和客户端还必须在同一进程内,因为此方式不执行任何跨进程编组。

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder binder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();

    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService so clients can call public methods
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    /** method for clients */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

LocalBinder 为客户端提供 getService() 方法,用于检索 LocalService 的当前实例。这样,客户端便可调用服务中的公共方法。例如,客户端可调用服务中的 getRandomNumber()

使用 Messenger

如需让接口跨不同进程工作,可以使用 Messenger 为服务创建接口。采用这种方式时,服务会定义一个 Handler,用于响应不同类型的 Message 对象。此 HandlerMessenger 的基础,后者随后可与客户端分享一个 IBinder,以便客户端能利用 Message 对象向服务发送命令。此外,客户端还可定义一个自有 Messenger,以便服务回传消息。

这是执行进程间通信 (IPC) 最为简单的方式,因为 Messenger 会在单个线程中创建包含所有请求的队列,这样就不必对服务进行线程安全设计。借助此方式,无需使用 AIDL 便可执行进程间通信 (IPC)。

public class MessengerService extends Service {
    /**
     * Command to the service to display a message
     */
    static final int MSG_SAY_HELLO = 1;

    /**
     * Handler of incoming messages from clients.
     */
    static class IncomingHandler extends Handler {
        private Context applicationContext;

        IncomingHandler(Context context) {
            applicationContext = context.getApplicationContext();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    Messenger mMessenger;

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        mMessenger = new Messenger(new IncomingHandler(this));
        return mMessenger.getBinder();
    }
}

为接口使用 Messenger 比使用 AIDL 更简单,因为 Messenger 会将所有服务调用加入队列。纯 AIDL 接口会同时向服务发送多个请求,那么服务就必须执行多线程处理。

对于大多数应用,服务无需执行多线程处理,因此使用 Messenger 可让服务一次处理一个调用。如果您的服务必须执行多线程处理,请使用 AIDL来定义接口。

使用 AIDL

Android 接口定义语言 (AIDL) 会将对象分解成原语,操作系统可通过识别这些原语并将其编组到各进程中来执行 IPC。以前采用 Messenger 的方式实际上是以 AIDL 作为其底层结构。如上所述,Messenger 会在单个线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。

不过,如果想让服务同时处理多个请求,可以直接使用 AIDL。在此情况下,您的服务必须达到线程安全的要求,并且能够进行多线程处理。

如需直接使用 AIDL,必须创建用于定义编程接口的 .aidl 文件。Android SDK 工具会利用该文件生成实现接口和处理 IPC 的抽象类,随后可在服务内对该类进行扩展。

注意:大多数应用不应使用 AIDL 来创建绑定服务,因为它可能需要多线程处理能力,并可能导致更为复杂的实现。因此,AIDL 并不适合大多数应用。

ServiceConnection

应用组件(客户端)可通过调用 bindService() 绑定到服务。然后,Android 系统会调用服务的 onBind() 方法,该方法会返回用于与服务交互的 IBinder

绑定是异步操作,并且 bindService() 可立即返回,无需将 IBinder 返回给客户端。如要接收 IBinder,客户端必须创建一个 ServiceConnection 实例,并将其传递给 bindService()ServiceConnection 包含一个回调方法,系统通过调用该回调方法来传递 IBinder

只有 Activity、服务和内容提供程序可以绑定到服务,无法从广播接收器绑定到服务。

如果bindService()方法返回 false,说明客户端与服务之间并无有效连接。不过,客户端仍应调用 unbindService();否则,客户端会使服务无法在空闲时关闭。

LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "onServiceDisconnected");
        mBound = false;
    }
};

Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);

如果使用 intent 绑定到 Service,请使用显式intent 来确保应用的安全性。使用隐式 intent 启动服务存在安全隐患,因为无法确定哪些服务将响应 intent,且用户无法看到哪些服务已启动。从 Android 5.0(API 级别 21)开始,如果使用隐式 intent 调用 bindService(),系统会抛出异常。

DeadObjectException

应该始终捕获 DeadObjectException 异常,系统会在连接中断时抛出此异常。这是远程方法抛出的唯一异常。

try {
 ...
} catch (DeadObjectException e) {
 ...
} finally {
 ...
}

日志分析

服务默认运行在主线程
//服务在同一进程
16:09:01.001 6045-6045/? D/NullBinderService: onCreate: 
16:09:01.001 6045-6045/? D/NullBinderService: onBind:

//服务在不同进程
16:09:25.804 6077-6077/? D/NullBinderServiceRemote: onCreate: 
16:09:25.804 6077-6077/? D/NullBinderServiceRemote: onBind: 
onBind返回null客户端onServiceConnect不会回调
//onBind 返回null, 客户端收不到回调
6045-6045/? D/NullBinderService: onCreate: 
6045-6045/? D/NullBinderService: onBind:

//onBind 返回不为null, 客户端能收到onServiceConnect不会回调
6045-6045/? D/ExtendBinderService: onCreate: 
6045-6045/? D/ExtendBinderService: onBind: 
6045-6045/? D/MainActivity: onServiceConnected: ComponentInfo{com.android/com.android.ExtendBinderService} iBinder com.android.ExtendBinderService$MyService@22aeb6b
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值