日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

framework层Service的基础知识和常见问题

發布時間:2023/12/9 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 framework层Service的基础知识和常见问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡介

framework層Service相關的邏輯主要在ActiveServices類中。

分類

  • 啟動方式
    • start方式啟動:onCreate() → onStartCommand() → stopService → onDestroy()
    • bind方式啟動:onCreate() → onBind() → unbindService → onUnbind() → onDestroy()
  • 運行方式(優先級)
    • 前臺Service:會在通知欄顯示通知,優先級較高;anr超時時間為20s
    • 后臺Service:沒有通知,優先級較低;anr超時時間為200s
  • 常見的adb命令

  • 啟動指定Service
    • adb shell am startservice -n serviceName
  • 查看Service的dump信息
    • adb shell dumpsys activity services
  • Service啟動失敗原因

    一般都有如下log打印:

    Slog.w(TAG, “Background start not allowed: service “+ service(啟動的Intent) + " to " + r.shortInstanceName(Service名字 )+ " from pid=” + callingPid(調用方的pid) + " uid=” + callingUid(調用方的uid)+ " pkg=" + callingPackage(調用方的包名) + " startFg?=" + fgRequired(是否是前臺Service));

    具體的邏輯在ActiveServices的startServiceLocked方法里

    啟動后臺Service
    // startRequested表示是否start方式啟動(此處首次啟動暫未賦值所以為false,且service stop后會賦值為false),fgRequired表示是否前臺Serviceif (forcedStandby || (!r.startRequested && !fgRequired)) {final int allowed = mAm.getAppStartModeLocked(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);......}
    后臺啟動Service && 不在電池優化白名單

    如果當前app的uid不是active(處于后臺),即使啟動的是前臺Service,也可能會失敗,例如:

    ActivityManager: Background start not allowed: service Intent { act=geofence_trigered cmp=com.xiaomi.smarthome/.scene.activity.GeoActionService (has extras) } to com.xiaomi.smarthome/.scene.activity.GeoActionService from pid=9233 uid=10270 pkg=com.xiaomi.smarthome startFg?=true

    原理代碼如下,下面的就是上面1中列出的代碼:

    final boolean bgLaunch = !mAm.isUidActiveLocked(r.appInfo.uid);boolean forcedStandby = false;if (bgLaunch && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {forcedStandby = true;}

    另外,如果進程的procState < 8 ,他在后臺保留1min后就會進入idle狀態,相應的Service也會被stop

    Service發生ANR的原因

    10s內未調用startForeground導致的ANR

    I am_anr : [0,16206,com.android.mms,952745573,Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{3201640 u0 com.android.mms/com.xiaomi.mms.transaction.MxActivateService}]

    以startForegroundService方式啟動的Service,則需要在10s內調用startForeground方法,否則會發生ANR。一般會將該方法放到Service的onStartCommand中去執行,因為這種類型的ANR超時時間是從framework層開始通知app端執行Service的onStartCommand開始計算的。如果將該方法放到Service的onCreate中去執行,onStartCommand方法返回值是START_NOT_STICKY,進程被殺后Service重啟不會回調onCreate方法,但是還會走ANR超時邏輯。

    private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg, boolean oomAdjusted) throws TransactionTooLargeException {.......while (r.pendingStarts.size() > 0) {.....if (r.fgRequired && !r.fgWaiting) {if (!r.isForeground) {scheduleServiceForegroundTransitionTimeoutLocked(r);} }

    前臺Service

    設置前臺Service

    如果需要提高進程的優先級,避免由于低內存或其他廠商定制策略被系統查殺,可以設置Service為前臺Service,app端通過調用startForeground方法實現。具體流程如下圖1:

    app端Service中調用startForeground
    • 傳入Notication的id和Notification的實例,前臺Service的類型
    • 如果是用startForegroundService方式啟動的,則需要在service的onStartCommand方法中調用該startForeground方法,避免anr
    //Service.java public final void startForeground(int id, @NonNull Notification notification, @ForegroundServiceType int foregroundServiceType) {try {mActivityManager.setServiceForeground(new ComponentName(this, mClassName), mToken, id,notification, 0, foregroundServiceType);} catch (RemoteException ex) {} }
    AMS中作中轉,調用ActiveServices.java中的方法(因為app端進程只有AMS或ATMS的binder對象)
    • 該操作持有AMS大鎖
    • 真正實現地方在ActiveServices中
    // AMS.java @Override public void setServiceForeground(ComponentName className, IBinder token,int id, Notification notification, int flags, int foregroundServiceType) {synchronized(this) {mServices.setServiceForegroundLocked(className, token, id, notification, flags, foregroundServiceType);} }
    具體實現邏輯

    下面可能會省略部分代碼

    public void setServiceForegroundLocked(ComponentName className, IBinder token,int id, Notification notification, int flags, int foregroundServiceType) {final int userId = UserHandle.getCallingUserId();// 清除calling uid,避免uid權限校驗通不過,清除后callingUid就時system server的uid 1000final long origId = Binder.clearCallingIdentity();try {// 通過component ,token ,userId獲取到service recordServiceRecord r = findServiceLocked(className, token, userId);if (r != null) {setServiceForegroundInnerLocked(r, id, notification, flags, foregroundServiceType);}} finally {// 恢復之前的calling uidBinder.restoreCallingIdentity(origId);} }
    • 傳入的notification id為0表示當前service退出前臺狀態
    • P或P之后前臺service需要申請FOREGROUND_SERVICE權限
    • 移除前臺service的10s超時消息
    • 更新isForeground為true
    • 通知異步執行入隊列
    /*** @param id Notification ID. Zero === exit foreground state for the given service.*/private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,Notification notification, int flags, int foregroundServiceType) {if (id != 0) {if (notification == null) {throw new IllegalArgumentException("null notification");}// Instant apps need permission to create foreground services.if (r.appInfo.isInstantApp()) {.......} else {// P或P之后前臺service需要申請FOREGROUND_SERVICE權限if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {mAm.enforcePermission(android.Manifest.permission.FOREGROUND_SERVICE,r.app.pid, r.appInfo.uid, "startForeground");}// 一般傳入的service type默認為FOREGROUND_SERVICE_TYPE_MANIFEST,而manifestType默認為0(none)int manifestType = r.serviceInfo.getForegroundServiceType();if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_MANIFEST) {foregroundServiceType = manifestType;}// 這種異常一般較少if ((foregroundServiceType & manifestType) != foregroundServiceType) {throw new IllegalArgumentException("foregroundServiceType "+ String.format("0x%08X", foregroundServiceType)+ " is not a subset of foregroundServiceType attribute "+ String.format("0x%08X", manifestType)+ " in service element of manifest file");}// 后臺啟動的前臺Service不得擁有“使用中權限”,下面有講到if (!r.mAllowWhileInUsePermissionInFgs) {Slog.w(TAG,"Foreground service started from background can not have "+ "location/camera/microphone access: service "+ r.shortInstanceName);}}boolean alreadyStartedOp = false;boolean stopProcStatsOp = false;//移除前臺service 10s的超時消息。重置fgRequired 和 fgWaitingif (r.fgRequired) { r.fgRequired = false;r.fgWaiting = false;alreadyStartedOp = stopProcStatsOp = true;mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);}try {boolean ignoreForeground = false;......// service的app不在電池優化白名單內&不是前臺app啟動,ignoreForeground賦值為trueif (!ignoreForeground&& !appIsTopLocked(r.appInfo.uid)&& appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {Slog.w(TAG,"Service.startForeground() not allowed due to bg restriction: service "+ r.shortInstanceName);updateServiceForegroundLocked(r.app, false);ignoreForeground = true;}if (!ignoreForeground) {// 取消可能存在的notificationif (r.foregroundId != id) {cancelForegroundNotificationLocked(r);r.foregroundId = id;}// 給service的notification相關變量賦值notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;r.foregroundNoti = notification;r.foregroundServiceType = foregroundServiceType;// 前臺service第一次設置會為falseif (!r.isForeground) {final ServiceMap smap = getServiceMapLocked(r.userId);if (smap != null) {// 統計每個app前臺service的數量ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName);if (active == null) {active = new ActiveForegroundApp();active.mPackageName = r.packageName;active.mUid = r.appInfo.uid;active.mShownWhileScreenOn = mScreenOn;if (r.app != null && r.app.uidRecord != null) {active.mAppOnTop = active.mShownWhileTop =r.app.uidRecord.getCurProcState()<= ActivityManager.PROCESS_STATE_TOP;}active.mStartTime = active.mStartVisibleTime= SystemClock.elapsedRealtime();smap.mActiveForegroundApps.put(r.packageName, active);requestUpdateActiveForegroundAppsLocked(smap, 0);}active.mNumActive++;}// 設置isForeground為true,第二次執行該方法后不必執行這些代碼,且表示當前service在前臺r.isForeground = true;........}// 每次執行該方法都會重新post通知,異步執行r.postNotification();if (r.app != null) {// 更新進程優先級updateServiceForegroundLocked(r.app, true);}getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);mAm.notifyPackageUse(r.serviceInfo.packageName,PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);} else {}} finally {.......}} else {// notification id為0 ,是取消前臺service的流程,下面講}
    發送通知
    • 通知入隊等操作異步進行,避免死鎖
    • 通知沒有對應的icon,打印警告信息,并給設置一個簡陋的通知模板,告知用戶當前正在運行該進程
      V/ActivityManager: Attempted to start a foreground service (com.mfashiongallery.emag/.lks.minusonescreen.overlay.OverlayService) with a broken notification (no icon: Notification(channel=gallery_overlay_windows shortcut=null contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 vis=PRIVATE))
    // ServiceRecord.javapublic void postNotification() {final int appUid = appInfo.uid;final int appPid = app.pid;if (foregroundId != 0 && foregroundNoti != null) {// Do asynchronous communication with notification manager to// avoid deadlocks.final String localPackageName = packageName;final int localForegroundId = foregroundId;final Notification _foregroundNoti = foregroundNoti;final ServiceRecord record = this;ams.mHandler.post(new Runnable() {public void run() {NotificationManagerInternal nm = LocalServices.getService(NotificationManagerInternal.class);if (nm == null) {return;}Notification localForegroundNoti = _foregroundNoti;try {if (localForegroundNoti.getSmallIcon() == null) {// 通知沒有對應icon,打印警告信息Slog.v(TAG, "Attempted to start a foreground service (" + shortInstanceName + ") with a broken notification (no icon: " + localForegroundNoti + ")");CharSequence appName = appInfo.loadLabel(ams.mContext.getPackageManager());if (appName == null) {appName = appInfo.packageName;}Context ctx = null;try {ctx = ams.mContext.createPackageContextAsUser(appInfo.packageName, 0, new UserHandle(userId));// 重新構建通知相關信息Notification.Builder notiBuilder = new Notification.Builder(ctx,localForegroundNoti.getChannelId());// 設置icon為app應用信息的iconnotiBuilder.setSmallIcon(appInfo.icon);// mark as foregroundnotiBuilder.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true);// 構建pendingIntentrunningIntent.setData(Uri.fromParts("package", appInfo.packageName, null));PendingIntent pi = PendingIntent.getActivityAsUser(ams.mContext, 0,runningIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,UserHandle.of(userId));// 設置系統的通知顏色notiBuilder.setColor(ams.mContext.getColor(com.android.internal.R.color.system_notification_accent_color));// 設置通知標題為“xxx 正在運行”notiBuilder.setContentTitle(ams.mContext.getString(com.android.internal.R.string.app_running_notification_title,appName));// 設置通知的內容"點按即可了解詳情或停止應用",miui定制國內版本為"可能導致系統卡頓,降低待機時間,點按關閉"// MIUI MOD START:// notiBuilder.setContentText(// ams.mContext.getString(// com.android.internal.R.string// .app_running_notification_text,// appName));if (miui.os.Build.IS_INTERNATIONAL_BUILD) {notiBuilder.setContentText(ams.mContext.getString(com.android.internal.R.string.app_running_notification_text,appName));} else {notiBuilder.setContentText(ams.mContext.getString(com.android.internal.R.string.app_running_notification_tip_text,appName));}// ENDnotiBuilder.setContentIntent(pi);localForegroundNoti = notiBuilder.build();} catch (PackageManager.NameNotFoundException e) {}}if (nm.getNotificationChannel(localPackageName, appUid,localForegroundNoti.getChannelId()) == null) {int targetSdkVersion = Build.VERSION_CODES.O_MR1;try {final ApplicationInfo applicationInfo =ams.mContext.getPackageManager().getApplicationInfoAsUser(appInfo.packageName, 0, userId);targetSdkVersion = applicationInfo.targetSdkVersion;} catch (PackageManager.NameNotFoundException e) {}if (targetSdkVersion >= Build.VERSION_CODES.O_MR1) {throw new RuntimeException("invalid channel for service notification: "+ foregroundNoti);}}if (localForegroundNoti.getSmallIcon() == null) {// Notifications whose icon is 0 are defined to not show// a notification, silently ignoring it. We don't want to// just ignore it, we want to prevent the service from// being foreground.throw new RuntimeException("invalid service notification: "+ foregroundNoti);}// 通知入隊nm.enqueueNotification(localPackageName, localPackageName,appUid, appPid, null, localForegroundId, localForegroundNoti,userId);foregroundNoti = localForegroundNoti; // save it for amending next time} catch (RuntimeException e) {Slog.w(TAG, "Error showing notification for service", e);// 出現問題會stop server,阿里巴巴app就經常因為這個原因被殺ams.mServices.killMisbehavingService(record,appUid, appPid, localPackageName);}}});}}

    取消前臺service

    app端調用

    取消前臺service的流程和設置前臺service的流程基本相同,傳入的參數不同,如圖2

    • 傳入的notification id為0
    • notifications為null
    • flags為1表示移除通知,0表示不移除
    public final void stopForeground(@StopForegroundFlags int flags) {try {mActivityManager.setServiceForeground(new ComponentName(this, mClassName), mToken, 0, null,flags, 0);} catch (RemoteException ex) {}}
    ActiveServices中的流程

    setServiceForegroundInnerLocked方法中notification id為0的else邏輯

    • 自減mActiveForegroundApps中對應app的mNumActive,代表當前app的前臺service -1
    • 如果app的mNumActive <= 0,則將該app從mActiveForegroundApps中移除,并更新adj
    • 傳入的flags為1,則需要取消notification
    // 取消前臺service,如果當前service是前臺serviceif (r.isForeground) {final ServiceMap smap = getServiceMapLocked(r.userId);if (smap != null) {// 更新mActiveForegroundApps中對應app的mNumActivedecActiveForegroundAppLocked(smap, r);}// 重置isForeground屬性r.isForeground = false; .......if (r.app != null) {mAm.updateLruProcessLocked(r.app, false, null);updateServiceForegroundLocked(r.app, true);}}// 如果傳入的flags為1,則要取消通知if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {cancelForegroundNotificationLocked(r);r.foregroundId = 0;r.foregroundNoti = null;} else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {r.stripForegroundServiceFlagFromNotification();if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {r.foregroundId = 0;r.foregroundNoti = null;}}}
    移除通知
    private void cancelForegroundNotificationLocked(ServiceRecord r) {// notification id不為0if (r.foregroundId != 0) {// First check to see if this app has any other active foreground services// with the same notification ID. If so, we shouldn't actually cancel it,// because that would wipe away the notification that still needs to be shown// due the other service.ServiceMap sm = getServiceMapLocked(r.userId);if (sm != null) {for (int i = sm.mServicesByInstanceName.size() - 1; i >= 0; i--) {ServiceRecord other = sm.mServicesByInstanceName.valueAt(i);if (other != r && other.foregroundId == r.foregroundId&& other.packageName.equals(r.packageName)) {// Found one! Abort the cancel.return;}}}r.cancelNotification();}} public void cancelNotification() {// 異步執行,避免死鎖final String localPackageName = packageName;final int localForegroundId = foregroundId;final int appUid = appInfo.uid;final int appPid = app != null ? app.pid : 0;ams.mHandler.post(new Runnable() {public void run() {NotificationManagerInternal nm = LocalServices.getService(NotificationManagerInternal.class);if (nm == null) {return;}try {nm.cancelNotification(localPackageName, localPackageName, appUid, appPid,null, localForegroundId, userId);} catch (RuntimeException e) {Slog.w(TAG, "Error canceling notification for service", e);}}});}

    后臺啟動的前臺Service不得擁有使用中權限

    android會限制從后臺啟動的前臺service擁有“使用中”權限,包括位置、攝像頭、麥克風等,否則會打印出如下的警告信息。
    W ActivityManager: Foreground service started from background can not have location/camera/microphone access: service com.mi.health/.scenerecognition.SceneForegroundService

    start或bind方式啟動都會去給mAllowWhileInUsePermissionInFgs賦值

    if (!r.mAllowWhileInUsePermissionInFgs) {r.mAllowWhileInUsePermissionInFgs =shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid,callingUid, service, r, allowBackgroundActivityStarts);}

    豁免情況

    • FGS由ROOT_UID,SYSTEM_UID,NFC_UID、SHELL_UID等啟動。
    • CallingUid 聲明START_ACTIVITIES_FROM_BACKGROUND權限(hide權限)
    • FGS由IME或其他可見應用啟動。
    • 在while-in-use白名單內:AttentionService, SystemCaptionService(for live caption)
    private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,int callingPid, int callingUid, Intent intent, ServiceRecord r,boolean allowBackgroundActivityStarts) {// 一般后臺啟動前臺service限制打開的if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {return true;}// 一般為falseif (allowBackgroundActivityStarts) {return true;}//部分uid豁免boolean isCallerSystem = false;final int callingAppId = UserHandle.getAppId(callingUid);switch (callingAppId) {case ROOT_UID:case SYSTEM_UID:case NFC_UID:case SHELL_UID:isCallerSystem = true;break;default:isCallerSystem = false;break;}if (isCallerSystem) {return true;}if (r.app != null) {ActiveInstrumentation instr = r.app.getActiveInstrumentation();if (instr != null && instr.mHasBackgroundActivityStartsPermission) {return true;}}final boolean hasAllowBackgroundActivityStartsToken = r.app != null? !r.app.mAllowBackgroundActivityStartsTokens.isEmpty() : false;if (hasAllowBackgroundActivityStartsToken) {return true;}// app聲明了START_ACTIVITIES_FROM_BACKGROUND權限豁免if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)== PERMISSION_GRANTED) {return true;}// app在top自然允許final boolean isCallingUidTopApp = appIsTopLocked(callingUid);if (isCallingUidTopApp) {return true;}// Does the calling UID have any visible activity?final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);if (isCallingUidVisible) {return true;}final boolean isWhiteListedPackage =mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage);if (isWhiteListedPackage) {return true;}// Is the calling UID a device owner app?final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid);if (isDeviceOwner) {return true;}return false;}

    后臺Service

    后臺Service管控

    android對后臺進程管控比較嚴格,app退到后臺超過1min就會進入idle狀態,并嘗試stop相關的Service,所以我們可能會經常見到如下log:

    08-27 20:08:12.038 1588 5729 I am_uid_active: 10135

    08-27 20:09:13.517 1588 2114 I am_uid_idle: 10135

    08-27 20:09:13.517 1588 2114 I am_stop_idle_service: [10135,com.android.htmlviewer/com.android.settings.services.MemoryOptimizationService]

    為了便于理解,以下代碼都只是節選方法中與之相關的一部分代碼

    在OomAdjuster.java的updateUidsLocked方法中發送IDLE_UIDS_MSG消息
    • isProcStateBackground 是判斷當前UidRecord的procState值是否小于8(PROCESS_STATE_TRANSIENT_BACKGROUND)
    • 后臺Service的procState一般是10(PROCESS_STATE_SERVICE)
    • 如果當前進程跟top進程有綁定(如處于top狀態的進程訪問當前進程的provider等),那他的procState為3(PROCESS_STATE_BOUND_TOP)
    • 如果當前UidRect處于后臺且不在臨時白名單內,但是上次不處于后臺或者在臨時白名單內,就會在1min(BACKGROUND_SETTLE_TIME)后將當前的UidRect idle
    private void updateUidsLocked(ActiveUids activeUids, final long nowElapsed) {for (int i = activeUids.size() - 1; i >= 0; i--) {final UidRecord uidRec = activeUids.valueAt(i);int uidChange = UidRecord.CHANGE_PROCSTATE;// 判斷當前procState是否處于后臺或者不在臨時白名單內 if (ActivityManager.isProcStateBackground(uidRec.getCurProcState()) && !uidRec.curWhitelist) {// 上次procState不處于后臺或者在臨時白名單內if (!ActivityManager.isProcStateBackground(uidRec.setProcState) || uidRec.setWhitelist) {// 這里傳入的elapsedRealtime而不是uptimeMillis,谷歌代碼曾出過問。uidRec.lastBackgroundTime = nowElapsed;if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {// 發送uid idle消息,處于后臺時間BACKGROUND_SETTLE_TIME是1minmService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,mConstants.BACKGROUND_SETTLE_TIME);}}
    在AMS的MainHandler處理該消息并調用idleUids去設置對應的uid idle
    • idle處理邏輯是在system server的main線程中進行的
    • idle時間是在處于后臺或不在臨時白名單的1min后
    • 遍歷active uid時,時間到了就執行doStopUidLocked
    • 時間未到計算更新為最近的一次idle時間并再次發送IDLE_UIDS_MSG消息
    final class MainHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {......case IDLE_UIDS_MSG: {idleUids();} break; @GuardedBy("mService")void idleUidsLocked() {final int N = mActiveUids.size();if (N <= 0) {return;}final long nowElapsed = SystemClock.elapsedRealtime();// 得到起始后臺時間點邊界,大于這個值此時不會被idlefinal long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME;long nextTime = 0;for (int i = N - 1; i >= 0; i--) {final UidRecord uidRec = mActiveUids.valueAt(i);final long bgTime = uidRec.lastBackgroundTime;// 上次處于后臺時間 > 0表示當前uid處于后臺if (bgTime > 0 && !uidRec.idle) {// 處于后臺時間超過1minif (bgTime <= maxBgTime) {// 打印event log : am_uid_idleEventLogTags.writeAmUidIdle(uidRec.uid);// 給idle標志位賦值uidRec.idle = true;uidRec.setIdle = true;// stop 后臺ServicemService.doStopUidLocked(uidRec.uid, uidRec);} else {// 計算下次idle消息時間點if (nextTime == 0 || nextTime > bgTime) {nextTime = bgTime;}}}} // 發送下次idle消息if (nextTime > 0) {mService.mHandler.removeMessages(IDLE_UIDS_MSG);mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);}}
    stop后臺service的邏輯
    • AMS的doStopUidLocked方法會先stop 后臺service,再去更新uid并分發uid的change
    • stopInBackgroundLocked回調后,service不一定會真被stop
    //AMS.java@GuardedBy("this")final void doStopUidLocked(int uid, final UidRecord uidRec) {mServices.stopInBackgroundLocked(uid);enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);} // ActiveServices.javavoid stopInBackgroundLocked(int uid) {// 獲取該uid下的所有serviceServiceMap services = mServiceMap.get(UserHandle.getUserId(uid));ArrayList<ServiceRecord> stopping = null;if (services != null) {// 遍歷所獲取到的servicesfor (int i = services.mServicesByInstanceName.size() - 1; i >= 0; i--) {ServiceRecord service = services.mServicesByInstanceName.valueAt(i);// start方式啟動的serviceif (service.appInfo.uid == uid && service.startRequested) {// 獲取當前service的start 權限,miui會多傳入callingPackage,對諸如小米推送拉起的service放開權限if (mAm.getAppStartModeLocked(service.appInfo.uid, service.packageName,service.appInfo.targetSdkVersion, -1, false, false, false)!= ActivityManager.APP_START_MODE_NORMAL) {if (stopping == null) {stopping = new ArrayList<>();}String compName = service.shortInstanceName;// 打印event log am_stop_idle_serviceEventLogTags.writeAmStopIdleService(service.appInfo.uid, compName);// 打印idle相關的system logStringBuilder sb = new StringBuilder(64);sb.append("Stopping service due to app idle: ");UserHandle.formatUid(sb, service.appInfo.uid);sb.append(" ");TimeUtils.formatDuration(service.createRealTime- SystemClock.elapsedRealtime(), sb);sb.append(" ");sb.append(compName);Slog.w(TAG, sb.toString());// 將當前service添加進stoppping列表stopping.add(service);// 如果當前app不在電池優化白名單中,那么前臺service也是有可能不允許啟動的,所以要取消notificationif (appRestrictedAnyInBackground(service.appInfo.uid, service.packageName)) {cancelForegroundNotificationLocked(service);}}}}if (stopping != null) {for (int i=stopping.size()-1; i>=0; i--) {ServiceRecord service = stopping.get(i);service.delayed = false;services.ensureNotStartingBackgroundLocked(service);// stop servicestopServiceLocked(service);}}}} private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn,boolean hasConn) {if (isServiceNeededLocked(r, knowConn, hasConn)) {return;}// 如果有新拉起service的需求,本次不會stop該serviceif (mPendingServices.contains(r)) {return;}bringDownServiceLocked(r);}

    總結

    以上是生活随笔為你收集整理的framework层Service的基础知识和常见问题的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。