Android进阶 四大组件的工作过程(二):Service的工作过程

Android进阶 四大组件的工作过程(二):Service的工作过程

Android进阶 四大组件的工作过程(二):Service的工作过程_第1张图片

导语

本片文章主要是来介绍Service组件的工作过程的,主要分成Service的启动和Service的绑定两个部分来讲。上一篇文章我们已经介绍了Activity的工作工程,而Service的工作过程也与Activity有一定的相关之处,也会涉及到AMS(ActivityManagerService)

上篇文章:

  • Android进阶 四大组件的工作过程(一):Activity的工作过程

Service的启动过程

Service的启动过程可以分为三个个过程,实际上也和Activity的工作过程相类似了。可以分为从ContextImpl到AMS的调用过程,从AMS到ActivityThread以及ActivityThread启动Service。

从ContextImpl到AMS的调用过程

我们先来看第一个部分,从ContextImpl到AMS的调用过程。如果要启动Service,我们首先会通过ContextWrapper的startService来开启流程。这个ContextWrapper是Context的子类,它实现了Context中的方法,我们就来看ContextWrapper的startService方法:

	.......
	Context mBase;
	.......
    public @Nullable ComponentName startService(Intent service) {
        return mBase.startService(service);
    }

它会调用类型为Context类的startService,Context类是一个抽象类,所以我们需要找到它的实现类,这个具体的实现类实际上我们在根Activity的创建过程中已经提到过了,可以回顾一下:

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

        ContextImpl appContext = createBaseContextForActivity(r);//1-----1
      	........
    }

我们直接看performLaunchActivity中的注释一处,这里就创建出了Context的实现类,并且在之后的attach方法中将其与Activity进行了绑定,可以看一下createBaseContextForActivity方法:

    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        final int displayId = ActivityClient.getInstance().getDisplayId(r.token);
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);

      	........
        return appContext;
    }

主要就是通过第一行的ContextImpl.createActivityContext方法创建出来的,这个方法里会调用到ContextImpl的构造方法。总之我们最后调用的就是这个ContextImpl的startService方法,这个startService方法又会调用到startServiceCommon方法,我们就直接看这个startServiceCommon方法:

private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            ComponentName cn = ActivityManager.getService().startService(     //1--------1
                    mMainThread.getApplicationThread(), service,
                    service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
                    getOpPackageName(), getAttributionTag(), user.getIdentifier());
           		..........
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

看重点,这里的重点显然就是注释一处的 ActivityManager.getService().startService()方法,看起来它会调用到AMS的startService方法,实际上也正是这样,和Activity的创建一样,是通过远程调用了AMS的方法。

到此为止,就正式进入到了AMS的处理和方法调用过程了,所以接下来让我们看第二部分。

从AMS到ActivityThread

上面提到远程调用到了AMS的startService方法,所以我们继续来看AMS的startService方法:

    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage,
            String callingFeatureId, int userId)
            throws TransactionTooLargeException {
      		...........
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
                res = mServices.startServiceLocked(caller, service,     //1-------1
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, callingFeatureId, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }

startService的重头就是继续调用到了注释一处的startServiceLocked方法,接下来会进行一系列的跳转:

 ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired,
            String callingPackage, @Nullable String callingFeatureId, final int userId,
            boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken)
            throws TransactionTooLargeException {
     	.........
        ServiceLookupResult res =
            retrieveServiceLocked(service, null, resolvedType, callingPackage,
                    callingPid, callingUid, userId, true, callerFg, false, false); //1-----1
 		if (res == null) {
            return null;
        }
        if (res.record == null) {   
            return new ComponentName("!", res.permission != null
                    ? res.permission : "private to package");
        }
        ServiceRecord r = res.record; //2-------2
 		........
 		 final ComponentName realResult =
                startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg,
                allowBackgroundActivityStarts, backgroundActivityStartsToken);//3-------3
     }

这里截取了比较重要的三段代码,在注释一处,会通过retrieveServiceLocked方法查找是否有与参数service对应的ServiceRecord(这个ServiceRecord和ActivityRecord类似,是描述Service的一个类),如果没有找到,接下来就会在注释二处会去到res的record参数并将其赋值给ServiceRecord变量,最后将其传入startServiceInnerLocked继续Service的启动。这个startServiceInnerLocked方法接下来又会进行跳转,具体最后会跳转到bringUpServiceLocked方法中,我们直接看这个方法:

    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen,
            boolean enqueueOomAdj)
            throws TransactionTooLargeException {
        ...........

        // Service is now being launched, its package can't be stopped.
        try {
            AppGlobals.getPackageManager().setPackageStoppedState(
                    r.packageName, false, r.userId);
        } catch (RemoteException e) {
        } catch (IllegalArgumentException e) {
            Slog.w(TAG, "Failed trying to unstop package "
                    + r.packageName + ": " + e);
        }

        final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
        final String procName = r.processName;
        HostingRecord hostingRecord = new HostingRecord(
                HostingRecord.HOSTING_TYPE_SERVICE, r.instanceName,
                r.definingPackageName, r.definingUid, r.serviceInfo.processName);
        ProcessRecord app;

        if (!isolated) {
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid); //1------1
            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                        + " app=" + app);
            if (app != null) {//2-----2
                final IApplicationThread thread = app.getThread();
                final int pid = app.getPid();
                final UidRecord uidRecord = app.getUidRecord();
                if (thread != null) {
                    try {
                        app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode,
                                mAm.mProcessStats);
                        realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
                                enqueueOomAdj);//3-------3
                        return null;
                    } catch (TransactionTooLargeException e) {
                        throw e;
                    } catch (RemoteException e) {
                        Slog.w(TAG, "Exception when starting service " + r.shortInstanceName, e);
                    }

                    // If a dead object exception was thrown -- fall through to
                    // restart the application.
                }
            }
        } 
        ..............

        // Not running -- get it started, and enqueue this service record
        // to be executed when the app comes up.
        if (app == null && !permissionsReviewRequired && !packageFrozen) {//4---------4
            // TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service
            //  was initiated from a notification tap or not.
            if (r.isSdkSandbox) {
                final int uid = Process.toSdkSandboxUid(r.sdkSandboxClientAppUid);
                app = mAm.startSdkSandboxProcessLocked(procName, r.appInfo, true, intentFlags,
                        hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, uid, r.sdkSandboxClientAppPackage);
                r.isolationHostProc = app;
            } else {
                app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,//5---------5
                        hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated);
            }
            if (app == null) {
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": process is bad";
                Slog.w(TAG, msg);
                bringDownServiceLocked(r, enqueueOomAdj);
                return msg;
            }
            if (isolated) {
                r.isolationHostProc = app;
            }
        }

        .........
        return null;
    }

我们从上往下分析这个方法,在注释一处会获取到Service要在哪个进程里启动,这个属性默认是会在当前进程中运行,我们也可以通过修改Service的process属性来进行修改,这个内容具体是在IPC中提到过。如果这个进程存在,那么接下来就会进入到注释二处的分支处,如果这个进程有线程那么就会执行注释三处的realStartServiceLocked方法。

若需要的进程不存在的话,就会进入到注释四处的分支处,紧接着会运行到注释五处的分支来启动一个新的进程。不过这不是我们的重点,我们继续回到注释三处的方法处。让我们看这个方法:

private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
            IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
            boolean enqueueOomAdj) throws RemoteException {
       		.........
            thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                    app.mState.getReportedProcState());
        	........
    }

我们先不看别的,实际上就是通过上面的一句来推进了Service的启动,这个thread点开来的话我们可以发现是一个IApplicationThread接口,这个接口在哪里实现呢?我们可以看ActivityThread的内部类ApplicationThread:

private class ApplicationThread extends IApplicationThread.Stub {
	............     
}

这显然是通过AIDL实现的,ApplicationThread类就是这个thread的实现类,具体最后还是调用到了ActivityThread的方法。所以说,到此为止又由AMS进入到了ActivityThread中了。接下来是Service启动的最后一部分。

ActivityThread启动Service

接下来进入到了ActivityThread中,让我们来看他的scheduleCreateService方法:

        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(H.CREATE_SERVICE, s);
        }

这个方法具体就是触发了自身Handler的方法,我们来看Handler对CREATE_SERVICE标志位的处理:

        case CREATE_SERVICE:
            if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        ("serviceCreate: " + String.valueOf(msg.obj)));
            }
            handleCreateService((CreateServiceData)msg.obj);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;

继续看中间的handleCreateService方法:

private void handleCreateService(CreateServiceData data) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

            Application app = packageInfo.makeApplicationInner(false, mInstrumentation);

            final java.lang.ClassLoader cl;
            if (data.info.splitName != null) {
                cl = packageInfo.getSplitClassLoader(data.info.splitName);
            } else {
                cl = packageInfo.getClassLoader();
            }
            service = packageInfo.getAppFactory() //1------1
                    .instantiateService(cl, data.info.name, data.intent);
            ContextImpl context = ContextImpl.getImpl(service
                    .createServiceBaseContext(this, packageInfo));
            .............

            service.attach(context, this, data.info.name, data.token, app,//2--------2
                    ActivityManager.getService());
            service.onCreate();//3----------3
            mServicesData.put(data.token, data);
            mServices.put(data.token, service);//4--------4
            try {
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); //5-----5
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                    "Unable to create service " + data.info.name
                    + ": " + e.toString(), e);
            }
        }
    }

这里我们还是从上往下看首先是通过packageInfo获取到了ClassLoader,这个ClassLoader是用于生成Service实例的。在注释一处就用这个ClassLoader获取到了Service的实例。接着在注释二处通过attach方法将service自身的context进行了绑定。
注释三处,调用了Service的onCreate回调;最后注释四处将当前Service添加入ActivityThread的service列表中便于后续管理。一切都完毕后,调用注释五处的方法通知AMS当前Service创建完毕了,这个方法用于标记服务执行完成,并根据不同的类型和结果执行相应的操作。

到这里为止,Service的启动过程就介绍完毕了。接下来我们来探讨Service的绑定过程。

Service的绑定过程

我们知道Service除了可以用Start启动外,还可以通过Bind方法进行客户端的绑定,接下来我们就来看Service的绑定过程。
Service的绑定过程我们也可以分成两个过程:ContextImpl到AMS的调用过程和Service的绑定过程。

ContextImpl到AMS调用

先来看第一个阶段,这个阶段和之前start过程很像,具体来说就是通过ContextWrapper的bindService方法跳转到ContextImpe的bindService方法,所以这里我们就直接看ContextImpl的bindService方法了:

    public boolean bindService(
            Intent service, int flags, Executor executor, ServiceConnection conn) {
        return bindServiceCommon(service, conn, flags, null, null, executor, getUser());
    }

这实际上还是老过程,我们继续看它的bindServiceCommon方法:

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            String instanceName, Handler handler, Executor executor, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser and
        // ActivityManagerLocal.bindSdkSandboxService
        IServiceConnection sd;
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (handler != null && executor != null) {
            throw new IllegalArgumentException("Handler and Executor both supplied");
        }
        if (mPackageInfo != null) {
            if (executor != null) {
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
            } else {
                sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
            }
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                flags |= BIND_WAIVE_PRIORITY;
            }
            service.prepareToLeaveProcess(this);
            int res = ActivityManager.getService().bindServiceInstance( //1------1
                    mMainThread.getApplicationThread(), getActivityToken(), service,
                    service.resolveTypeIfNeeded(getContentResolver()),
                    sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

这里我们看注释一处,其实这个过程真的是非常类似,接下来就会开始进入到AMS中调用AMS的方法了,接下来我们来看AMS中的调用。

AMS到ActivityThread调用

上面说到了bindServiceInstance方法,接下来就看这个方法:

private int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String instanceName,
            boolean isSdkSandboxService, int sdkSandboxClientAppUid,
            String sdkSandboxClientAppPackage, String callingPackage, int userId)
            throws TransactionTooLargeException {
        enforceNotIsolatedCaller("bindService");
        enforceAllowedToStartOrBindServiceIfSdkSandbox(service);

        // Refuse possible leaked file descriptors
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        if (isSdkSandboxService && instanceName == null) {
            throw new IllegalArgumentException("No instance name provided for isolated process");
        }

        // Ensure that instanceName, which is caller provided, does not contain
        // unusual characters.
        if (instanceName != null) {
            for (int i = 0; i < instanceName.length(); ++i) {
                char c = instanceName.charAt(i);
                if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
                            || (c >= '0' && c <= '9') || c == '_' || c == '.')) {
                    throw new IllegalArgumentException("Illegal instanceName");
                }
            }
        }

        try {
            if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                final ComponentName cn = service.getComponent();
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindService:"
                        + (cn != null ? cn.toShortString() : service.getAction()));
            }
            synchronized (this) {
                return mServices.bindServiceLocked(caller, token, service, resolvedType, connection,
                        flags, instanceName, isSdkSandboxService, sdkSandboxClientAppUid,
                        sdkSandboxClientAppPackage, callingPackage, userId); //1---1
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

还是接着跳转,看注释一处的方法bindServiceLocked:

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String instanceName, boolean isSdkSandboxService, int sdkSandboxClientAppUid,
            String sdkSandboxClientAppPackage, String callingPackage, final int userId)
            throws TransactionTooLargeException {
        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
                + " type=" + resolvedType + " conn=" + connection.asBinder()
                + " flags=0x" + Integer.toHexString(flags));
			.........

            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); //1-------1
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent,
                    callerApp.uid, callerApp.processName, callingPackage, res.aliasComponent);

			.........
            ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
            if (clist == null) {
                clist = new ArrayList<>();
                mServiceConnections.put(binder, clist);
            }
            clist.add(c);
            boolean needOomAdj = false;
            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
                needOomAdj = true;
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,//2-------2
                        permissionsReviewRequired, packageFrozen, true) != null) {
                    mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
                    return 0;
                }
            }
			.........

            if (s.app != null && b.intent.received) {//3------3
                // Service is already running, so we can immediately
                // publish the connection.

                // If what the client try to start/connect was an alias, then we need to
                // pass the alias component name instead to the client.
                final ComponentName clientSideComponentName =
                        res.aliasComponent != null ? res.aliasComponent : s.name;
                try {
                    c.conn.connected(clientSideComponentName, b.intent.binder, false);//4-------4
                } catch (Exception e) {
                    Slog.w(TAG, "Failure sending service " + s.shortInstanceName
                            + " to connection " + c.conn.asBinder()
                            + " (in " + c.binding.client.processName + ")", e);
                }

                // If this is the first app connected back to this binding,
                // and the service had previously asked to be told when
                // rebound, then do so.
                if (b.intent.apps.size() == 1 && b.intent.doRebind) {//5--------5
                    requestServiceBindingLocked(s, b.intent, callerFg, true);//6------6
                }
            } else if (!b.intent.requested) {//7-----7
                requestServiceBindingLocked(s, b.intent, callerFg, false);//8------8
            }

            maybeLogBindCrossProfileService(userId, callingPackage, callerApp.info.uid);

            getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);

        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        notifyBindingServiceEventLocked(callerApp, callingPackage);

        return 1;
    }

这里我们先介绍一下相关的类代表什么:

  • AppBindRecord代表应用程序与服务之间的绑定关系。它记录了应用程序与服务之间的连接信息,包括应用程序的UID、进程名称、包名,以及与服务建立的连接对象。
  • ConnectionRecord:这是一个代表应用程序与服务之间连接的类。它包含了连接对象的相关信息,例如连接的应用程序、连接的服务、连接的标志等。
  • IntentBindRecord 类表示服务(ServiceRecord)与绑定的 Intent 之间的关联关系记录。

首先在注释一处获得了Service的AppBindRecord信息和ConnectionRecord信息;接着,如果创建的标注为BIND_AUTO_CREATE的话就会进入到注释二处的bringUpServiceLocked方法中,这个方法中又会调用到我们在启动流程中提到的realStartServiceLocked方法,也就是说会自动地创建和启动Service;注释三处的条件代表service已经运行,这样就会紧接着调用到注释四处的connected方法,具体来说是会调用到InnerConncetion的connected方法,并且最终调用到LoadedApk的connected方法:

        public void connected(ComponentName name, IBinder service, boolean dead) {
            if (mActivityExecutor != null) {
                mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
            } else if (mActivityThread != null) {
                mActivityThread.post(new RunConnection(name, service, 0, dead));
            } else {
                doConnected(name, service, dead);
            }
        }

这会调用H(ActivityThread的Handler)的post方法向主线程发送消息,并且解决当前应用程序进程和Service跨进程通信的问题。接下来我们来看注释五到八的内容,其实他们最终调用到的都是requestServiceBindingLocked方法,唯一的差别就是最后一个参数的差别。具体来说这两个分支分别代表当前应用程序是不是第一个和Service绑定的。接下来再看requestServiceBindingLocked方法:

private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        if (r.app == null || r.app.getThread() == null) {
            // If service is not currently running, can't yet bind.
            return false;
        }
        if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
                + " rebind=" + rebind);
        if ((!i.requested || rebind) && i.apps.size() > 0) {//1------1
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind",
                        OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
                r.app.getThread().scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.mState.getReportedProcState());  //2-----------2
                if (!rebind) {
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (TransactionTooLargeException e) {
                // Keep the executeNesting count accurate.
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying, false);
                throw e;
            } catch (RemoteException e) {
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
                // Keep the executeNesting count accurate.
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying, false);
                return false;
            }
        }
        return true;
    }

我们先看注释一处的判断条件,i.requested表示当前Intent是否发送过绑定Service的请求。接下来我们还需要稍微分析一下IntentBindRecord。AMS会为每个绑定Service的Intent分配一个IntentBindRecord类型的对象。IntentBindRecord类中又有一个apps列表来存储当前Intent绑定Service的应用程序进程。所以i.apps.size() > 0代表当前Intent绑定Service的应用程序进程大于0。

看注释二处的代码,接下来就和之前一样,跳转到了ActivityThread之中了。

AppBindRecord的结构

为了帮助我们更好地理解,我觉得还是有必要来详细地解析一下AppBindRecord的结构和它与其他辅助类的关系,我们先来看AppBindRecord的源码:

final class AppBindRecord {
    final ServiceRecord service;    // The running service.
    final IntentBindRecord intent;  // The intent we are bound to.
    final ProcessRecord client;     // Who has started/bound the service.

    final ArraySet<ConnectionRecord> connections = new ArraySet<>();
                                    // All ConnectionRecord for this client.

    void dump(PrintWriter pw, String prefix) {
        pw.println(prefix + "service=" + service);
        pw.println(prefix + "client=" + client);
        dumpInIntentBind(pw, prefix);
    }

    void dumpInIntentBind(PrintWriter pw, String prefix) {
        final int N = connections.size();
        if (N > 0) {
            pw.println(prefix + "Per-process Connections:");
            for (int i=0; i<N; i++) {
                ConnectionRecord c = connections.valueAt(i);
                pw.println(prefix + "  " + c);
            }
        }
    }

    AppBindRecord(ServiceRecord _service, IntentBindRecord _intent,
            ProcessRecord _client) {
        service = _service;
        intent = _intent;
        client = _client;
    }

    public String toString() {
        return "AppBindRecord{"
            + Integer.toHexString(System.identityHashCode(this))
            + " " + service.shortInstanceName + ":" + client.processName + "}";
    }

    void dumpDebug(ProtoOutputStream proto, long fieldId) {
        long token = proto.start(fieldId);
        proto.write(AppBindRecordProto.SERVICE_NAME, service.shortInstanceName);
        proto.write(AppBindRecordProto.CLIENT_PROC_NAME, client.processName);
        final int N = connections.size();
        for (int i=0; i<N; i++) {
            ConnectionRecord conn = connections.valueAt(i);
            proto.write(AppBindRecordProto.CONNECTIONS,
                Integer.toHexString(System.identityHashCode(conn)));
        }
        proto.end(token);
    }
}

可以看到,它的内部有四个成员变量,分别是ServiceRecord类型,IntentBindRecord类型,ProcessRecord类型和一个ConnectionRecord的集合。

而IntentBindRecord中存储有一个ServiceRecord,这是用来描述被绑定的Service的;还有一个Key为ProcessRecord,Value为AppBindRecord的Map。具体差不多就这样如图所述:
Android进阶 四大组件的工作过程(二):Service的工作过程_第2张图片

ActivityThread绑定Service

直接看ActivityThread的scheduleBindService方法:

        public final void scheduleBindService(IBinder token, Intent intent,
                boolean rebind, int processState) {
            updateProcessState(processState, false);
            BindServiceData s = new BindServiceData();
            s.token = token;
            s.intent = intent;
            s.rebind = rebind;

            if (DEBUG_SERVICE)
                Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                        + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
            sendMessage(H.BIND_SERVICE, s);
        }

实际上还是老一套,会向自身的Handler发送消息,我们还是看Handler是如何处理这个标志位的:

          case BIND_SERVICE:
              Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
              handleBindService((BindServiceData)msg.obj);
              Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
              break;

显然是会接着调用handleBindService:

private void handleBindService(BindServiceData data) {
        CreateServiceData createData = mServicesData.get(data.token);
        Service s = mServices.get(data.token);//1---------1
        if (DEBUG_SERVICE)
            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess(isProtectedComponent(createData.info),
                        s.getAttributionSource());
                try {
                    if (!data.rebind) {//2--------2
                        IBinder binder = s.onBind(data.intent);
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {//3---------3
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to bind to service " + s
                            + " with " + data.intent + ": " + e.toString(), e);
                }
            }
        }
    }

在注释一处获得了要进行绑定的Service对象,如果已经Service已经调用过unbind方法就会进入到注释三处的分支,触发onRebind的回调;不然就会进入到注释二的分支处,先触发onBind回调之后又会调用AMS的publicService方法。我们来看publishServiceLocked方法:

    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        try {
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r
                    + " " + intent + ": " + service);
            if (r != null) {
                Intent.FilterComparison filter
                        = new Intent.FilterComparison(intent);
                IntentBindRecord b = r.bindings.get(filter);
                if (b != null && !b.received) {
                    b.binder = service;
                    b.requested = true;
                    b.received = true;
                    ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
                    for (int conni = connections.size() - 1; conni >= 0; conni--) {
                        ArrayList<ConnectionRecord> clist = connections.valueAt(conni);
                        for (int i=0; i<clist.size(); i++) {
                            ConnectionRecord c = clist.get(i);
                           	........
                            try {
                                c.conn.connected(clientSideComponentName, service, false);//1-----1
                            } catch (Exception e) {
                                Slog.w(TAG, "Failure sending service " + r.shortInstanceName
                                      + " to connection " + c.conn.asBinder()
                                      + " (in " + c.binding.client.processName + ")", e);
                            }
                        }
                    }
                }

                serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false, false);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

看注释一处,这里就是遍历了Service中的所有ConnectionRecord对象,然后调用conncted方法,这个我们在之前也提到过,最后是会调用到LoadedApk的connected方法:

public void connected(ComponentName name, IBinder service, boolean dead) {
    if (mActivityExecutor != null) {
        mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
    } else if (mActivityThread != null) {
        mActivityThread.post(new RunConnection(name, service, 0, dead));
    } else {
        doConnected(name, service, dead);
    }
}

public void doConnected(ComponentName name, IBinder service, boolean dead) {
            ServiceDispatcher.ConnectionInfo old;
            ServiceDispatcher.ConnectionInfo info;

            synchronized (this) {
               ..........
            if (dead) {
                mConnection.onBindingDied(name);
            } else {
                // If there is a new viable service, it is now connected.
                if (service != null) {
                    mConnection.onServiceConnected(name, service);//1------1
                } else {
                    // The binding machinery worked, but the remote returned null from onBind().
                    mConnection.onNullBinding(name);
                }
            }
        }

上面的RunConnection实际上是LoadedApk的内部类,最后也是会执行doConnected方法。我们看上面的注释一处,这就是执行了我们bindService是传入的ServiceConnection对象的回调方法,这样就完成了整个Service的绑定过程了。

Service工作过程总结

下面是我总结的一张关于Service工作过程的总结,其中省略了一些细节,主要是帮助理解大致的过程。相信大家也可以发现无论是绑定还是启动,其中都有很多的重合处,无非就是从AMS到ActiviyThread,并且都是通过远程调用的方式。如果要抠细节的话可以看一下AppBindRecord和其他辅助类的关系,这样可以帮助我们更好地理解这整个设计。
Android进阶 四大组件的工作过程(二):Service的工作过程_第3张图片

你可能感兴趣的:(Android开发笔记,android,java,开发语言,系统架构)