您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“ActivityManagerService之Service怎么啟動”,內容詳細,步驟清晰,細節處理妥當,希望這篇“ActivityManagerService之Service怎么啟動”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
Service 的啟動最終會調用如下方法
// ContextImpl.java private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) { try { // 從 Android 5.0 ,必須使用顯式 Intent 啟動 Service validateServiceIntent(service); service.prepareToLeaveProcess(this); // 通過 AMS 啟動 Service ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), requireForeground, getOpPackageName(), getAttributionTag(), user.getIdentifier()); if (cn != null) { // ... 處理異常結果 ... } // 成功啟動后,返回已啟動的 Service 組件名 return cn; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
Service 最終是通過 AMS 進行啟動的,一旦啟動成功,會返回 Service 的組件名,通過這個返回結果,可以判斷 Service 是否啟動成功,這一點往往被很多開發者忽略。
AMS 最終會調用 ActiveService#startServiceLocked() 來啟動 Service
ActivityManagerService 把 Service 的所有事務都交給 ActiveServices 處理。
// ActiveServices.java 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 { // 調用方是否處于前臺 final boolean callerFg; if (caller != null) { final ProcessRecord callerApp = mAm.getRecordForAppLOSP(caller); if (callerApp == null) { throw new SecurityException( "Unable to find app for caller " + caller + " (pid=" + callingPid + ") when starting service " + service); } callerFg = callerApp.mState.getSetSchedGroup() != ProcessList.SCHED_GROUP_BACKGROUND; } else { callerFg = true; } // 1. 查詢 Service // 其實這是是從緩存獲取 ServiceRecord,或者建立 ServiceRecord 并緩存 ServiceLookupResult res = retrieveServiceLocked(service, null, resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg, false, false); if (res == null) { return null; } if (res.record == null) { return new ComponentName("!", res.permission != null ? res.permission : "private to package"); } // 從查詢的結果中獲取 Service 記錄 ServiceRecord r = res.record; // 2. 對 Service 啟動施加限制 // ... if (forcedStandby || (!r.startRequested && !fgRequired)) { // Service 所在的進程處于后臺,是無法啟動 Service final int allowed = mAm.getAppStartModeLOSP(r.appInfo.uid, r.packageName, r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { Slog.w(TAG, "Background start not allowed: service " + service + " to " + r.shortInstanceName + " from pid=" + callingPid + " uid=" + callingUid + " pkg=" + callingPackage + " startFg?=" + fgRequired); // ... UidRecord uidRec = mAm.mProcessList.getUidRecordLOSP(r.appInfo.uid); return new ComponentName("?", "app is in background uid " + uidRec); } } // ... // 3. 進入下一步 Service 啟動過程 return startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg, allowBackgroundActivityStarts, backgroundActivityStartsToken); }
首先為 Service 在服務端建立一個記錄 ServiceRecord 并緩存起來,這個過程比較簡單,讀者自行分析。
然后,在 Service 啟動之前,施加一些限制,代碼中展示了一段后臺 app 無法啟動 Service 的限制,但是也省略了其他限制的代碼,有興趣的讀者可以自行分析。
本文旨在分析 Service 的啟動流程,對于一些小細節,限于篇幅原因,就不細致分析,請讀者自行研讀代碼。
最后進入下一步的啟動的流程
Service 的啟動,會調用好幾個類似的函數,但是每一個函數最做了一部分功能,這樣就可以把一個龐大的函數分割為幾個小的函數,代碼閱讀性更高,我們要學習這種做法。
private ComponentName startServiceInnerLocked(ServiceRecord r, Intent service, int callingUid, int callingPid, boolean fgRequired, boolean callerFg, boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken) throws TransactionTooLargeException { // ... // 1. 更新 ServiceRecord 數據 r.lastActivity = SystemClock.uptimeMillis(); r.startRequested = true; r.delayedStop = false; r.fgRequired = fgRequired; // 保存即將發送給 Service 的參數 r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), service, neededGrants, callingUid)); // 授予 app 啟動前臺 Service 的 app op 權限 if (fgRequired) { // ... } // 下面一段,確定 addToStarting 的值 final ServiceMap smap = getServiceMapLocked(r.userId); boolean addToStarting = false; // 注意這里的判斷條件 // !callerFg 表示調用方不處于前臺 // !fgRequired 表示啟動的是 非前臺Service // r.app == null 表示 Service 還沒有啟動過 if (!callerFg && !fgRequired && r.app == null && mAm.mUserController.hasStartedUserState(r.userId)) { ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid); if (proc == null || proc.mState.getCurProcState() > PROCESS_STATE_RECEIVER) { // app 沒有創建,或者已經創建,但是在后臺沒有運行任何四大組件 // ... // 延時Service不在這里啟動,直接返回 if (r.delayed) { return r.name; } // 如果目前要啟動非前臺Service超時過了最大數量的,那么當前這個Service,要設置為延時 Service // 而延時 Service 不在這里啟動,因此直接返回 if (smap.mStartingBackground.size() >= mMaxStartingBackground) { // Something else is starting, delay! Slog.i(TAG_SERVICE, "Delaying start of: " + r); smap.mDelayedStartList.add(r); r.delayed = true; return r.name; } addToStarting = true; } else if (proc.mState.getCurProcState() >= ActivityManager.PROCESS_STATE_SERVICE) { // app 處于后臺,但是只運行了 service 和 receiver addToStarting = true; } } // allowBackgroundActivityStarts 目前為 false if (allowBackgroundActivityStarts) { r.allowBgActivityStartsOnServiceStart(backgroundActivityStartsToken); } // 2. 繼續下一階段的啟動流程 ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); return cmp; }
現在沒有 Service 的啟動限制了,是時候正式啟動了,首先更新 ServiceRecord 數據,這里要注意,使用 ServiceRecord#pendingStarts 保存了要發送給 Service 的參數。
接下來有一段關于延時 Service 以及監聽 后臺Service 相關的代碼,這是為了優化 Service 啟動過多的情況,而做出的一個優化。對于系統優化的開發者,你可能要重點關注一下,本文先不管這個優化功能。
然后繼續看下一步的啟動流程
// ActiveServices.java ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting) throws TransactionTooLargeException { synchronized (mAm.mProcessStats.mLock) { final ServiceState stracker = r.getTracker(); if (stracker != null) { stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity); } } // 是否調用 onStart() r.callStart = false; final int uid = r.appInfo.uid; final String packageName = r.name.getPackageName(); final String serviceName = r.name.getClassName(); FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName, serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__START); mAm.mBatteryStatsService.noteServiceStartRunning(uid, packageName, serviceName); // 1. 拉起 Service String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false /* whileRestarting */, false /* permissionsReviewRequired */, false /* packageFrozen */, true /* enqueueOomAdj */); /* Will be a no-op if nothing pending */ mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE); if (error != null) { return new ComponentName("!!", error); } // 優化后臺Service啟動過多的情況 if (r.startRequested && addToStarting) { boolean first = smap.mStartingBackground.size() == 0; smap.mStartingBackground.add(r); // 設置一個超時時間 r.startingBgTimeout = SystemClock.uptimeMillis() + mAm.mConstants.BG_START_TIMEOUT; if (first) { smap.rescheduleDelayedStartsLocked(); } } else if (callerFg || r.fgRequired) { smap.ensureNotStartingBackgroundLocked(r); } return r.name; }
很簡單,這一步主要是通過 bringUpServiceLocked() 拉起 Service
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen, boolean enqueueOomAdj) throws TransactionTooLargeException { // 1. 如果 Service 已經啟動過一次,直接發送發送參數給 Service 即可 // 因為只有 Service 啟動過,r.app 才不為 null if (r.app != null && r.app.getThread() != null) { sendServiceArgsLocked(r, execInFg, false); return null; } // ... final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0; final String procName = r.processName; HostingRecord hostingRecord = new HostingRecord("service", r.instanceName); ProcessRecord app; if (!isolated) { // 1. Service 進程存在,但是 Service 還沒有啟動過,那么通知宿主進程啟動 Service // 由于前面已經判斷過 r.app 不為 null 的情況,所以這里處理的就是 Service 沒有啟動過的情況 app = mAm.getProcessRecordLocked(procName, r.appInfo.uid); if (app != null) { // 進程粗在 final IApplicationThread thread = app.getThread(); final int pid = app.getPid(); final UidRecord uidRecord = app.getUidRecord(); if (thread != null) { // attach application 已經完成 try { app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats); realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg, enqueueOomAdj); return null; } // ... } } } else { // ... } // 3. Service 所在的進程沒有運行,那么要 fork 一個進程 if (app == null && !permissionsReviewRequired && !packageFrozen) { // TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service // was initiated from a notification tap or not. if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags, hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated)) == 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.isolatedProc = app; } } // 對于前臺 Service 的 app, 暫時添加到省電模式白名單中 if (r.fgRequired) { mAm.tempAllowlistUidLocked(r.appInfo.uid, SERVICE_START_FOREGROUND_TIMEOUT, REASON_SERVICE_LAUNCH, "fg-service-launch", TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, r.mRecentCallingUid); } // 3.1 mPendingServices 保存正在等待進程起來的 Service // 進程起來后,執行 attach application 過程時,會自動啟動這個 Service if (!mPendingServices.contains(r)) { mPendingServices.add(r); } if (r.delayedStop) { // ... } return null; }
拉起一個 Service 要分好幾種情況
如果 Service 已經啟動過,那么直接發送參數給 Service 去執行任務即可。這是最簡單的一種情況。
如果 Service 沒有啟動過,但是 Service 的宿主進程是存在的,那么通知宿主進程創建 Service,然后發送參數給它。
如果 Service 的宿主進程不存在,那么得先 fork 一個進程,然后用 mPendingServices 保存這個等待進程啟動的 Service。當進程起來后,在執行 attach application 的過程中,AMS 會自動完成 Service 的啟動流程。
第3種情況,明顯是包含前面兩種情況,因此下面直接分析這個過程。
Service 的宿主進程起來后,在執行 attach application 的過程中,AMS 會自動啟動那些等待進程起來的 Service,如下
private boolean attachApplicationLocked(@NonNull IApplicationThread thread, int pid, int callingUid, long startSeq) { // ... try { // ... // 進程初始化 if (app.getIsolatedEntryPoint() != null) { // This is an isolated process which should just call an entry point instead of // being bound to an application. thread.runIsolatedEntryPoint( app.getIsolatedEntryPoint(), app.getIsolatedEntryPointArgs()); } else if (instr2 != null) { thread.bindApplication(processName, appInfo, providerList, instr2.mClass, profilerInfo, instr2.mArguments, instr2.mWatcher, instr2.mUiAutomationConnection, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.isPersistent(), new Configuration(app.getWindowProcessController().getConfiguration()), app.getCompat(), getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, autofillOptions, contentCaptureOptions, app.getDisabledCompatChanges(), serializedSystemFontMap); } else { thread.bindApplication(processName, appInfo, providerList, null, profilerInfo, null, null, null, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.isPersistent(), new Configuration(app.getWindowProcessController().getConfiguration()), app.getCompat(), getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, autofillOptions, contentCaptureOptions, app.getDisabledCompatChanges(), serializedSystemFontMap); } // ... } catch (Exception e) { // ... return false; } // ... if (!badApp) { try { // 啟動等待進程的 Service didSomething |= mServices.attachApplicationLocked(app, processName); checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked"); } } // ... return true; }
最終調用 ActiveServices#attachApplicationLocked() 完成 Service 的啟動
// ActiveServices.java boolean attachApplicationLocked(ProcessRecord proc, String processName) throws RemoteException { boolean didSomething = false; // mPendingServices 保存的就是那些等待進程起來的 Service if (mPendingServices.size() > 0) { ServiceRecord sr = null; try { // 遍歷 mPendingServices ,啟動屬于該進程的所有 Service for (int i=0; i<mPendingServices.size(); i++) { sr = mPendingServices.get(i); // 過濾掉不屬于這個進程的 Service if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid || !processName.equals(sr.processName))) { continue; } final IApplicationThread thread = proc.getThread(); final int pid = proc.getPid(); final UidRecord uidRecord = proc.getUidRecord(); mPendingServices.remove(i); i--; proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode, mAm.mProcessStats); // 啟動 Service realStartServiceLocked(sr, proc, thread, pid, uidRecord, sr.createdFromFg, true); didSomething = true; if (!isServiceNeededLocked(sr, false, false)) { bringDownServiceLocked(sr, true); } mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE); } } catch (RemoteException e) { // ... } } // 處理進程重啟而需要重啟的 Service if (mRestartingServices.size() > 0) { // ... } return didSomething; }
終于,我們看到了我們想看到的東西,先從 mPendingServices 中過濾掉不屬于進程的 Service,然后啟動它
// ActiveServices.java private void realStartServiceLocked(ServiceRecord r, ProcessRecord app, IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg, boolean enqueueOomAdj) throws RemoteException { // ... // 注意,r.app 的值才不為 null,也就是 ServiceRecord#app 不為 null // 因此可以通過 r.app 判斷 Service 是否已經啟動過 r.setProcess(app, thread, pid, uidRecord); r.restartTime = r.lastActivity = SystemClock.uptimeMillis(); final ProcessServiceRecord psr = app.mServices; final boolean newService = psr.startService(r); // 注意,這里實現了 Service 超時的功能 bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */); mAm.updateLruProcessLocked(app, false, null); updateServiceForegroundLocked(psr, /* oomAdj= */ false); // Force an immediate oomAdjUpdate, so the client app could be in the correct process state // before doing any service related transactions mAm.enqueueOomAdjTargetLocked(app); mAm.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_START_SERVICE); boolean created = false; try { // ... // 1. 通知宿主進程創建Service thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo), app.mState.getReportedProcState()); r.postNotification(); created = true; // ... } catch (DeadObjectException e) { // ... } finally { if (!created) { // ... } } // ... // 2. 發送參數給 Service sendServiceArgsLocked(r, execInFg, true); if (r.delayed) { // ... } if (r.delayedStop) { // ... } }
現在,正式與宿主進程交互來啟動 Service
首先通過 IApplicationThread#scheduleCreateService() 來通知宿主進程創建 Service。
然后通過 IApplicationThread#scheduleServiceArgs() 把參數發送給宿主進程,宿主進程會把參數發送給 Service 執行任務。
宿主進程接收到創建 Service 的指令后,會通過 Handler 發送一個消息,最終調用如下方法
// ActivityThread.java private void handleCreateService(CreateServiceData data) { unscheduleGcIdler(); LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; try { // 創建 Application,并調用 Application#onCreate() Application app = packageInfo.makeApplication(false, mInstrumentation); final java.lang.ClassLoader cl; if (data.info.splitName != null) { cl = packageInfo.getSplitClassLoader(data.info.splitName); } else { cl = packageInfo.getClassLoader(); } // 創建 Service service = packageInfo.getAppFactory() .instantiateService(cl, data.info.name, data.intent); ContextImpl context = ContextImpl.getImpl(service .createServiceBaseContext(this, packageInfo)); if (data.info.splitName != null) { context = (ContextImpl) context.createContextForSplit(data.info.splitName); } if (data.info.attributionTags != null && data.info.attributionTags.length > 0) { final String attributionTag = data.info.attributionTags[0]; context = (ContextImpl) context.createAttributionContext(attributionTag); } context.getResources().addLoaders( app.getResources().getLoaders().toArray(new ResourcesLoader[0])); context.setOuterContext(service); service.attach(context, this, data.info.name, data.token, app, ActivityManager.getService()); // 執行 Service#onCreate() service.onCreate(); mServicesData.put(data.token, data); mServices.put(data.token, service); // 通知服務端Service創建成功 try { ActivityManager.getService().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } catch (Exception e) { // ... } }
宿主繼承會先創建 Application,并執行 Application#onCreate(),然后創建 Service,并執行 Service#onCreate(),最后通知服務端 Service 創建成功。這一套流程,簡直行云流水。
宿主進程接收到服務端發送過來的 Service 啟動參數后,最終調用如下方法
// ActivityThread.java private void handleServiceArgs(ServiceArgsData data) { CreateServiceData createData = mServicesData.get(data.token); Service s = mServices.get(data.token); if (s != null) { try { if (data.args != null) { data.args.setExtrasClassLoader(s.getClassLoader()); data.args.prepareToEnterProcess(isProtectedComponent(createData.info), s.getAttributionSource()); } int res; if (!data.taskRemoved) { // 調用 Service#onStartCommand() 接收參數,并執行任務 // 注意,這里是在主線程 res = s.onStartCommand(data.args, data.flags, data.startId); } else { s.onTaskRemoved(data.args); res = Service.START_TASK_REMOVED_COMPLETE; } QueuedWork.waitToFinish(); try { // 把 Service#onStartCommand() 的執行結果反饋給服務端 // 因為這個結果影響到 Service 重啟的方式 ActivityManager.getService().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_START, data.startId, res); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } catch (Exception e) { // ... } } }
當宿主進程收到 Service 的啟動參數后,調用 Service#onStartCommand() 接收參數,并執行任務。
注意,Service#onStartCommand() 是在主線程中執行,其實四大組件的生命周期都是在主線程中執行的,這一點大家要牢記。
然后把 Service#onStartCommand() 的返回結果返回給服務端。這個方法的返回結果很重要,它影響了 Service 重啟的方式。這里簡單展示下結果的處理流程
// ActiveServices.java void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res, boolean enqueueOomAdj) { boolean inDestroying = mDestroyingServices.contains(r); if (r != null) { if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) { // 注意了,ServiceRecord#callStart , 是在 Service 執行完 onStartCommand() 并反饋結果后,才設置為 true r.callStart = true; switch (res) { case Service.START_STICKY_COMPATIBILITY: // Service 會重啟,但是不會收到原來的啟動參數 case Service.START_STICKY: { // 注意最后一個參數為 true // 它表示從已發送的參數 Service#deliveredStarts 中移除此次執行的參數 // 因為Service重啟時,不需要這個參數 r.findDeliveredStart(startId, false, true); // Don't stop if killed. r.stopIfKilled = false; break; } case Service.START_NOT_STICKY: { // ... break; } // Service 會重啟,并且也會收到原來的啟動參數 case Service.START_REDELIVER_INTENT: { // 注意最后一個參數 true // 它表示不需要從已發送的參數 Service#deliveredStarts 中移除此次執行的參數 // 因此Service重啟時,需要這個參數 ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false); if (si != null) { si.deliveryCount = 0; si.doneExecutingCount++; // Don't stop if killed. r.stopIfKilled = true; } break; } // ... } if (res == Service.START_STICKY_COMPATIBILITY) { r.callStart = false; } } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) { // ... } final long origId = Binder.clearCallingIdentity(); // 主要更新 oom ajd serviceDoneExecutingLocked(r, inDestroying, inDestroying, enqueueOomAdj); Binder.restoreCallingIdentity(origId); } }
這里就涉及到 Service 的基本功,上面展示了兩個返回值的處理過程,這兩個返回值都表示,由于內存不足而殺掉的 Service,當內存充足時,會重啟 Service,但是一個返回值需要再次發送之前的啟動參數,而另外一個返回值不需要。
對于需要發送參數的返回值,不需要從 Service#deliveredStart 列表中刪除已經發送的參數,因為 Service 重啟時,需要發送它。相反,就需要從列表中刪除已經發送的參數,因為 Service 重啟不再需要它了。
讀到這里,這篇“ActivityManagerService之Service怎么啟動”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。