天天看點

startService 分析--之一1, startService1,應用程序2 AMS程序

1, startService

啟動一個服務至少需要在2個程序之間進行跨程序通信,

1,程序A向AMS發送一個請求

2,AMS處理之後,到程序A所啟動的服務所在的程序。

1,應用程序

用戶端apk調用startService方法調用流程圖如下,

startService 分析--之一1, startService1,應用程式2 AMS程式

Context的startService方法如下,

public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, mUser);
}
           

startServiceCommon方法如下,

private ComponentName startServiceCommon(Intent service, UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess();
            ComponentName cn = ActivityManagerNative.getDefault().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                 getContentResolver()), getOpPackageName(), user.getIdentifier());
            if (cn != null) {
                •••
            }
            return cn;
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
    }
           

不用說,直接利用binder機制,跨程序調用AMS的startservice方法

2 AMS程序

在AMS服務所在的程序調用流程圖如下,

startService 分析--之一1, startService1,應用程式2 AMS程式

AMS的startService方法如下,

public ComponentName startService(IApplicationThread caller, Intent service,
    String resolvedType, String callingPackage, int userId)
           throws TransactionTooLargeException {
        enforceNotIsolatedCaller("startService");
       •••
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid, callingPackage, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }
           

mServices是ActiveServices類型的對象,其startServiceLocked方法如下所示,

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String 
resolvedType, int callingPid, int callingUid, String callingPackage, int userId)
            throws TransactionTooLargeException {
       •••
        ServiceLookupResult res =retrieveServiceLocked(service, resolvedType, 
              callingPackage, callingPid, callingUid, userId, true, callerFg);
        if (res == null) {
            return null;
        }
       •••
        ServiceRecord r = res.record;

        if (!mAm.getUserManagerLocked().exists(r.userId)) {
            Slog.d(TAG, "Trying to start service with non-existent user! " + r.userId);
            return null;
        }
        •••
        return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    }
           

startServiceLocked做2件事情,首先通過retrieveServiceLocked方法找到(或建立)一個ServiceRecord節點,然後才能

執行後續的啟動service的動作。

retrieveServiceLocked方法如下,

private ServiceLookupResult retrieveServiceLocked(Intent service,
  String resolvedType, String callingPackage, int callingPid, int callingUid, int userId,
            boolean createIfNeeded, boolean callingFromFg) {
        ServiceRecord r = null;
       •••
        ServiceMap smap = getServiceMap(userId);
        final ComponentName comp = service.getComponent();
        if (comp != null) {
            r = smap.mServicesByName.get(comp);
        }
        if (r == null) {
            Intent.FilterComparison filter = new Intent.FilterComparison(service);
            r = smap.mServicesByIntent.get(filter);
        }
        if (r == null) {
            try {
                ResolveInfo rInfo =
                    AppGlobals.getPackageManager().resolveService(
                                service, resolvedType,
                                ActivityManagerService.STOCK_PM_FLAGS, userId);
                ServiceInfo sInfo =
                    rInfo != null ? rInfo.serviceInfo : null;
                if (sInfo == null) {
                    Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId +
                          ": not found");
                    return null;
                }
                ComponentName name = new ComponentName(
                        sInfo.applicationInfo.packageName, sInfo.name);
                if (userId > 0) {
                    if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
                            sInfo.name, sInfo.flags)
                            && mAm.isValidSingletonCall(callingUid, sInfo.applicationInfo.uid)) {
                        userId = 0;
                        smap = getServiceMap(0);
                    }
                    sInfo = new ServiceInfo(sInfo);
                    sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId);
                }
                r = smap.mServicesByName.get(name);
                if (r == null && createIfNeeded) {
                    Intent.FilterComparison filter
                            = new Intent.FilterComparison(service.cloneFilter());
                    ServiceRestarter res = new ServiceRestarter();
                    BatteryStatsImpl.Uid.Pkg.Serv ss = null;
                    BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics();
                    synchronized (stats) {
                        ss = stats.getServiceStatsLocked(
                                sInfo.applicationInfo.uid, sInfo.packageName,
                                sInfo.name);
                    }
                    r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);
                    res.setService(r);
                    smap.mServicesByName.put(name, r);
                    smap.mServicesByIntent.put(filter, r);

                    •••
            } catch (RemoteException ex) {
                // pm is in same process, this will never happen.
            }
        }
        •••
       if (!mAm.mIntentFirewall.checkService(r.name, service, callingUid, callingPid,
                    resolvedType, r.appInfo)) {
                return null;
            }
            return new ServiceLookupResult(r, null);
        }
        return null;
}
           

代碼較長, 在AMS内部的相關表格裡找到對應的ServiceRecord節點,如果找不到,就建立一個新節點,并插入到相應的表格中。

示意圖如下:

startService 分析--之一1, startService1,應用程式2 AMS程式

startServiceInnerLocked方法如下,

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
        ProcessStats.ServiceState stracker = r.getTracker();
        if (stracker != null) {
     stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
        }
        r.callStart = false;
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startRunningLocked();
        }
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);
        if (error != null) {
            return new ComponentName("!!", error);
        }
        •••
        return r.name;
    }
           

bringUpServiceLocked方法如下,

private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean 
  execInFg, boolean whileRestarting) throws TransactionTooLargeException {
     
        if (r.app != null && r.app.thread != null) {
            sendServiceArgsLocked(r, execInFg, false);
            return null;
        }
       ••• 
        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;
        ProcessRecord app;

        if (!isolated) {
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
           
            if (app != null && app.thread != null) {
                try {
        app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                } catch (TransactionTooLargeException e) {
                    throw e;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
                } 
            }
        } else { 
            app = r.isolatedProc;
        }

        // Not running -- get it started, and enqueue this service record
        // to be executed when the app comes up.
        if (app == null) {
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    "service", r.name, false, isolated, false)) == null) {
               •••
}

        return null;
    }
           

在啟動一個服務時,分為三種情況:

1,該服務已經啟動了,最後會直接調用其onStartCommand方法

2,該服務所在的apk已經在運作,直接啟動該服務,調用其onCreate方法

3,該服務所在的apk還未啟動,則首先啟動該apk,然後啟動該服務。

繼續閱讀