preface

IPC series of articles: Recommended reading in order.

Android IPC Service Android IPC AIDL Service Android IPC AIDL Android IPC server (IBinder) Android IPC server (IBinder)

Android has four main components: Activity, Service, BroadcastReceiver, and ContentProvider. Their functions are as follows:

BroadcastReceiver– > Receive broadcast ContentProvider– > provide data for other modules to use

This article focuses on services, from which you will learn:

Start and stop of Service 2. Time-consuming operation performed by Service 3. Relationship between Service and Thread and Manager 4

1. Start and stop Service

Start by defining a Service class called MyService that inherits from Service.

public class MyService extends Service { public MyService() { super(); } @nullable @override public IBinder onBind(Intent Intent) {return null; } @override public void onCreate() {// we can do some initialization, as opposed to onDestroy() 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 void onDestroy() {super.ondestroy (); }}Copy the code

This is the simplest Service Demo. To use this Service, you need to register it in androidmanifest.xml:

        <service android:name=".service.MyService">
        </service>
Copy the code

Now that Service is defined, how do I use it? There are two ways to start a Service:

——> startService ——> bindService(Intent Intent)

These are both methods in the Context

Start Service

Construct an Intent, pass it in startService(Intent Intent).

    private void startService() {
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
    }
Copy the code

In this way, the Service calls as follows:

The points to note are:

OnStartCommand (xx) is still called when an existing Service is started again.

Close Service

After Service is enabled, Service is already started. To disable the Service, do the following:

    private void stopService() {
        Intent intent = new Intent(this, MyService.class);
        stopService(intent);
    }
Copy the code

Or when a Service is done it ends itself:

stopSelf();
Copy the code

Binding enable Service

From the above example, you can see that after the Service is enabled, the caller is no longer associated with the Service. For example, if the caller is an Activity, the function of a Service is to keep counting. In the scenario where Service is enabled, two problems occur:

1. The Activity can’t get the Service count result directly (indirectly, via broadcasting, etc.), that is, it can’t get the Service reference. 2. When an Activity exits, the Service will not be closed unless it is actively stopped. The unfortunate metaphor is, “To manage a child without caring.”

Binding to enable Service solves this problem. In order to enable Service binding, the above Demo changes slightly.

Define MyBinder to inherit from Binder:

Public class MyBinder extends Binder {private Service Service; public MyBinder(Service service) { this.service = service; Public Service getService() {return Service; }}Copy the code

We overwrite onBind(xx) to return null when the Service is displayed. The method is not called in this scenario. When a Service is enabled by a binding, a reference to IBiner is returned to the binder.

#MyService.java
    public IBinder onBind(Intent intent) {
        return new MyBinder(this);
    }
Copy the code

What is returned is a reference to the MyBinder object that holds the MyService reference. Where does the binder receive the IBinder reference? Define the ServiceConnection anonymous inner class in the Activity:

ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName Name, IBinder service) {//service is returned from onBind(xx) MyBinder MyBinder = (MyBinder)service; MyService MyService = (MyService) mybinder.getService (); } @override public void onServiceDisconnected(ComponentName name) {//Service is called when it is disconnected;Copy the code

With a ServiceConnection reference, you then need to establish a relationship with the Service, which is the binding that starts the Service.

    private void bindService() {
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    }
Copy the code

As you can see above, the binding starts the process:

Connect the Service to ServiceConnection (onBind(xx)) This reference holds a Service reference. Once the binding is complete, IBinder is called into ServiceConnection’s onServiceConnected(xx) interface to return an IBinder reference

This answers the first question above: The binder can’t get a Service reference. Let’s look at the flow of binding to open a Service call method:

Solution to the Service

Since the ServiceConnection reference is passed in the binding, you can guess that the ServiceConnection reference needs to be passed in the unbinding, otherwise you cannot determine which Service to unbind.

    private void unBindService() {
        unbindService(serviceConnection);
    }
Copy the code

Manually calling this method unbinds the Service. When the Activity binding starts a Service, if the Activity is destroyed, the corresponding Service is also destroyed. That answers the second question.

It is important to note that if the Service is displayed as enabled, it cannot be unbound to close the Service. If the binding has Service enabled, the Service method cannot be closed with a display.

2. Service Performs time-consuming operations

In the Service onCreate(xx) method the loop counts:

#MyService.java
    @Override
    public void onCreate() {
        super.onCreate();

        while(true) {
            count++;
            try {
                Thread.sleep(1000);
            } catch (Exception e) {

            }
        }
    }
Copy the code

OnCreate (xx) is executed on the main thread. Take a look at the onCreate(xx) call stack.

#ActivityThread.java private class ApplicationThread extends IApplicationThread.Stub { ... //IPC communication calls this method, Public final void scheduleCreateService(IBinder Token, ServiceInfo Info, CompatibilityInfo compatInfo, int processState) { updateProcessState(processState, false); CreateServiceData s = new CreateServiceData(); s.token = token; s.info = info; s.compatInfo = compatInfo; // sendMessage sendMessage(h.create_service, s); }... } private void sendMessage(int what, Object obj, int arg1, int arg2, Boolean async) {... Message msg = Message.obtain(); msg.what = what; msg.obj = obj; msg.arg1 = arg1; msg.arg2 = arg2; if (async) { msg.setAsynchronous(true); } //mH is the Handler constructed in the ActivityThread, that is, the Handler constructed in the main thread. mH.sendMessage(msg); }Copy the code

Now look at where the Message is received:

#ActivityThread.java public void handleMessage(Message msg) { switch (msg.what) { ... case CREATE_SERVICE: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj))); // CreateService handleCreateService((CreateServiceData) msg.obj); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case BIND_SERVICE: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind"); // BindService handleBindService((BindServiceData) msg.obj); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case UNBIND_SERVICE: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind"); handleUnbindService((BindServiceData) msg.obj); Service schedulePurgeIdler(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; . }}Copy the code

MyService overrides onCreate(xx), onBind(xx), and onUnbind(xx), all of which are called in the main thread. Since the Service methods are executed in the main thread, the child thread must do the counting.

#MyService.java
    @Override
    public void onCreate() {
        super.onCreate();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true) {
                    Log.d("time:", count + "");
                    count++;
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {

                    }
                }
            }
        }).start();
    }

    public int getCount() {
        return count;
    }
Copy the code

The Service is always counting. How do we inform the caller of the count? Suppose the caller is an Activity. Again, there are two ways:

2. If it is bound to an open Service, the Activity gets the IBinder reference, which in turn gets the Service reference. Finally, it can call getCount() to get the count value. And update the UI.

IntentService, which encapsulates HandlerThread and provides callback methods to perform time-consuming tasks, is available if starting a child thread every time is a hassle.

3. The relationship between Service and Thread and Manager

Service contacts Thread

If a Service can’t perform time-consuming operations directly, why do you need a Service instead of just starting a child thread? A Service runs in the background for a long time. A Service object exists all the time. When we need to use a Service, we find it through an Intent or an IBinder and use the functionality it provides. Also achieve counting function:

  • If you turn on the Thread count directly in your Activity, you will close the Thread when the Activity exits. When the Activity is started again, the Thread reference is not found and the accumulated count cannot be continued. Furthermore, regardless of memory leaks, the Activity exits without closing Thread, and when the Activity starts again, the Thread reference is still not found.
  • In addition, if you want to separate counting functions for multiple activities, you cannot use Thread to share counting functions with multiple activities.

The essence of the above problem is to maintain a reference to the Thread object. Thread is just a tool, there is no need to maintain global references (that’s what Thread pools do).

The Service and the Manager

Since maintaining a global Thread reference method is not recommended, implement a singleton Manager(management class) to hold threads and use them to perform time-consuming tasks, while the outside world calls this Manager to retrieve data, as follows:

public class CountManager { private static volatile CountManager instance; private CountManager(){ startCount(); } private int count = 0; public static CountManager getInstance() { if (instance == null) { synchronized (CountManager.class) { if (instance == null) { instance = new CountManager(); } } } return instance; } private void startCount() { new Thread(new Runnable() { @Override public void run() { while(true) { Log.d("time:", count + ""); count++; try { Thread.sleep(1000); } catch (Exception e) { } } } }).start(); } public int getCount() { return count; }}Copy the code

In fact, many projects are using Activity + Manager to achieve page display + data acquisition. The Activity displays the UI, and the background retrieves data from the Manager, such as from a database or from the network. Finally, the data is fed back to the Activity to refresh the UI. Now that you have Manager, is it necessary to use Service? The answer is yes. As one of the four components of Android, Service is widely used in Android system.

With Service + Binder, Android interprocess communication can be implemented

4. Initial acquaintance with Service interprocess communication

The previous examples looked at the caller and the called being in the same process. Can they call each other if they are not in the same process? If the Activity in process A needs to use CountManager in process B, it cannot be called directly. With Services, binders do this.

Process A’s Activity shows that the UI needs data, which is retrieved from process B. Process B enables Service to produce data. Process A binds Service of process B to obtain IBinder reference, and finally invokes Service to obtain data. The key is the bridge in the middle, the Binder. As you can see, the focus of a Service should be on interprocess communication.

The following articles will focus on Binder, AIDL and other knowledge related to THE IPC process.

This article is based on Android 10.0

If you like, please like, pay attention to your encouragement is my motivation to move forward

Learn Android step by step with me as we continue to update