天天看點

基于N源碼Service啟動分析

前面分析了應用内部Activity的啟動過程,其實如果是應用第一個Activity的啟動,流程上還會多一些建立程序和ActivityThread的過程。Service同樣也是如此,如果是應用内部啟動服務,且沒有配置android:process的話,預設就在應用程序中啟動,下面我們就配置android:process=“:remote”,讓服務在另一個程序中啟動。

private void startServiceTest(){
		Intent intent = new Intent("com.android.wuliq.service");
		startService(intent);
	}
           

接着進入ContextImpl.java

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

    @Override
    public boolean stopService(Intent service) {
        warnIfCallingFromSystemProcess();
        return stopServiceCommon(service, mUser);
    }
    private ComponentName startServiceCommon(Intent service, UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            ComponentName cn = ActivityManagerNative.getDefault().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), getOpPackageName(), user.getIdentifier());
            if (cn != null) {
                if (cn.getPackageName().equals("!")) {
                    throw new SecurityException(
                            "Not allowed to start service " + service
                            + " without permission " + cn.getClassName());
                } else if (cn.getPackageName().equals("!!")) {
                    throw new SecurityException(
                            "Unable to start service " + service
                            + ": " + cn.getClassName());
                }
            }
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
           

可以看到跟Activity啟動很類似,調用ActivityManagerNative.getDefault().startService進行跨程序通訊。getDefault()擷取本地代理ActivityManagerProxy對象,調用ActivityManagerProxy中的startService方法:

public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, String callingPackage, int userId) throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        service.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeString(callingPackage);
        data.writeInt(userId);
        mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
        reply.readException();
        ComponentName res = ComponentName.readFromParcel(reply);
        data.recycle();
        reply.recycle();
        return res;
    }
           

用遠端binder對象mRemote調用transact會通過底層binder驅動轉到遠端服務的中間者stub(ActivityManagerNative):

case START_SERVICE_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            Intent service = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            String callingPackage = data.readString();
            int userId = data.readInt();
            ComponentName cn = startService(app, service, resolvedType, callingPackage, userId);
            reply.writeNoException();
            ComponentName.writeToParcel(cn, reply);
            return true;
        }
           

可以看到遠端這邊通過IApplicationThread app = ApplicationThreadNative.asInterface(b);擷取本地代理ApplicationThreadProxy,後面從服務端調用用戶端接口需要用到它。

接着調用到服務端實作者AMS:

public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, String callingPackage, int userId)
            throws TransactionTooLargeException {
			ComponentName res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid, callingPackage, userId);
	}
           

這裡調用了ActiveServices.java中的startServiceLocked方法:

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

這裡會通過retrieveServiceLocked去檢索AndroidManifest.xml檔案并将服務資訊存儲在ServiceRecord中。接着跳轉到startServiceInnerLocked,裡面又跳轉到bringUpServiceLocked函數:

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
		ProcessRecord app;
		app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
		if (app != null && app.thread != null) {
            realStartServiceLocked(r, app, execInFg);
		}
        if (app == null && !permissionsReviewRequired) {
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    "service", r.name, false, isolated, false)) == 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);
                return msg;
            }
            if (isolated) {
                r.isolatedProc = app;
            }
        }
	}
           

這裡的mAm為ActivityManagerService類型,如果擷取程序資訊不為空,則調用realStartServiceLocked啟動服務。這裡我們要求在另一個程序中啟動,是以程序并未建立,為空。是以調用AMS的startProcessLocked方法,開啟程序:

private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
            if (entryPoint == null) entryPoint = "android.app.ActivityThread";
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            checkTime(startTime, "startProcess: asking zygote to start proc");
            Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);
			this.mPidsSelfLocked.put(startResult.pid, app);
	}
           

這裡調用Process.start函數建立了一個新的程序,指定新的程序執行android.app.ActivityThread類。最後将表示這個新程序的ProcessRecord儲存在mPidSelfLocked清單中。

建立一個程序後,然後導入android.app.ActivityThread這個類,然後執行它的main函數:

public static void main(String[] args) {
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
		Looper.loop();
}
           

可以看到這裡建立了ActivityThread對象,并建立looper和消息隊列。主線程的消息隊列就是在這裡建立的。在Android應用程式中,每一個程序對應一個ActivityThread執行個體,這裡調用ActivityThread.attach函數進一步處理:

private void attach(boolean system) {
            final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
}
           

可以看到,又是跨程序通路,由經驗得知,直接進入AMS的attachApplication函數:

private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
			ProcessRecord app;
			app = mPidsSelfLocked.get(pid);
			mServices.attachApplicationLocked(app, processName);
	}
           

擷取之前儲存的程序資訊,接着調用ActiveServices.java的attachApplicationLocked函數:

boolean attachApplicationLocked(ProcessRecord proc, String processName)
            throws RemoteException {
                for (int i=0; i<mPendingServices.size(); i++) {
                    sr = mPendingServices.get(i);
                    if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
                            || !processName.equals(sr.processName))) {
                        continue;
                    }

                    mPendingServices.remove(i);
                    i--;
                    proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,
                            mAm.mProcessStats);
                    realStartServiceLocked(sr, proc, sr.createdFromFg);
                    didSomething = true;
                    if (!isServiceNeeded(sr, false, false)) {
                        // We were waiting for this service to start, but it is actually no
                        // longer needed.  This could happen because bringDownServiceIfNeeded
                        // won't bring down a service that is pending...  so now the pending
                        // is done, so let's drop it.
                        bringDownServiceLocked(sr);
                    }
                }
	}
           

擷取之前儲存的服務資訊,調用realStartServiceLocked啟動服務:

private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
			sendServiceArgsLocked(r, execInFg, true);
	}
           

這裡的app.thread就是前面attachApplication的時候擷取的用戶端的本地代理ApplicationThreadProxy,調用它的scheduleCreateService會跨程序到用戶端的ApplicationThread中的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);
        }
           

下面的流程就跟Activity的啟動很相似了。

case CREATE_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
           
private void handleCreateService(CreateServiceData data) {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            service.onCreate();
}
           

可以看到這裡擷取ClassLoader建立了Service,并建立了ContextImpl、Application等,attach給service,最後調用onCreate()。

至于onStartCommand是在什麼時候調用呢,跟蹤代碼發現在ActiveServices.java中app.thread.scheduleCreateService執行完後,調用了sendServiceArgsLocked(r, execInFg, true);函數,也是跨程序到用戶端,發消息調用onStart。

至此,這個自定義的服務就啟動起來了。

        這樣,Android系統在新程序中啟動服務的過程就分析完成了,雖然很複雜,但是條理很清晰。它通過三次Binder程序間通信完成了服務的啟動過程,分别是:

        一. 從主程序調用到ActivityManagerService程序中,完成新程序的建立;

        二. 從新程序調用到ActivityManagerService程序中,擷取要在新程序啟動的服務的相關資訊;

        三. 從ActivityManagerService程序又回到新程序中,最終将服務啟動起來。