Service of Android Application Component Research

Source: Internet
Author: User

This article is an original article. You are welcome to repost it! Please indicate the source when reprint: http://blog.csdn.net/windskier

The previous two articles introduced the management of acitivity, including the management of saving tasks and operations at various stages in the activity lifecycle, in this article, we will take a detailed look at the application service management process in the Android system.

Service is a very important component in Android. As a developer engaged in Android development, service is a component that must be mastered. This article does not analyze how to use service, instead, we analyze the source code of AMS from the perspective of how AMS manages services, in order to understand the source code implementation of AMS operations on services.

We know that there are two ways to start the service. One is to start the service by using the startservice () method. The start of this method is used as the client side, but only as a initiator.onStartCommand() Method to require the service to do some operations, but the service through the bind method is different, and the bindservice () method to start and bind
The client can access service-related business operations through the service interface provided by the service through IPC.

The lifecycles of the two methods are also different. When the client starts the service through the startservice () method, the client calls the stopservice () method or the service itself.Stopself () is used to end the current service. If multiple clients start this service, a client will stop it.

The clientIf the service is started in bindservice () mode, the client ends the connection with the service through unbindservice (). The lifecycle of this method will be introduced later.

For more service applications, see the official Android documentation.

The source code for starting Serivce using startservice () is not analyzed here. The following describes the process of starting and bind service using the bindservice () method, startup actually contains the source code for starting Serivce in startservice () mode.

1. servicedispatcher1.1 creates servicedispatcher. It can be seen from its name that it is similar to a dispatcher. This class is used to assign service ibinder to the client after the client bind a service is successful; when the client unbind service is used, it is responsible for notifying the status of the client unbind service.

@ Contextimpl. Java

    @Override    public boolean bindService(Intent service, ServiceConnection conn,            int flags) {        IServiceConnection sd;        if (mPackageInfo != null) {            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),                    mMainThread.getHandler(), flags);        } else {            throw new RuntimeException("Not supported in system context");        }        try {            int res = ActivityManagerNative.getDefault().bindService(                mMainThread.getApplicationThread(), getActivityToken(),                service, service.resolveTypeIfNeeded(getContentResolver()),                sd, flags);            if (res < 0) {                throw new SecurityException(                        "Not allowed to bind to service " + service);            }            return res != 0;        } catch (RemoteException e) {            return false;        }    }

The mpackageinfo object is a loadedapk type, which stores the content of the current package.

@ Contextimpl. Java

    /*package*/ LoadedApk mPackageInfo;

@ Loadedapk. Java

    public final IServiceConnection getServiceDispatcher(ServiceConnection c,            Context context, Handler handler, int flags) {        synchronized (mServices) {            LoadedApk.ServiceDispatcher sd = null;            HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);            if (map != null) {                sd = map.get(c);            }            if (sd == null) {                sd = new ServiceDispatcher(c, context, handler, flags);                if (map == null) {                    map = new HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>();                    mServices.put(context, map);                }                map.put(c, sd);            } else {                sd.validate(context, handler);            }            return sd.getIServiceConnection();        }    }

From the code above, we can see that the management of servicedispatcher is based on package. That is to say, for a certain package, the component is defined in it. If the request is to bind a service

Loadedapk will assign a servicedispatcher to this component. Therefore, servicedispatcher is a concept of the client. Each time component requests bind a different service, loadedapk will assign a servicedispatcher to it, as shown in.

1.2 innerconnection

From the above bindservice () code, we can see that what we pass to AMS in the BIND process is not serviceconnection, but an iserviceconnection interface, and its corresponding service entity type is innerconnection, innerconnection is an internal static class of servicedispatcher.

Question 1: Why does it not directly pass serviceconnection to AMS as a callback, but another type of innerconnection?

The first point is that serviceconnection is an interface (Java), which is an internal class object after implements serviceconnection. According to the implementation mechanism of Android ibinder, during the IPC call process, the passed parameter must be a parcel object, which requires that the passed object be of the parcel type, or this type implements the method for writing the parcel object, that is, writetoparcel (). Even if serviceconnection implements writetoparcel (), it cannot pass its callback function to AMS, because writetoparcel () is mainly used to package member variables, parcel does not package the method. Therefore, it is impossible for the system to pass the internal class of serviceconnection to AMS. Therefore, you must provide another method to call the method in serviceconnection, this innerconnection plays this role.

Question 2: since we decided to use innerconnection to provide the AMS Method for remotely calling serviceconnection, why do we still need servicedispatcher?

In fact, in my opinion, servicedispatcher can be completely replaced by innerconnection, which can be implemented in terms of functions. However, in IPC communication programming, we should try to reduce the workload of ibinder parameters as much as possible, this is understandable.

2. Client intent

In Android, if an application includes shareduserid of "android. uid. System", this includes system service. This application requires a bind service, and this service only allows the application of "android. uid. system" to bind. This is a security concern. Some services do not want applications outside the system UID to request services.

In the scenario described above, if a third-party appliction, it cannot have "android. UID. system "shareduserid. If you want to request the service above, you cannot directly bind this servcie. Instead, you can only request it through the application of the above system uid. So how to request service through the application of system uid? AMS provides a set of solutions for this situation. The following uses inputmethodmanagerservice as an example to describe.

First, let's briefly introduce the logic of the input method. Because multiple input methods can exist in the system, inputmethodmanagerservice will go to the input method service set by the bind system or selected by the user, these input methods can only be requested by the application of the system uid. IMS will provide a pendingintent to AMS when using the BIND Input Method service. Since this pendingintent is used by the client to access the input method service, we call this pendingintent clientintent. The relevant code is as follows:

Startinputinnerlocked () @ inputmethodmanagerservice. Java

        mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);        mCurIntent.setComponent(info.getComponent());        mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,                com.android.internal.R.string.input_method_binding_label);        mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(                mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));        if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) {            mLastBindTime = SystemClock.uptimeMillis();            mHaveConnection = true;            mCurId = info.getId();            mCurToken = new Binder();            try {                if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurToken);                mIWindowManager.addWindowToken(mCurToken,                        WindowManager.LayoutParams.TYPE_INPUT_METHOD);            } catch (RemoteException e) {            }            return new InputBindResult(null, mCurId, mCurSeq);        } else {            mCurIntent = null;            Slog.w(TAG, "Failure connecting to input method service: "                    + mCurIntent);        }

As shown in, the input method service can only be requested by the application of the system uid. The system service inputmethodmanagerservice bind it and provides a client intent to AMS. If a third-party application needs to request the input method service, operations can only be performed through the activity indicated by the client intent provided by IMS. This activity is called deleagesettings. In this way, the security problem caused by the absence of arbitrary operations by third-party applications on the input method service is avoided.

The following code manages client intent in AMS:

Bindservice () @ activitymanagerservice. Java

            if (callerApp.info.uid == Process.SYSTEM_UID) {                // Hacky kind of thing -- allow system stuff to tell us                // what they are, so we can report this elsewhere for                // others to know why certain services are running.                try {                    clientIntent = (PendingIntent)service.getParcelableExtra(                            Intent.EXTRA_CLIENT_INTENT);                } catch (RuntimeException e) {                }                if (clientIntent != null) {                    clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);                    if (clientLabel != 0) {                        // There are no useful extras in the intent, trash them.                        // System code calling with this stuff just needs to know                        // this will happen.                        service = service.cloneFilter();                    }                }            }
3. the following section briefly describes the data structures involved in service management by AMS. Through these data structures, we can see how AMS manages services, this part has no complicated and hard-to-understand content. You can simply read the code with an intuitive understanding.

Each service can have different application processes for bind, and the intent of the service may have multiple, that is, there may be multiple processes for bind the service, in addition, multiple different processes may use the same intent for BIND serice. Indicates the possibility of such existence.

AMS assigns an intentbindrecord type object to the intent of the service for each bind and stores it in the servicerecord. bindings member variable;

AMS assigns an appbindrecord type object to each bind process of the service. Because different applications may use the same intent for bind Service, AMS stores this appbindrecord object in intentbindrecord. in apps, a typical case of multi-process using the same intent is the client intent mentioned in the previous section;

AMS assigns a connectionrecord type object for each bind connection. Each application process may have multiple components to bind the service using the same intent, the connectionrecord object in the same process is stored in appbindrecord. connections;

The connections in activityrecord and processrecord contains the connectionrecord objects of all the different services of the BIND. It has different ranges from appbindrecord. connections.

All in all, there are several data structures related to the Service as follows:

4. Set the forground attribute

When the system space is tight, the system needs to kill some processes and reclaim their memory for use by other processes. Of course, the process where the service is located cannot be spared, but if the current service is very important, if it is not allowed to be recycled by the system, you need to set its forground attribute when the service is started. It is very easy to set it through the startforeground () method of the service. The default service is background.

If the service does not want to maintain the forground attribute, you can call the stopforeground () method of the service to implement it and return it to the background attribute.

When setting the service as the forground attribute, You can require the system to display a notification in the status bar. The user can use notification to perform the set operation.

If a service in a process is in the forground attribute state, the oom_adj value recycled by the process is perceptible_app_adj, so as to avoid being recycled by the system. Later, we will analyze the OOM mechanism of AMS.

Computeoomadjlocked () @ activitymanagerservice. Java

         else if (app.foregroundServices) {            // The user is aware of this app, so make it visible.            adj = PERCEPTIBLE_APP_ADJ;            schedGroup = Process.THREAD_GROUP_DEFAULT;            app.adjType = "foreground-service";        } 

5. Unbind Service

The bind service process is a collection creation and bind process from scratch, while the unbind process is the process of collecting unbind and destroying the service, but the unbind process includes the process of destroying the service at any time, it is necessary to study this rule.

5.1 for the client corresponding to the unbind service, the unbind service process will unbind all clients that use the same iserviceconnection bind; for the service, only when the number of processes recorded by an intent bind service is 0 will the service process perform the unbind operation, as described in the following code. Removeconnectionlocked () @ activitymanagerservice. Java
        if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0                && b.intent.hasBound) {            try {                bumpServiceExecutingLocked(s, "unbind");                updateOomAdjLocked(s.app);                b.intent.hasBound = false;                // Assume the client doesn't want to know about a rebind;                // we will deal with that later if it asks for one.                b.intent.doRebind = false;                s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());            } catch (Exception e) {                Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);                serviceDoneExecutingLocked(s, true);            }        }

5.2 destroy Service

When destroy is required for a service in unbind, you must first check the flag set when BIND is used. If flag bind_auto_create is set, after unbind, the system checks whether the destroy service should be used, as shown in the following code:

Removeconnectionlocked () @ activitymanagerservice. Java

        if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {            bringDownServiceLocked(s, false);        }

If flag bind_auto_create is not set, AMS does not consider the destoy service operation. Next, we will analyze the conditions that the AMS meets if bind_auto_create is set to destroy.

● In the unbind process, after removing the current connectionrecord, it is found that other connectionrecord in the service is still set with bind_auto_create, so the service will not be destroy;

Destroy is the service only when no connectionrecord is set to bind_auto_create in the service. Therefore, we can see that bind_auto_create plays a decisive role in the service destruction process.

Bringdownservicelocked () @ activitymanagerservice. Java

            if (!force) {                // XXX should probably keep a count of the number of auto-create                // connections directly in the service.                Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();                while (it.hasNext()) {                    ArrayList<ConnectionRecord> cr = it.next();                    for (int i=0; i<cr.size(); i++) {                        if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {                            return;                        }                    }                }            }

● If no connectionrecord is set for bind_auto_create in the service, the service will be destroyed, and all clients will be notified through iserviceconnection. Connected () before destruction.

Bringdownservicelocked () @ activitymanagerservice. Java

            Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator();            while (it.hasNext()) {                ArrayList<ConnectionRecord> c = it.next();                for (int i=0; i<c.size(); i++) {                    try {                        c.get(i).conn.connected(r.name, null);                    } catch (Exception e) {                        Slog.w(TAG, "Failure disconnecting service " + r.name +                              " to connection " + c.get(i).conn.asBinder() +                              " (in " + c.get(i).binding.client.processName + ")", e);                    }                }            }

And unbind all clients.

Bringdownservicelocked () @ activitymanagerservice. Java

        if (r.bindings.size() > 0 && r.app != null && r.app.thread != null) {            Iterator<IntentBindRecord> it = r.bindings.values().iterator();            while (it.hasNext()) {                IntentBindRecord ibr = it.next();                if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down binding " + ibr                        + ": hasBound=" + ibr.hasBound);                if (r.app != null && r.app.thread != null && ibr.hasBound) {                    try {                        bumpServiceExecutingLocked(r, "bring down unbind");                        updateOomAdjLocked(r.app);                        ibr.hasBound = false;                        r.app.thread.scheduleUnbindService(r,                                ibr.intent.getIntent());                    } catch (Exception e) {                        Slog.w(TAG, "Exception when unbinding service "                                + r.shortName, e);                        serviceDoneExecutingLocked(r, true);                    }                }            }        }

To sum up, pay attention to the following four points during the unbind process:

1. The unbind process will unbind all clients that use the same iserviceconnection bind service;

2. The unbind process is for intent. Therefore, the unbind process is executed only when the number of processes recorded in the intentbindrecord of the bind service is 0. That is to say, if multiple processes use the same intent to bind the service, only when the intent-based processes are removed from the intentbindrecord, The unbind is actually taken.

3. if bind_auto_create is set for connectionrecord of unbind, AMS will attempt to destroy this service. If bind_auto_create is not set for all connectionrecord of the service, the service will be destroyed. In other words, if bind_auto_create is set for multiple connectionrecord in the service, the service will be destroyed only when bind_auto_create connectionrecord is set for the last one, even if there are other connectionrecord
Bind this service.

4. If the current service is started through startservice (), no unbind process will be destroyed.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.