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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2023/12/9 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 framework层Service的基础知识和常见问题 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

簡(jiǎn)介

framework層Service相關(guān)的邏輯主要在ActiveServices類(lèi)中。

分類(lèi)

  • 啟動(dòng)方式
    • start方式啟動(dòng):onCreate() → onStartCommand() → stopService → onDestroy()
    • bind方式啟動(dòng):onCreate() → onBind() → unbindService → onUnbind() → onDestroy()
  • 運(yùn)行方式(優(yōu)先級(jí))
    • 前臺(tái)Service:會(huì)在通知欄顯示通知,優(yōu)先級(jí)較高;anr超時(shí)時(shí)間為20s
    • 后臺(tái)Service:沒(méi)有通知,優(yōu)先級(jí)較低;anr超時(shí)時(shí)間為200s
  • 常見(jiàn)的adb命令

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

    一般都有如下log打印:

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

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

    啟動(dòng)后臺(tái)Service
    // startRequested表示是否start方式啟動(dòng)(此處首次啟動(dòng)暫未賦值所以為false,且service stop后會(huì)賦值為false),fgRequired表示是否前臺(tái)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);......}
    后臺(tái)啟動(dòng)Service && 不在電池優(yōu)化白名單

    如果當(dāng)前app的uid不是active(處于后臺(tái)),即使啟動(dòng)的是前臺(tái)Service,也可能會(huì)失敗,例如:

    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;}

    另外,如果進(jìn)程的procState < 8 ,他在后臺(tái)保留1min后就會(huì)進(jìn)入idle狀態(tài),相應(yīng)的Service也會(huì)被stop

    Service發(fā)生ANR的原因

    10s內(nèi)未調(diào)用startForeground導(dǎo)致的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方式啟動(dòng)的Service,則需要在10s內(nèi)調(diào)用startForeground方法,否則會(huì)發(fā)生ANR。一般會(huì)將該方法放到Service的onStartCommand中去執(zhí)行,因?yàn)檫@種類(lèi)型的ANR超時(shí)時(shí)間是從framework層開(kāi)始通知app端執(zhí)行Service的onStartCommand開(kāi)始計(jì)算的。如果將該方法放到Service的onCreate中去執(zhí)行,onStartCommand方法返回值是START_NOT_STICKY,進(jìn)程被殺后Service重啟不會(huì)回調(diào)onCreate方法,但是還會(huì)走ANR超時(shí)邏輯。

    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);} }

    前臺(tái)Service

    設(shè)置前臺(tái)Service

    如果需要提高進(jìn)程的優(yōu)先級(jí),避免由于低內(nèi)存或其他廠商定制策略被系統(tǒng)查殺,可以設(shè)置Service為前臺(tái)Service,app端通過(guò)調(diào)用startForeground方法實(shí)現(xiàn)。具體流程如下圖1:

    app端Service中調(diào)用startForeground
    • 傳入Notication的id和Notification的實(shí)例,前臺(tái)Service的類(lèi)型
    • 如果是用startForegroundService方式啟動(dòng)的,則需要在service的onStartCommand方法中調(diào)用該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中作中轉(zhuǎn),調(diào)用ActiveServices.java中的方法(因?yàn)閍pp端進(jìn)程只有AMS或ATMS的binder對(duì)象)
    • 該操作持有AMS大鎖
    • 真正實(shí)現(xiàn)地方在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);} }
    具體實(shí)現(xiàn)邏輯

    下面可能會(huì)省略部分代碼

    public void setServiceForegroundLocked(ComponentName className, IBinder token,int id, Notification notification, int flags, int foregroundServiceType) {final int userId = UserHandle.getCallingUserId();// 清除calling uid,避免uid權(quán)限校驗(yàn)通不過(guò),清除后callingUid就時(shí)system server的uid 1000final long origId = Binder.clearCallingIdentity();try {// 通過(guò)component ,token ,userId獲取到service recordServiceRecord r = findServiceLocked(className, token, userId);if (r != null) {setServiceForegroundInnerLocked(r, id, notification, flags, foregroundServiceType);}} finally {// 恢復(fù)之前的calling uidBinder.restoreCallingIdentity(origId);} }
    • 傳入的notification id為0表示當(dāng)前service退出前臺(tái)狀態(tài)
    • P或P之后前臺(tái)service需要申請(qǐng)F(tuán)OREGROUND_SERVICE權(quán)限
    • 移除前臺(tái)service的10s超時(shí)消息
    • 更新isForeground為true
    • 通知異步執(zhí)行入隊(duì)列
    /*** @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之后前臺(tái)service需要申請(qǐng)F(tuán)OREGROUND_SERVICE權(quán)限if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {mAm.enforcePermission(android.Manifest.permission.FOREGROUND_SERVICE,r.app.pid, r.appInfo.uid, "startForeground");}// 一般傳入的service type默認(rèn)為FOREGROUND_SERVICE_TYPE_MANIFEST,而manifestType默認(rèn)為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");}// 后臺(tái)啟動(dòng)的前臺(tái)Service不得擁有“使用中權(quán)限”,下面有講到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;//移除前臺(tái)service 10s的超時(shí)消息。重置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不在電池優(yōu)化白名單內(nèi)&不是前臺(tái)app啟動(dòng),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相關(guān)變量賦值notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;r.foregroundNoti = notification;r.foregroundServiceType = foregroundServiceType;// 前臺(tái)service第一次設(shè)置會(huì)為falseif (!r.isForeground) {final ServiceMap smap = getServiceMapLocked(r.userId);if (smap != null) {// 統(tǒng)計(jì)每個(gè)app前臺(tái)service的數(shù)量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++;}// 設(shè)置isForeground為true,第二次執(zhí)行該方法后不必執(zhí)行這些代碼,且表示當(dāng)前service在前臺(tái)r.isForeground = true;........}// 每次執(zhí)行該方法都會(huì)重新post通知,異步執(zhí)行r.postNotification();if (r.app != null) {// 更新進(jìn)程優(yōu)先級(jí)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 ,是取消前臺(tái)service的流程,下面講}
    發(fā)送通知
    • 通知入隊(duì)等操作異步進(jìn)行,避免死鎖
    • 通知沒(méi)有對(duì)應(yīng)的icon,打印警告信息,并給設(shè)置一個(gè)簡(jiǎn)陋的通知模板,告知用戶當(dāng)前正在運(yùn)行該進(jìn)程
      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) {// 通知沒(méi)有對(duì)應(yīng)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));// 重新構(gòu)建通知相關(guān)信息Notification.Builder notiBuilder = new Notification.Builder(ctx,localForegroundNoti.getChannelId());// 設(shè)置icon為app應(yīng)用信息的iconnotiBuilder.setSmallIcon(appInfo.icon);// mark as foregroundnotiBuilder.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true);// 構(gòu)建pendingIntentrunningIntent.setData(Uri.fromParts("package", appInfo.packageName, null));PendingIntent pi = PendingIntent.getActivityAsUser(ams.mContext, 0,runningIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,UserHandle.of(userId));// 設(shè)置系統(tǒng)的通知顏色notiBuilder.setColor(ams.mContext.getColor(com.android.internal.R.color.system_notification_accent_color));// 設(shè)置通知標(biāo)題為“xxx 正在運(yùn)行”notiBuilder.setContentTitle(ams.mContext.getString(com.android.internal.R.string.app_running_notification_title,appName));// 設(shè)置通知的內(nèi)容"點(diǎn)按即可了解詳情或停止應(yīng)用",miui定制國(guó)內(nèi)版本為"可能導(dǎo)致系統(tǒng)卡頓,降低待機(jī)時(shí)間,點(diǎn)按關(guān)閉"// 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);}// 通知入隊(duì)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);// 出現(xiàn)問(wèn)題會(huì)stop server,阿里巴巴app就經(jīng)常因?yàn)檫@個(gè)原因被殺ams.mServices.killMisbehavingService(record,appUid, appPid, localPackageName);}}});}}

    取消前臺(tái)service

    app端調(diào)用

    取消前臺(tái)service的流程和設(shè)置前臺(tái)service的流程基本相同,傳入的參數(shù)不同,如圖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中對(duì)應(yīng)app的mNumActive,代表當(dāng)前app的前臺(tái)service -1
    • 如果app的mNumActive <= 0,則將該app從mActiveForegroundApps中移除,并更新adj
    • 傳入的flags為1,則需要取消notification
    // 取消前臺(tái)service,如果當(dāng)前service是前臺(tái)serviceif (r.isForeground) {final ServiceMap smap = getServiceMapLocked(r.userId);if (smap != null) {// 更新mActiveForegroundApps中對(duì)應(yīng)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() {// 異步執(zhí)行,避免死鎖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);}}});}

    后臺(tái)啟動(dòng)的前臺(tái)Service不得擁有使用中權(quán)限

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

    start或bind方式啟動(dòng)都會(huì)去給mAllowWhileInUsePermissionInFgs賦值

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

    豁免情況

    • FGS由ROOT_UID,SYSTEM_UID,NFC_UID、SHELL_UID等啟動(dòng)。
    • CallingUid 聲明START_ACTIVITIES_FROM_BACKGROUND權(quán)限(hide權(quán)限)
    • FGS由IME或其他可見(jiàn)應(yīng)用啟動(dòng)。
    • 在while-in-use白名單內(nèi):AttentionService, SystemCaptionService(for live caption)
    private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,int callingPid, int callingUid, Intent intent, ServiceRecord r,boolean allowBackgroundActivityStarts) {// 一般后臺(tái)啟動(dòng)前臺(tái)service限制打開(kāi)的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權(quán)限豁免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;}

    后臺(tái)Service

    后臺(tái)Service管控

    android對(duì)后臺(tái)進(jìn)程管控比較嚴(yán)格,app退到后臺(tái)超過(guò)1min就會(huì)進(jìn)入idle狀態(tài),并嘗試stop相關(guān)的Service,所以我們可能會(huì)經(jīng)常見(jiàn)到如下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]

    為了便于理解,以下代碼都只是節(jié)選方法中與之相關(guān)的一部分代碼

    在OomAdjuster.java的updateUidsLocked方法中發(fā)送IDLE_UIDS_MSG消息
    • isProcStateBackground 是判斷當(dāng)前UidRecord的procState值是否小于8(PROCESS_STATE_TRANSIENT_BACKGROUND)
    • 后臺(tái)Service的procState一般是10(PROCESS_STATE_SERVICE)
    • 如果當(dāng)前進(jìn)程跟top進(jìn)程有綁定(如處于top狀態(tài)的進(jìn)程訪問(wèn)當(dāng)前進(jìn)程的provider等),那他的procState為3(PROCESS_STATE_BOUND_TOP)
    • 如果當(dāng)前UidRect處于后臺(tái)且不在臨時(shí)白名單內(nèi),但是上次不處于后臺(tái)或者在臨時(shí)白名單內(nèi),就會(huì)在1min(BACKGROUND_SETTLE_TIME)后將當(dāng)前的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;// 判斷當(dāng)前procState是否處于后臺(tái)或者不在臨時(shí)白名單內(nèi) if (ActivityManager.isProcStateBackground(uidRec.getCurProcState()) && !uidRec.curWhitelist) {// 上次procState不處于后臺(tái)或者在臨時(shí)白名單內(nèi)if (!ActivityManager.isProcStateBackground(uidRec.setProcState) || uidRec.setWhitelist) {// 這里傳入的elapsedRealtime而不是uptimeMillis,谷歌代碼曾出過(guò)問(wèn)。uidRec.lastBackgroundTime = nowElapsed;if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {// 發(fā)送uid idle消息,處于后臺(tái)時(shí)間BACKGROUND_SETTLE_TIME是1minmService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,mConstants.BACKGROUND_SETTLE_TIME);}}
    在AMS的MainHandler處理該消息并調(diào)用idleUids去設(shè)置對(duì)應(yīng)的uid idle
    • idle處理邏輯是在system server的main線程中進(jìn)行的
    • idle時(shí)間是在處于后臺(tái)或不在臨時(shí)白名單的1min后
    • 遍歷active uid時(shí),時(shí)間到了就執(zhí)行doStopUidLocked
    • 時(shí)間未到計(jì)算更新為最近的一次idle時(shí)間并再次發(fā)送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();// 得到起始后臺(tái)時(shí)間點(diǎn)邊界,大于這個(gè)值此時(shí)不會(huì)被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;// 上次處于后臺(tái)時(shí)間 > 0表示當(dāng)前uid處于后臺(tái)if (bgTime > 0 && !uidRec.idle) {// 處于后臺(tái)時(shí)間超過(guò)1minif (bgTime <= maxBgTime) {// 打印event log : am_uid_idleEventLogTags.writeAmUidIdle(uidRec.uid);// 給idle標(biāo)志位賦值uidRec.idle = true;uidRec.setIdle = true;// stop 后臺(tái)ServicemService.doStopUidLocked(uidRec.uid, uidRec);} else {// 計(jì)算下次idle消息時(shí)間點(diǎn)if (nextTime == 0 || nextTime > bgTime) {nextTime = bgTime;}}}} // 發(fā)送下次idle消息if (nextTime > 0) {mService.mHandler.removeMessages(IDLE_UIDS_MSG);mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);}}
    stop后臺(tái)service的邏輯
    • AMS的doStopUidLocked方法會(huì)先stop 后臺(tái)service,再去更新uid并分發(fā)uid的change
    • stopInBackgroundLocked回調(diào)后,service不一定會(huì)真被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方式啟動(dòng)的serviceif (service.appInfo.uid == uid && service.startRequested) {// 獲取當(dāng)前service的start 權(quán)限,miui會(huì)多傳入callingPackage,對(duì)諸如小米推送拉起的service放開(kāi)權(quán)限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相關(guān)的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());// 將當(dāng)前service添加進(jìn)stoppping列表stopping.add(service);// 如果當(dāng)前app不在電池優(yōu)化白名單中,那么前臺(tái)service也是有可能不允許啟動(dòng)的,所以要取消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的需求,本次不會(huì)stop該serviceif (mPendingServices.contains(r)) {return;}bringDownServiceLocked(r);}

    總結(jié)

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

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。