Service is familiar to Android developers. As one of the four components of Android, Service also plays an important role in our development. In Android interviews, Service is often asked by interviewers. What exactly is a Service? A Service is an application component that runs operations in the background for a long time without providing a user interface. It can be started by another component and continues to run in the background even if the user switches to another application. Yes, this is the concept of Service, and as an Android developer, you’re more or less familiar with it, but not everyone knows everything. Some time ago, the project used Service, so this article summarizes the use of Service.

The Service directory. PNG

Service

Service and Activity are one of the four components of Android, and they have their own life cycle. To master the use of Service, it is necessary to understand the methods in the life cycle of Service, and the timing and function of each method callback in the life cycle. It is important to note that a Service can be started in two ways, and the life cycle of the two ways is different. Let’s look at the lifecycle approaches for each startup.

StartService Starts the Service in startService mode

When an application component starts a Service using the startService method, the Service is in the started state. Once the Service is started, it runs in the background indefinitely, its life cycle independent of the component that started it, even if the component that started it is destroyed. Since the started service runs in the background for a long time, which will consume a lot of battery of the phone, we should call stopSelf() to stop the service after the task is completed, or call stopService through other application components to stop the service.

StartService After starting the service, the following life cycle is performed: onCreate() -> onStartCommand() -> onStart()(now deprecated) -> onDestroy(). Take a look at some of its lifecycle methods:

  • OnCreate () : this method is called when the service is first started. Before onStartCommand and onBind, if the service is already started, this method is not called when the service is started again, so you can do some initialization in onCreate. For example, to perform time-consuming operations, you can create a thread here, and to play music, you can initialize the music player here.

  • onStartCommand(): When the startService method is used to start the service, the onCreate method is called and the service is started and will run in the background indefinitely until the service is stopped by stopService or stopSelf.

  • OnDestroy (): This method is called when a service is no longer needed and is about to be destroyed. The service should implement this method to clean up all resources such as threads, registered listeners, sinks, and so on. This is the last call received by the service.

With these lifecycle methods in mind, let’s write a simple Service.

To use a Service, you need to inherit the Service class (or IntentService, as Howe explains) as follows:

public class SimpleService extends Service {
    public static final String TAG = "SimpleService";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG,"call onBind...");
        return null;
    }

    @Override
    public void onCreate(a) {
        Log.i(TAG,"call onCreate...");
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Log.i(TAG,"call onStart...");
    }

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

    @Override
    public void onDestroy(a) {
        Log.i(TAG,"call onDestroy..."); }}Copy the code

After the Service class is written, we need to register it in the manifest file under the Application tag:

        <service android:name=".service.SimpleService"
                 android:exported="false"
            />Copy the code

After the Service is written and registered in the manifest file, we can start the Service. Launching a Service is similar to launching an Activity with the Intent as follows:

  @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.start_service:
                Intent intent = new Intent(this,SimpleService.class);
                // Start the service
                startService(intent);
                break;
            case R.id.stop_service:
                Intent service = new Intent(this,SimpleService.class);
                // Stop the service
                stopService(service);
                break; }}Copy the code

Interface. The PNG

As shown in the interface above, there are two buttons, namely startService and StopService. Click the button of startService and StopService respectively to see the log printed by the life cycle callback method:

PNG indicates the life cycle log file

Summary: If a service is started by startService, it will run in the background indefinitely until the service is terminated by stopService or stopSelf. The service is independent of the component that started it, that is, when the component starts the service, the component and the service are irrelevant, even if the component that started it is destroyed, the service still runs in the background. Services started this way do not communicate well with components.

Start the service using bindService

In addition to startService, another way to start a service is through the bindService method, known as the binding service, which, as its name suggests, binds the starting component to the service. The service started by startService mentioned above is independent of the component. Even if the component that started the service is destroyed, the service still runs in the background without interference. BindSerivce bindings are different, however, and are related to the life cycle of the bound component. As follows:

Multiple components can be bound to the same service, and if only one component is bound to the service, the service will stop when the bound component is destroyed. If multiple components are bound to a service, the service stops when all components bound to the service are destroyed.

The life cycle of bindService is different from that of startService. The life cycle of bindServie is as follows: onCreate -> onBind -> onUnbind ->onDestroy. One of the important ones is onBind and the onUnbind method.

  • onBind(): In the implementation, you must return an IBinder interface for the client to communicate with the Service. You must implement this method. This method is an abstract method of the Service, but if you don’t allow binding, Return NULL.

  • OnUnbind (): This method is called when all components bound to the service are unbound.

With these two approaches in mind, let’s look at how to bind a service. Bind(); bind (); IBinder (); bind ();

public class SimpleService extends Service {
    public static final String TAG = "SimpleService";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG,"call onBind...");
        // Return the IBinder interface object
        return new MyBinder();
    }

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

    @Override
    public void onCreate(a) {
        Log.i(TAG,"call onCreate...");
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Log.i(TAG,"call onStart...");
    }

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

    @Override
    public void onDestroy(a) {
        Log.i(TAG,"call onDestroy...");
    }

    // Add a class that inherits Binder
    public  class MyBinder extends Binder{
        // Add methods to interact with the outside world
        public String  getStringInfo(a){
          return "Called a method in the service"; }}}Copy the code

2. When binding the service, provide a ServiceConnection interface and obtain Binder objects in the interface callback to communicate with the service.

 private SimpleService.MyBinder mMyBinder;
    // Bind/unbind the Service callback interface
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // Callback after successful binding
            //1, obtain the Binder interface object
            mMyBinder = (SimpleService.MyBinder) service;
            //2, get data from the service
            String content = mMyBinder.getStringInfo();
            // 3, the interface prompts
            Toast.makeText(ServiceSimpleActivity.this,content,Toast.LENGTH_LONG).show();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
           // Callback after unbinding
            mMyBinder = null; }};Copy the code

3. Bind and unbind services

            case R.id.bind_service:
                Intent intent = new Intent(this,SimpleService.class);
                // Bind the service
                bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
                break;
            case R.id.unbind_service:
                // Unbind the service
                unbindService(mConnection);

                break;Copy the code

Click the bind button to bind the service, and you have your MyBinder object in onServiceConnected. Now you can communicate with your service through this object.

PNG binding service.png

The lifecycle method calls are as follows:

image.png

As you can see, the binding service lifecycle calls onCreate,onBind,onUnbind, and onDestroy. Only the middle two lifecycle methods are different from startService. One chart shows the similarities and differences in the life cycles of the two approaches:

PNG Service life cycle

Tips: Service lifecycle methods are different from activities in that they do not call superclass lifecycle methods, such as super.oncreate ().

Multiple components bind to the same service

A Service can bind multiple components to the same Service. The first component binding calls the onCreate lifecycle method, and subsequent bindings simply call the onBind method to return the IBinder to the client. When a component bound to a service calls unbindService to unbind the service or the component itself has been reclaimed by the system, the service is stopped from being reclaimed and the onUnbind and onDestroy methods are called back.

Several ways in which services communicate with application components

A BroadcastReceiver is a Service that runs in the background indefinitely and is independent of the component that starts it. As a result, it has no relation to the BroadcastReceiver. Therefore, it cannot communicate with the component that started it. Although the Service does not provide a way to start communication in this way, there are other ways to solve this problem, using BroadcastReceiver.

Scenario Description: Start an image download Service that runs in the background for a long time through startService, and then click the download button on the interface to deliver a download link to the Service through the Intent. When the download is complete, the BroadcastReceiver notifies the Activity interface to display the image. Take a look at the code implementation:

The Service code is as follows:

public class DownloadService extends Service {
    public static final String IMAGE = "iamge_url";
    public static final String RECEIVER_ACTION = "com.zhouwei.simpleservice";
    private static final String TAG = "DownloadService";
    public static final String ACTION_START_SERVICER = "com.zhouwei.startservice";
    public static final String ACTION_DOWNLOAD = "com.zhouwei.startdownload";
    private Looper mServiceLooper;
    private ServiceHandler mServiceHandler;
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper){
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            // The worker thread does a time-consuming download
            String url = (String) msg.obj;
            Bitmap bitmap = null;
            try {
                bitmap = Picasso.with(getApplicationContext()).load(url).get();
                Intent intent = new Intent();
                intent.putExtra("bitmap",bitmap);
                intent.setAction(RECEIVER_ACTION);
                // Notification display
                LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
            } catch (IOException e) {
                e.printStackTrace();
            }


            // Stop the service when the work is donestopSelf(); }}@Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onCreate(a) {
        // Start a worker thread to do time-consuming work
        HandlerThread thread = new HandlerThread("ServiceHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        // Get the worker thread's Looper
        mServiceLooper = thread.getLooper();
        // Create a Handler for the worker thread
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG,"call onStartCommand...");
        if(intent.getAction().equals(ACTION_DOWNLOAD)){
            handleCommand(intent);
        }else if(intent.getAction().equals(ACTION_START_SERVICER)){
            //do nothing
        }

        return START_STICKY;
    }

    private void handleCommand(Intent intent){
        String url = intent.getStringExtra(IMAGE);
        // Send a message to downloadMessage message = mServiceHandler.obtainMessage(); message.obj = url; mServiceHandler.sendMessage(message); }}Copy the code

A new DownloadService is created, which starts a worker thread in it, downloads the image in the thread, and then notifies the Activity to display it via BroadcastReceiver.

The code for the Activity is as simple as registering its BroadcastReceiver and displaying the image in the onReceiver.

private ImageView mImageView;
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Display the image
            Bitmap bitmap = intent.getParcelableExtra("bitmap"); mImageView.setImageBitmap(bitmap); }};Copy the code

    /**
     * 启动下载
     */
    private void startDownload(a){
        Intent intent = new Intent(this,DownloadService.class);
        // Start the service
        intent.putExtra(DownloadService.IMAGE,"http://www.8kmm.com/UploadFiles/2012/8/201208140920132659.jpg");
        intent.setAction(DownloadService.ACTION_DOWNLOAD);
        startService(intent);
    }Copy the code

The effect is as follows:

Service Downloads image.gif

This is done using BroadcastReceiver to communicate with components and services.

2. LocaService uses Binder to communicate with services since the service started by startService is separate from the component that started it. It is difficult to communicate with each other, so Google also provides a communication method between the two, that is, component binding service, which is to bind components and services together through bindService mentioned above. The component can get an IBinder interface that the Service returns via onBind, and the two can communicate, which is a common way for Service application classes to communicate.

A component communicates with a service through a Binder interface to simulate a service playing music. First define a communication interface for IPlayer:

/** * Created by zhouwei on 17/5/11. */

public interface IPlayer {
    / / play
    public void play(a);
    / / pause
    public void pause(a);
    / / stop
    public void stop(a);
    // Get the playback progress
    public int getProgress(a);
    // Get the duration
    public int getDuration(a);
}Copy the code

Then add a MusicService class that inherits Service to implement the Iplayer interface:

public class MusicService extends Service implements IPlayer{
    public static final String TAG = "MusicService";
    private LocalService mBinder = new LocalService();
    public class LocalService extends Binder{
        public MusicService getService(a){
            return MusicService.this; }}@Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void play(a) {
        Log.i(TAG,"music play...");
    }

    @Override
    public void pause(a) {
        Log.i(TAG,"music pause...");
    }

    @Override
    public void stop(a) {
        Log.i(TAG,"music stop...");
    }

    @Override
    public int getProgress(a) {
        return 100;
    }

    @Override
    public int getDuration(a) {
        return 10240; }}Copy the code

Binder provides a getService method that returns a MusicService instance. IBinder obtains the MusicService instance and communicates with the Service.

The code for the Activity is as follows:

 private MusicService.LocalService mLocalService;
    private MusicService mMusicService;
    // Bind/unbind the Service callback interface
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //1, obtain the Binder interface object
            mLocalService = (MusicService.LocalService) service;
            //2, get the MusicService instance
            mMusicService = mLocalService.getService();

            // Once you have the Music Service instance, you can call interface methods
            // It can be used to play/pause music, and it can also be used to get the progress of the current playing music, duration, etc

            mMusicService.play();

            mMusicService.pause();

            mMusicService.stop();

            int progress = mMusicService.getProgress();
            Log.i(MusicService.TAG,"progress:"+progress);

            int duration = mMusicService.getDuration();
            Log.i(MusicService.TAG,"duration:"+duration);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
           // Callback after unbinding
            mMusicService = null;
            mLocalService = null; }};Copy the code

Once you get MusicService, you can call interface methods, such as playing music, pausing, stopping, getting progress, and so on. Take a look at the printed log:

PNG to simulate playing music

Using AIDL communication (Cross-town Communication IPC) AIDL (Android Interface Definition Language) does all the work of breaking up objects into primitives that the operating system can recognize and group into processes to perform IPC. If you don’t have multithreaded access to the service, you can also use Messenger to communicate across town, and the Messenger method actually has AIDL as its underlying structure. Messenger creates a queue containing all client requests in a single thread so that the service receives one request at a time. However, if you want the service to handle multiple requests at the same time, you can use AIDL directly. In this case, your service must be multithreaded and thread-safe in design. To use AIDL directly, you must create an.aidl file that defines the programming interface. The Android SDK tools use this file to generate an abstract class that implements the interface and handles IPC, which you can then extend within the service.

Since space is limited, AIDL implementation of interprocess communication will be covered in a separate article, which will not be verbose here.

Note: Use AIDL only when you allow different clients to use IPC to access the service and want to handle multiple threads in the service. If you don’t want to use IPC to access different applications, use the inheritance Binder described above to communicate with interfaces.

The Service summary

StartService Starts the Service. After the Service is started, it runs in the background indefinitely until the Service is stopped by stopService or stopSelf. The Service is independent from the component and communication is difficult (but there are ways to do this). Via BroadcastReceiver). Another way is to bindService, which is a binding service. Components are bound to the service, and the service is affected by the component later in its life. If all the components bound to the service are destroyed, the service will stop. The service binding approach is usually used when components and services need to communicate with each other. StartService is typically used to perform tasks in the background without returning results to the component. The two approaches are not completely independent, that is, you can bind a service that has already been started with startService, and you can indicate the Action to be performed by adding an Action to the Intent. For example, an Intent Action is used to mark the music to be played, and startService is called to start the music service to play the music. When the interface needs to display the playing progress, binderService can be used to bind the service to obtain the song information. In this case, the Service needs to implement a life cycle in two ways. In this case, the service cannot be stopped by stopService or stopSelf unless all clients have been unbound.

A Service runs on the main thread. Therefore, it cannot perform time-consuming or intensive tasks. To perform time-consuming or computationally intensive tasks, start a worker thread in the Service and execute the tasks in the thread. Or use IntentService, as discussed in the next section.

IntentService

IntentService is a subclass of Service that uses a worker thread to process all startup requests one by one, which is the best option if you don’t require the Service to process multiple requests simultaneously. You simply implement the onHandIntent method, which receives the Intent for each launch request, enabling you to perform background work.

IntentService sample

IntentService starts a worker thread by default and stops the service when the task is finished. So IntentService is sufficient for most of our work. IntentService is relatively simple. As long as you implement a method OnHandleIntent, let’s look at an example:

IntentService extension class:

public class MyIntentService extends IntentService {
    public static final String TAG ="MyIntentService";
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public MyIntentService(a) {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
      // This is already a worker thread

       boolean isMainThread =  Thread.currentThread() == Looper.getMainLooper().getThread();
        Log.i(TAG,"is main thread:"+isMainThread);

        // Perform the time-consuming download operation
        mockDownload();
    }

    /** * simulate the execution of download */
    private void mockDownload(a){
       try {
           Thread.sleep(5000);
           Log.i(TAG,"Download complete...");
       }catch(Exception e){ e.printStackTrace(); }}}Copy the code

Then start the service and look at the printed log as shown below:

image.png

Check whether the main thread, the result is false, indicating that a worker thread is started, 5 seconds later, print download complete, and automatically stop the service.

IntentService source analysis

IntentService automatically starts a thread for us to perform time-consuming operations and automatically stops the service when the task is complete. Let’s take a look at the source code to find out. In fact, IntentService source is very simple, more than a hundred lines. Take a look:

   // 1, there is a Looper variable and a ServiceHandler variable. ServiceHander inherits the Handler to handle messages
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
           // Call onHandleIntent in the worker thread, and subclasses perform specific actions based on the data passed by the Intent
            onHandleIntent((Intent)msg.obj);
          // The Service is automatically stopped after the task is completedstopSelf(msg.arg1); }}//2, in the OnCreate method, a thread HandlerThread is created and started
// Then get the worker thread's Looper and initialize the Handler with Looper.
 public void onCreate(a) {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
//3, the onStart() method sends a message to Handler and passes the Intent to Handler for processing
 @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
// 4, onStartCommand calls onStart directly
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
// 5 Finally, there is an abstract method that subclasses need to implement. This method is called in the handleMessage, which is executed in the worker thread.
 protected abstract void onHandleIntent(@Nullable Intent intent);Copy the code

The code above is clearly commented out, so let’s look at the whole process in a diagram.

Diagram of IntentService. PNG

The code is very simple, IntentService source code looks familiar? Starting a worker thread in an Intent is just like starting an Intent Service. Create a thread in onCreate, start, initialize Handler and Looper, and then send a message to Handler to handle the task in onStartCommand.

IntentService summary

IntentService is a subclass of Service. By default, IntentService starts a worker thread to execute a time-consuming task and automatically stops the Service after the task is completed. Extending IntentService is easy by providing a constructor and implementing the onHandleIntent method without overwriting the other methods of the parent class. But if you want to bind the service, you’ll have to override onBind to return an IBinder. With Service, multiple requests can be executed simultaneously, whereas with IntentService, only one request can be executed simultaneously.

If you have any questions, please feel free to discuss them. All Demo code has been uploaded to Github:github.com/pinguo-zhou… (A Demo code that contains all of my blog points)

API Guides Service Android API Definition Language (AIDL)