現象就是,service啓動後,應用退到後臺運行,大概過幾分鐘service就會中止。堆棧信息: ActivityManager: Stopping service due to app idle: xxxxService.
java
分析源碼:android
// UID is now in the background (and not on the temp whitelist). Was it
// previously in the foreground (or on the temp whitelist)?
if (!ActivityManager.isProcStateBackground(uidRec.setProcState)
|| uidRec.setWhitelist) {
uidRec.lastBackgroundTime = nowElapsed;
if (!mHandler.hasMessages(IDLE_UIDS_MSG)) {
// Note: the background settle time is in elapsed realtime, while
// the handler time base is uptime. All this means is that we may
// stop background uids later than we had intended, but that only
// happens because the device was sleeping so we are okay anyway.
mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
mConstants.BACKGROUND_SETTLE_TIME);
}
}
複製代碼
如何是後臺service而且再也不白名單內,會發送一個延遲消息,延遲時間是BACKGROUND_SETTLE_TIME。bash
public long BACKGROUND_SETTLE_TIME = DEFAULT_BACKGROUND_SETTLE_TIME;
private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
複製代碼
延遲時間大概是1分鐘。app
也就是:8.0及以上版本手機中有一個機制,app退到後臺後1分鐘後會清理再也不白名單中的後臺service.ide
拋出的異常信息:Not allowed to start service Intent XXX : app is in background uid UidRecord
ui
**分析源碼: **this
<!--檢測當前app是否容許後臺啓動-->
final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
<!--若是不容許 Background start not allowed-->
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
...
<!--返回 ? 告訴客戶端如今處於後臺啓動狀態,禁止你-->
return new ComponentName("?", "app is in background uid " + uidRec);
}
<!--返回值是?的狀況下就是後臺啓動service的異常-->
if (cn.getPackageName().equals("?")) {
throw new IllegalStateException(
"Not allowed to start service " + service + ": " + cn.getClassName());
}
複製代碼
回收優先級:前臺進程<可視進程<服務進程<後臺進程<內容供應根節點<空進程google
開啓前臺Service,會在通知欄顯示 經過notification方式 如音樂播放spa
kotlin代碼片斷:code
啓動/中止服務
if (!CallingStateListener.isCallingStateListener()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(Intent(this, CallingStateListener::class.java))
} else {
startService(Intent(this, CallingStateListener::class.java))
}
}
//關閉監聽電話狀態服務
if (CallingStateListener.isCallingStateListener()) {
stopService(Intent(this, CallingStateListener::class.java))
}
複製代碼
service 類
class CallingStateListener : Service() {
private val TAG = "CallingStateListener"
private var phoneStateListener: PhoneStateListener? = null
private var telephonyManager: TelephonyManager? = null
private val notificationId = "callingChannelId"
private val notificationName = "callingChannelName"
override fun onCreate() {
super.onCreate()
initCallingStateListener()
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//建立NotificationChannel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
notificationId,
notificationName,
NotificationManager.IMPORTANCE_HIGH
)
notificationManager.createNotificationChannel(channel)
}
startForeground(1, getNotification())
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
private fun initCallingStateListener() {
phoneStateListener = object : PhoneStateListener() {
@SuppressLint("MissingPermission")
override fun onCallStateChanged(state: Int, incomingNumber: String) {
super.onCallStateChanged(state, incomingNumber)
when (state) {
TelephonyManager.CALL_STATE_IDLE // 待機,即無電話時,掛斷時觸發
-> {
Log.i(TAG, "callingState:掛斷,對方手機號$incomingNumber")
}
TelephonyManager.CALL_STATE_RINGING // 響鈴,來電時觸發
-> {
Log.i(TAG, "callingState:來電,對方手機號$incomingNumber")
}
TelephonyManager.CALL_STATE_OFFHOOK // 摘機,接聽或打電話時觸發
-> {
Log.i(TAG, "callingState:撥打電話,對方手機號$incomingNumber")
}
else -> {
Log.i(TAG, "callingState:其餘")
}
}
}
}
// 設置來電監聽器
telephonyManager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager?
telephonyManager?.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE)
}
private fun getNotification(): Notification {
val builder = Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("通話服務")
.setContentText("服務正在運行")
//設置Notification的ChannelID,不然不能正常顯示
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(notificationId)
}
return builder.build()
}
override fun onDestroy() {
stopForeground(true)
super.onDestroy()
}
companion object {
fun isCallingStateListener() =
ServiceUtils.isServiceRunning(CallingStateListener::class.java)
}
}
複製代碼