preface

During this period of time, I was reading something related to IPC, which inevitably involved service and process thread, and in the process of research, I realized that my memory of these things had begun to be a little vague — which is not to be expected. So I just took a bit of time to warm up and have this blog post.

Thank you very much for the official Google documentation – especially since some of the articles are available in Chinese.

The body of the

1. What is service?

A Service is an application component that can operate in the background for a long time without using a user interface. So the question is, since it doesn’t use a user interface, how does it know when to start doing what? The answer is — it can form some kind of relationship with other reference components so that it can perform the appropriate action at the appropriate time based on the information it sends in.

In general, there are two kinds of connections: startService() and bindService(). Both of these relationships can get a service up and running, but they differ in many other ways.

Meow ~ How to start the service The way to stop service How a service communicates with the component that starts it The service life cycle
startService The service is started when the startService() method is called in the other component The service stops running when the stopSelf() method is called within a service, or when the stopService() method is called by another component The default communication mode is not provided, and the service runs independently after it is started Once started, a service can run in the background indefinitely, even if the component that started it has been destroyed, until it is stopped
bindService The service is started when the bindService() method is called in the other component The service will stop running after all components bound to the service are destroyed or they all call the unbindService() method With ServiceConnection, components can interact with services, send requests, get results, and even perform these operations across processes using IPC The service stops when all its bound components are unbound (either because the component has been destroyed or because it called the unbindService() method)

Note: 1. “Other Components” in the table do not include Broadcast Receiver, which cannot directly start or bind service. 2. StartService () does not conflict with bindService(). The same service may have both a component calling startService() to start it and a component binding to it. When the same service has both of these relationships with other components, its life cycle changes and the service must be stopped from both perspectives to truly stop.

Ok, from this table we can see that each of these two ways to start a service has its own characteristics and we can choose the appropriate way according to the needs of our project.

2. How to create a service?

This is something that almost every introductory Android book will say, and it’s basically what they say:

  • Create a class that inherits Service(or a subclass of it, such as IntentService) and override some of its key callback methods, such as onStartCommand(), onBind(), etc
  • Declare it in the SSE document and configure other attributes as required.

To be fair, all of this is like creating a new Activity, like its twin — including some of its key methods, which are filled with a strong gay love.

  • OnCreate () this method is called only once during the life of each service, and it is called before onStartCommand() and onBond(), so we can do some one-time initialization in this method.

  • OnStartCommand () This method is called when another component starts a service using the startService() method.

  • OnBind () this method is called when other components have been bound to the service via the bindService() method. This method has an IBinder return value, which means that when overwriting it you must return an IBinder object that supports communication between other components and the service — and if you don’t want the service to be bound by another component, You can do this by returning a null value in this method.

  • OnDestroy () this is the last method that the service will call in its lifetime. When this method is called, the service will be destroyed. So we should clean up some resources in this method, such as registered listeners and so on.

Only the Android :name attribute is mandatory for use in the sse document. However, sometimes proper configuration can make development go more smoothly, so it’s important to know what properties can be declared by registering a service.


Copy the code
  • Android :enabled: This service can be instantiated by the system if it is true, but not if it is false. The default is true

  • Android: Exported: If true, components of other applications can also invoke and interact with the service. If false, only applications with the same user ID or the same application can enable or bind to the service. Its default value depends on whether the service has intent filters. If there is no filter, it means that the service can only be called if the exact class name of the service is specified. In other words, the service can only be used internally — other applications do not know its class name. The default value exported in this case is false. On the other hand, a filter means that a service takes into account external usage, and the default exported value is true

  • Android :icon: an icon that represents this service

  • Android :isolatedProcess: If set to true, the service runs in a special process that is separate from the rest of the system and can only be communicated with through the Service API. The default is false.

  • Android :label: the name of the service displayed to the user. If not set, the label attribute is used by default.

  • Path name to the android: name: this service, such as “com. Lypeer. Demo. MyService”. This property is the only one that must be filled in.

  • Other components must have the specified permission to start the service.

  • Android: Process: The name of the thread in which the service runs. The default service starts in the main thread.

Here’s an example:

public class ServiceDemo extends Service { private static final String TAG = "ServiceDome"; @Override public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Nullable @Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind"); return null; } @Override public void onDestroy() { Log.d(TAG, "onDestroy"); super.onDestroy(); }}Copy the code

Copy the code

3. How to start service?

There are two ways to start a service, but they are both very general. Here are two ways to start a service.

3.1, the startService ()

Another component can start a particular service by calling the startService() method, which causes the onStartCommand() method in the service to be called. When the startService() method is called, other components need to pass an intent parameter in the method. The service will then receive the intent in onStartCommand() and fetch some data. For example, if an Activity wants to store some data in a database, I can pass the data to a service with an Intent, and then ask the service to connect to the database and store the data. At this point, the user can do anything else — even destroy the Activity — without affecting the service’s ability to store data.

When a service is started in this way, its life cycle is independent of the component that started it. It can run indefinitely in the background, as long as the service does not call stopSelf() and no other component calls stopService() for it.

Alternatively, if you decide to start a service this way and you don’t want the service to be bound, you might have a better choice than creating a class that inherits the Service.

If we extend the Service class, we usually need to create a new thread to perform the work, because by default the Service will work on the main thread of the application, which will degrade the performance of all running activities. IntentService is different. It is a subclass of Service and uses a worker thread to handle all startService requests mindfully. If you don’t require the service to handle multiple requests at the same time, then somehow inheriting the class is better than directly inheriting the service — IntentService already does this:

  • Create a default worker thread that executes all intents passed to onStartCommand() outside the main thread of the application
  • Create a work queue for passing an Intent one by one to the onHandleIntent() implementation so you never have to worry about multithreading
  • Stop the service after all the start requests have been processed and mom will never have to worry about me forgetting to call stopSelf() again
  • Provides the default implementation of onBind() (returns NULL)
  • Provides a default implementation of onStartCommand() that sends the Intent to the work queue and the onHandleIntent() implementation in turn

So we just need to implement the onHandleIntent() method to accomplish the specific functionality logic.

6 don’t? Must be 6! Here is a chestnut:


Intent intent = new Intent(MainActivity.this , IntentServiceDemo.class);
startService(intent);Copy the code
public class IntentServiceDemo extends IntentService {

    public IntentServiceDemo(String name) {
        super(name);
        
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        
    }
}Copy the code

This is much simpler than the inherited Service implementation above. Be careful, though, that if you need to override another method, such as onDestroy(), don’t delete its superclass implementation! Because its superclass implementation might include initialization and destruction of worker threads and work queues, it could be problematic without them.

However, if you need a service to handle multiple requests at the same time, then you have to inherit the service. You have to deal with the worker thread yourself. Here’s an official chestnut:

public class HelloService extends Service { private Looper mServiceLooper; private ServiceHandler mServiceHandler; private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } stopSelf(msg.arg1); } } @Override public void onCreate() { HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; mServiceHandler.sendMessage(msg); return START_STICKY; } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); }}Copy the code

It’s obviously a lot more work than IntentService. However, because it handles the call to onStartCommand itself, it can execute multiple requests at the same time — although it does not do so in the official chestnut. But if you want to do this, you can create a thread for each request and run the requests immediately.

Also, we notice that onStartCommand() returns a strange value, START_STICKY. What is this? Or what is the return value of this method used for? In fact, its return value is used to specify the system’s behavior toward the current thread. Its return value must be one of the following constants: -start_not_sticky: If the system terminates the service after onStartCommand() returns, the service will not be rebuilt unless there is a pending Intent to deliver. This is the safest option to avoid running the service when it is not necessary and when the application can easily restart all outstanding jobs.

  • START_STICKY: If the system terminates the service after onStartCommand() returns, the service is rebuilt and onStartCommand() is called, but the last Intent is never redelivered. Instead, onStartCommand() is called with an empty Intent unless there is a pending Intent to start the service (in which case those intents will be delivered). This applies to media players (or similar services) that do not execute commands but run indefinitely and wait for jobs.

  • START_REDELIVER_INTENT: If the system terminates the service after onStartCommand() returns, the service is rebuilt and onStartCommand() is called with the last Intent passed to the service. Any pending intents are delivered in sequence. This applies to services that actively perform jobs that should be restored immediately, such as downloading files.

In addition, when implementing a service from a service, remember to stop the service. Because the system does not stop or destroy services unless it must reclaim memory resources. In fact, if the service is bound to a visible component, it is almost never stopped or destroyed. In this case, if you forget to stop it when it’s done, you will waste system resources and drain battery power, which should be avoided.

3.2, bindService ()

This is a more complex way to start a service than startService, and a service that starts in this way can do more things, such as send requests to it from other components, receive responses from it, even conduct IPC through it, and so on. We usually refer to the component to which it is bound as a client and call it a server.

To create a service that supports bindings, we would have to override its onBind() method. This method returns an IBinder object, which is the interface the client uses to interact with the server. To get the IBinder interface, there are usually three ways: inherit Binder classes, use Messenger classes, and use AIDL.

Each of these three approaches has its own advantages and disadvantages, more on which will be covered in the next blog post.

conclusion

BindService (), how to start a service, how to create a service, how to start a service, etc. There are a lot of other things not covered, including the details of bindService(), how to choose the appropriate way to start a service. Services in Android: Binder, Messenger, AIDL (2)

Overall, this blog is very superficial in many aspects, such as how IntentService is encapsulated internally. What happens between startService() and onStartCommand()? What exactly is IBinder(), and why do you want to interact with it?

So this is just a popular science article, please don’t make fun of its depth.

Thanks again, Google.