1) 我正在开始Service
或JobService
通过startService
,startForegroundService
对于 android Oreo 和ContextCompat.startForegroundService
2)在onCreate
我做的服务的方法中startForeground (1, notification)
,在onDestroy
我做的stopForeground (false)
在onCreate
service 方法中,BroadcastReceiver
启动,接收TelephonyManager.EXTRA_STATE
诸如 OFFHOOK、RINGING、IDLE 之类的事件
这是注册接收器的代码
applicationContext.registerReceiver (
broadcastReceiver,
IntentFilter (TelephonyManager.ACTION_PHONE_STATE_CHANGED),
Manifest.permission.CALL_PHONE, null)
启动服务后,我立即致电
val intent = Intent (Intent.ACTION_DIAL)
intent.data = Uri.parse ("tel: +77787020453")
startActivity (intent)
我开始打电话
我进入服务日志
18: 38: 17.008 I / main >>>>: Unstoppable: onCreate
18: 38: 17.013 4371-4371 / me.myapp I / main >>>>: UnstoppableJob: onStartCommand
18: 38: 17.231 4371-5110 / me.myapp I / main >>>>: Unstoppable - 1
18: 38: 18.730 4371-5110 / me.myapp I / main >>>>: Unstoppable - 2
18: 38: 19.082 4371-4371 / me.myapp I / Unstoppable: blockCallReciever: onRecieve:
18: 38: 19.082 4371-4371 / me.myapp I / Unstoppable: android.intent.action.PHONE_STATE = OFFHOOK
18: 38: 21.715 5169-5169 /? I / main >>>>: Unstoppable: onCreate ⁉️
18: 38: 21.741 5169-5169 /? I / main >>>>: UnstoppableJob: onStartCommand
18: 38: 21.947 5169-5231 /? I / main >>>>: Unstoppable - 1 ⁉️
18: 38: 24.114 5242-5242 / me.myapp I / main >>>>: Unstoppable: onCreate
18: 38: 24.145 5242-5242 / me.myapp I / main >>>>: UnstoppableJob: onStartCommand
Unstoppable - 1, Unstoppable - 2 - 这是我开始查看服务是否实时运行的计时器。
我不明白为什么服务onCreate
再次调用而不调用onDestroy
,谁能帮我解决这个问题并在拨打电话时使服务稳定工作?
服务代码
package me.test.ui.test.job
import android.Manifest
import android.annotation.TargetApi
import android.app.Service
import android.content.*
import android.os.Build
import android.os.IBinder
import android.telephony.PhoneNumberUtils
import android.telephony.TelephonyManager
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import io.reactivex.Completable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import me.testapp.TestApp
import me.testapp.SyncedTime
import me.testapp.entity.CallLogItemPojo
import me.testapp.entity.CallLogPhoneStatePojo
import me.testapp.entity.PhoneStateString
import me.testapp.interactors.ContactsInteractor
import me.testapp.interactors.LoginScreenInteractor
import me.testapp.interactors.TaskInteractor
import me.testapp.managers.BlockedTelephonyManager
import me.testapp.managers.SettingsManager
import me.testapp.ui.test.TestActivity
import me.testapp.ui.test.TestActivity.Companion.log
import java.lang.Exception
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class UnstoppableJob : Service() {
override fun onBind(p0: Intent?): IBinder? {
return null
}
private lateinit var timer: Timer
private var TAG = "Unstoppable"
private var timerTask = object : TimerTask() {
override fun run() {
var lifetimeOfTask = preferences.getLong(SettingsManager.TASK_LIFETIME_MILLIES, 0)
if (lifetimeOfTask != 0.toLong() && lifetimeOfTask < System.currentTimeMillis()) {
stopSelf()
log(message = "Unstoppable: stopSelf")
}
log(message = "Unstoppable - $count")
count++
}
}
@Inject
lateinit var loginInteractor: LoginScreenInteractor
@Inject
lateinit var interactor: TaskInteractor
private var count = 1
@Inject
lateinit var preferences: SharedPreferences
private var dispose: Disposable? = null
private val disposable = CompositeDisposable()
@Inject
lateinit var contactsInteractor: ContactsInteractor
private val listPhoneState = LinkedList<PhoneStateString>()
private var phoneNumber: String? = null
private var id: Int? = null
private var token: String? = null
private var type: String? = null
private var callTypeForReceiver: String? = null
private val phoneList = mutableListOf<String>()
private val idListOfTask = mutableSetOf<Int>()
private val timeMap = mutableMapOf<Int, Disposable>()
private val blockCallReciever = object: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.i(TAG, "blockCallReciever: onRecieve:")
var lifetimeOfTask = preferences.getLong(SettingsManager.TASK_LIFETIME_MILLIES, 0)
Log.i(
TAG, intent?.action + " " + intent?.getStringExtra(
TelephonyManager.EXTRA_STATE) + " " + intent?.getStringExtra(
TelephonyManager.EXTRA_STATE_RINGING))
if (intent == null || intent.action != "android.intent.action.PHONE_STATE") {
return
}
val callNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)
//val number = intent?.getStringExtra(TelephonyManager.EXTRA_)
val state = intent.getStringExtra(TelephonyManager.EXTRA_STATE)
if (callNumber != null && callNumber.isNotEmpty()) {
var it = PhoneStateString(callNumber, state, Date())
listPhoneState.add(it)
// Send CDR
if (it.state == "IDLE" &&
lifetimeOfTask >= System.currentTimeMillis()) {
Log.i(TAG, "TestApplistener: IDLE send cdr")
sendCDR()
}
Log.i(TAG, "RingApplistener: ${it.state == "RINGING"} && ${preferences.getBoolean(SettingsManager.AUTO_RESET_CALLS, false)} && ${lifetimeOfTask >= System.currentTimeMillis()}")
if (it.state == "RINGING" &&
preferences.getBoolean(SettingsManager.AUTO_RESET_CALLS, false) &&
lifetimeOfTask >= System.currentTimeMillis()) {
var isCallEnded = BlockedTelephonyManager.findHandler(applicationContext!!).endCall()
Log.i(TAG, "RingApplistener: auto reset call: isCallEnded=$isCallEnded")
// TODO: check it
if (type != null && type == "incoming" && callTypeForReceiver != null && callTypeForReceiver == "decline_call") {
val isCallEnded = BlockedTelephonyManager.findHandler(applicationContext!!).endCall()
Log.i(TAG, "RingApplistener: auto reset call: isCallEnded=$isCallEnded")
}
}
}
Log.d(TAG, "blockCallReciever: onRecieve - number $callNumber, state: $state")
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun sendCDR() {
val bufList = mutableListOf<PhoneStateString>()
do {
bufList.add(listPhoneState.pop())
} while (bufList.last().state != "IDLE")
if (bufList.isNotEmpty()) {
dispose = contactsInteractor.loadLastCallLog(phoneNumber).flatMapCompletable {
val list = if (!phoneNumber.isNullOrEmpty())
it.filter { PhoneNumberUtils.normalizeNumber(it.number) == PhoneNumberUtils.normalizeNumber(
phoneNumber
)
}
else
it
list.map { it.callLogPhoneState = CallLogPhoneStatePojo(bufList) }
Log.i(TAG, "TestAppListenerService: $token $id sendCdr ${list.toString()}")
if (token == null || id == null) {
Completable.error(Throwable("null"))
} else {
interactor.sendCdr(token!!, id!!, CallLogItemPojo(list))
}
}.subscribe({
Log.i(TAG, "TaskPresenter: " + "successful")
}, {
Log.i(TAG, "TaskPresenter: " + "unsuccessful ${it.message}")
})
}
}
override fun onCreate() {
super.onCreate()
log(message = "Unstoppable: onCreate")
timer = Timer()
timer.schedule(timerTask, 222, 1500)
TestApp.component.inject(this)
applicationContext.registerReceiver(blockCallReciever, IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED),
Manifest.permission.CALL_PHONE, null)
val notification = NotificationCompat.Builder(this, TestApp.CHANNEL_ID)
.setContentTitle("TestApp")
.setContentText("Foreground service")
.build()
startForeground(1, notification)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
TestActivity.log(message = "UnstoppableJob: onStartCommand")
if (intent?.extras != null) {
intent.extras!!.also {
val buf = it
buf.keySet().forEach {
Log.i(TAG ,"Command: $it = ${buf.get(it)}")
}
if (it.containsKey("exit")) {
disposable.dispose()
dispose?.dispose()
this@UnstoppableJob.stopSelf()
stopSelf()
}
if (it.containsKey("phoneNumber")) {
phoneNumber = it.getString("phoneNumber")
if (phoneNumber != null) {
phoneList.add(phoneNumber!!)
}
}
if (it.containsKey("id")) id = it.getInt("id")
if (it.containsKey("token")) token = it.getString("token")
if (it.containsKey("type")) type = it.getString("type")
if (it.containsKey("callTypeForReceiver")) callTypeForReceiver = it.getString("callTypeForReceiver")
if (it.getBoolean("delete", false)) {
removeTask(it.getInt("id"))
}
if (it.containsKey("ringing") && it.containsKey("id")) {
SyncedTime.requestTimeFromTestApp()
if (!idListOfTask.contains(it.getInt("id"))) {
timeMap[it.getInt("id")] = Completable.complete().delay(60, TimeUnit.SECONDS).subscribe {
removeTask(it.getInt("id"))
}
}
idListOfTask.add(it.getInt("id"))
}
}
}
return super.onStartCommand(intent, flags, startId)
}
private fun removeTask(id: Int) {
Log.i(TAG,"remove tasks by $id")
idListOfTask.remove(id)
if (idListOfTask.size == 0) {
dispose?.dispose()
disposable.dispose()
onClear()
}
}
fun onClear() {
disposable.dispose()
timeMap.values.forEach {
it.dispose()
}
this@UnstoppableJob.stopSelf()
}
override fun bindService(service: Intent?, conn: ServiceConnection, flags: Int): Boolean {
return super.bindService(service, conn, flags)
}
override fun unbindService(conn: ServiceConnection) {
super.unbindService(conn)
}
override fun onDestroy() {
super.onDestroy()
TestActivity.log(message = "UnstoppableJob: onDestroy")
try {
timer.cancel()
timerTask.cancel()
count = 1
} catch (e: Exception) {
log(message = "Unstoppable: onDestroy - error cancel timer: ${e.message}")
}
dispose?.dispose()
disposable.dispose()
applicationContext.unregisterReceiver(blockCallReciever)
var restoreServiceIntent = Intent("con.raone.start.serivce")
restoreServiceIntent.putExtra("startService", true)
sendBroadcast(restoreServiceIntent)
stopForeground(false)
}
}