Detailed explanation and use of binding service (binderService) in Android service (service)

foreword

The first two articles introduced ordinary background services and foreground services. These services have a common feature that there is no relationship between the components that start the service and the service. If you want to have a relationship between the two, you need to bind the two, which uses the binding service.


What is Binding Service

A bound service is an Android service that provides a client-server interface with which clients (such as Android activities) can interact. A bound service generally involves a client and a server, and a bound service is a server in a client-server interface. With bound services, components such as activities can bind to services, send requests, receive responses, and perform interprocess communication (IPC). Bound services are typically only active while serving other application components, and do not run indefinitely in the background. Simply put, a service that provides a client-server interface for clients to directly interact with the service is called a bound service. .

A bound service is the implementation of the Service class, allowing other applications to bind and interact with it. To provide a binding for a service, you must implement the onBind() callback method. This method returns an IBinder object that defines the programming interface that clients can use to interact with the service.

A bound service allows an application component to bind to it by calling bindService() , thus creating a long-lived connection. This service normally does not allow components to start it by calling startService() .

Create bound services when you need to interact with services in Activities and other application components, or if you need to expose some application functionality to other applications through interprocess communication (IPC).

Start service and bind service

The type of a service is generally single, such as started service and bound service, but it can also have multiple states. You can create a service with both started and bound states. In other words, you can start a service by calling startService() , let the service run indefinitely, and you can bind clients to the service by calling bindService() .

If you do allow a service to have both started and bound states, then instead of destroying the service after all clients have unbound from the service after the service starts, it must be up to you to call stopSelf() or stopService() Explicitly stop the service. That is, when there are two states at the same time, the service cannot be destroyed after the service is unbound.

Although you should usually implement onBind()the or onStartCommand(), sometimes you need to implement both methods. For example, a music player might find it useful to have its service run indefinitely while providing bindings. This way, the activity can start the service to play the music, and the music playback won't stop even if the user navigates away from the app. Then, when the user returns to the app, the activity can bind to the service and regain playback control.

Features of Bound Services

Clients bindService()bind . When invoked, it must provide an implementation ServiceConnectionof that monitors the connection to the service. The return value of bindService() indicates whether the requested service exists and whether the client is allowed to access it. When the Android system creates a connection between the client and the service, it calls ServiceConnection onServiceConnected(). The onServiceConnected() method includes a IBinder parameter that the client then uses to communicate with the bound service.

You can have multiple clients connected to a service at the same time. However, the system caches the IBinderservice communication channel. In other words, the system calls the service's onBind() method to generate the IBinder only when the first client binds the service . The system then passes that IBinder to all other clients bound to the same service without calling onBind() again .

When the last client unbinds from the service, the system destroys the service (unless it was also started via startService()).

In the process of implementing the binding service, the most important link is to define the interface returned by the onBind() callback method . The next section walks you through a few different ways you can define the IBinder interface for your service.

To create a bound service:

  • You define the interface to communicate with the service by implementing the onBind() callback method that returns the IBinder. Other application components can then retrieve this interface by calling bindService() and start calling service-related methods. A service is only available to the application components it is bound to, so if no components are bound to the service, the system destroys the service. You don't have to stop bound services in the same way as services started via onStartCommand().

  • You must define interfaces that specify how clients communicate with the service. This interface between the service and the client must be an implementation of IBinder, and the service must return this interface from the onBind() callback method. After receiving the IBinder, the client can start interacting with the service through this interface.

Multiple clients can bind to a service concurrently. After completing the interaction with the service, the client unbinds it by calling unbindService() . If there are no clients bound to the service, the system destroys the service.

There are several ways to implement a bound service, and this implementation is more complex than starting a service.


Binding service usage

Create a bound service

When creating a service that provides bindings, you must provide an IBinder that provides a programmatic interface for clients to interact with the service. You can define interfaces in three ways:

1. Extend the binder class

Service binding is performed by inheriting the form of Binder class, which is the most common situation. It is applicable to the communication between the service and the client in the application, and the service and the client run in the same process (common case), you should extend the Binder class and return an instance of that class from onBind() to create the interface. After the client receives Binder, it can use it to directly access the public methods provided by Binder implementation or Service.

Application Scenario

If your service is only used by local applications and does not need to work across processes, you can implement your own Binder class through which clients can directly access public methods in the service.

This should be preferred if the service is simply a background worker for your own application. The only time you wouldn't create an interface this way is if another application or a different process is hogging your service.

需要注意的是:

  • This only works if the client and service are in the same application and process (the most common case). For example, this works well for a music app that needs to bind the Activity to its own service that plays music in the background.
  • The service and client must be within the same application so that the client can cast the returned object and call its API correctly. The service and client must also be in the same process, as this method does not perform any cross-process marshalling.
Use Cases

Implementing a binding service requires the following development steps:

  1. Create a service class that inherits from Serviceand implements the lifecycle functions of the Service class.
  2. In your service, create a Binder instance that does one of the following:
  • 包含客户端可调用的公共方法。
    
  • 返回当前的 Service 实例,该实例中包含客户端可调用的公共方法。这里不绝对,也可以直接定义方法,不做返回,直接在onBind() 中返回Binder实例也可。
    
  • 返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法。
    
  1. Return this Binder instance from onBind()the callback method.
  2. In the client, receive from onServiceConnected()the callback method Binderand invoke the bound service with the provided method.

按照上述描述的步骤,首先创建一个服务类

package com.cop.ronghw.study_android_exact;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

/**
 * 创建一个绑定服务
 */
public class MyBinderService extends Service {
    
    
    public MyBinderService() {
    
    
    }

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

    @Override
    public void onCreate() {
    
    
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    
    
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
    
    
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
    
    
        return super.onUnbind(intent);
    }
}

其次创建Binder实例,做为绑定器供客户端使用,并提供客户端调用的公共方法:

/**
 * 创建一个绑定服务
 */
public class MyBinderService extends Service {
    
    
    public MyBinderService() {
    
    
    }

    //给客户端使用的binder
    private final IBinder iBinder =  new MyServiceBinder();
    /**
     * 用于客户端绑定器的类。因为我们知道这个服务总是运行在与其客户机相同的进程中,
     * 所以我们不需要处理IPC。
     */
    public class MyServiceBinder extends Binder{
    
    
          MyBinderService getService(){
    
    
              //返回MyBinderService实例,以便客户端可以调用公共方法
             return MyBinderService.this;
          }
        }

    @Override
    public IBinder onBind(Intent intent) {
    
    
        Log.i("MyBinderService","执行onBind方法");
        return iBinder;
    }

    //服务提供的公共方法,供客户端调用
    public String getFileString(){
    
    
       return "file获取成功";
    }
    // 文件上传
    public void uplodFile(){
    
    
        Log.i("MyBinderService","uplodFile-文件上传");
    }

    @Override
    public void onCreate() {
    
    
        Log.i("MyBinderService","执行onCreate方法");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    
    
        Log.i("MyBinderService","执行onStartCommand方法");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
    
    
        Log.i("MyBinderService","执行onDestroy方法");
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
    
    
        Log.i("MyBinderService","执行onUnbind方法");
        return super.onUnbind(intent);
    }

}

MyServiceBinder provides clients with a getService() method for retrieving the current instance of MyBinderService. This way, clients can call public methods in the service. For example, the client can call the two public methods getFileString() and uplodFile() in the service.

接着在客户端添加测试按钮,并绑定服务:

public class MyBinderActivity extends AppCompatActivity{
    
    

    MyBinderService myBinderService;

   //定义服务绑定的回调,传递给bindService()
    private ServiceConnection connection = new ServiceConnection() {
    
    
        @Override
        public void onServiceConnected(ComponentName className,IBinder service) {
    
    
            //我们已经绑定到MyBinderService,向下转型转换IBinder为MyBinderService.MyServiceBinder并获得MyBinderService实例
           MyBinderService.MyServiceBinder binder = (MyBinderService.MyServiceBinder) service;
           myBinderService = binder.getService();

        }
        @Override
        public void onServiceDisconnected(ComponentName arg0) {
    
    

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mybinder);
    }

    // 活动隐藏挂起后解绑服务
    @Override
    protected void onStop() {
    
    
        super.onStop();
        //解绑服务
        unbindService(connection);
    }

    // 在这里我们分别在活动OnStart这里及界面刚刚加载时就给此活动绑定好服务
    @Override
    protected void onStart() {
    
    
        super.onStart();
        // activity加载后就进行服务绑定
        Intent intent = new Intent(this, MyBinderService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    //测试点击事件
    public void onButtonclick(View v){
    
    
        String fileString  = myBinderService.getFileString();
        Log.i("MyBinderActivity","文件内容为=="+fileString);
        myBinderService.uplodFile();
    }
}

In the above code: After MyBinderActivity starts and is visible, it will automatically call the bindService binding service, connect to the application service through this method, and create it when needed. This defines dependencies between applications and services. The bindService method provides three parameters:

  • service intent : Identifies the service to connect to. Intent must specify an explicit component name.
  • serviceConnection : Receives information when the service starts and stops. This must be a valid ServiceConnection object, it cannot be null. It is similar to a communication pipeline, and the connection between the client and the server is established through the ServiceConnection instance object.
  • flags : Various flags are provided here. In this example, BIND_AUTO_CREATE is used, which means that as long as the binding exists, the service will be created automatically. The third parameter is a flag indicating binding options. To create a service that is not already active, this parameter should normally be BIND_AUTO_CREATE. Other possible values ​​are BIND_DEBUG_UNBIND and BIND_NOT_FOREGROUND, or 0 (for no parameter).

The parameter serviceConnection instance object is created in MyBinderActivity, and two methods are rewritten in this internal class: onServiceDisconnected() and onServiceConnected (ComponentName name, IBinder service) These two methods are respectively used to establish a connection between the client and the server Called after failure and successful connection establishment. where onServiceDisconnected()the method is called when the connection to the service is lost. This usually happens when the process hosting the service crashes or is killed. This does not remove the ServiceConnection itself - this binding to the service will remain active and you will receive a call to onserviceconnected(ComponentName, IBinder) when the service is next run. In the onServiceConnected method, the service instance is obtained through downward transformation, and the service instance will be used in subsequent methods to use the public methods defined in the service.
It should be noted that both methods onServiceDisconnected() and onServiceConnected (ComponentName name, IBinder service) are triggered after the bindservice() method is called.

Let's take a look at the execution log: When entering MyBinderActivity, the onStart method of the activity will be executed. In the onStart method, bindService(intent, connection, Context.BIND_AUTO_CREATE) will be called to bind the service. At this time, the oncreat method of the service will be called And the onbind method is as follows:
insert image description here
At this time, if the binding is successful, the MyBinderService instance will be returned in MyBinderActivity. At this point, the method in MyBinderService can be called in the activity. This allows the client to establish a connection with the server.

The return value of the bindService method is a Boolean value, which is True if the system is starting a service that the client has permission to bind to; if the system cannot find the service, or if your client does not have permission to bind the service, is False. Regardless of the return value, you should call unbindService(ServiceConnection) later to release the connection.

Summarize

我们再把上述步骤总结一下:

Application components (clients) can bind to a service by calling bindService() . The Android system then calls the service's onBind() method, which returns an IBinder for interacting with the service .

Binding is an asynchronous operation, and bindService() returns immediately without returning an IBinder to the client. To receive an IBinder , the client must create a ServiceConnection instance and pass it to bindService() . ServiceConnection contains a callback method that is called by the system passing the IBinder .

Note: Only Activities, Services, and Content Providers can bind to Services, you cannot bind to Services from Broadcast Receivers.

如要从您的客户端绑定到服务,请按以下步骤操作:

  1. Implement ServiceConnection.
    Your implementation must replace two callback methods:
  • The onServiceConnected()
    system calls this method to pass the IBinder returned by the service's onBind() method.
  • onServiceDisconnected()
    This method is called by the Android system when the connection to the service is unexpectedly interrupted, such as when the service crashes or is terminated. The system does not call this method when the client unbinds.
  1. Call bindService(), passing the ServiceConnection implementation.

Note: If this method returns false, your client does not have a valid connection to the service. However, your client should still call unbindService(); otherwise, your client will prevent the service from shutting down when idle.

  1. When the system calls the onServiceConnected() callback method, you can use the methods defined by the interface to start calling the service.
  2. To disconnect from the service, call unbindService().
    When the app destroys the client, if the client is still bound to the service, the destruction causes the client to unbind. A better approach is to unbind the client immediately after the client interacts with the service. Doing so shuts down idle services.

以下是一些有关绑定到服务的重要说明

  • You should always catch the DeadObjectException, which is thrown when the connection is broken. This is the only exception thrown by remote methods.
  • Objects are references counted across processes.
  • You typically pair bind and unbind during the bring-up and tear-down moments of the matching client lifecycle, as the following example shows:
    • If you only need to interact with the service while the Activity is visible, you should bind during onStart() and unbind during onStop().
    • If you want your Activity to receive responses even when it's stopped in the background, you can bind during onCreate() and unbind during onDestroy(). Note that this means that your Activity needs to use the service the entire time it is running (even in the background), so if the service is in another process, when you increase the weight of that process, the system kills the process's The odds will increase.

Note: In general, you should not bind and unbind during the Activity's onResume() and onPause(), because these callbacks occur every time you switch lifecycle states, you should keep the processing during these transitions in the minimum level. Also, if multiple Activities within your app bind to the same service, and a transition occurs between two of the Activities, then if the current Activity unbinds first (during a pause), the next Activity binds again ( recovery), the system may destroy and re-create the service.

The lifecycle of a bound service

When a service is unbound from all clients, the Android system destroys the service (unless it was also started with a startService() call). So if your service is a purely bound service, you don't need to manage its lifecycle, the Android system manages it for you depending on whether it is bound to any client or not.

However, if you choose to implement the onStartCommand() callback method, you must explicitly stop the service because the system now considers the service to be started. In this case, the service will run until it stops itself via stopSelf(), or another component calls stopService() (regardless of whether the service is bound to any clients).

Also, if your service is up and accepting binds, when the system calls your onUnbind() method, you can optionally return true if you want to receive an onRebind() call the next time the client binds to the service. onRebind() returns null, but the client still receives the IBinder in its onServiceConnected() callback. The diagram below illustrates the logic of this lifecycle.
insert image description here

Guess you like

Origin blog.csdn.net/superzhang6666/article/details/129527741