几个小时后,我的无尽后台服务自动停止。为啥?

Posted

技术标签:

【中文标题】几个小时后,我的无尽后台服务自动停止。为啥?【英文标题】:My Endless background Service Stopped Automatically After Few Hours . Why?几个小时后,我的无尽后台服务自动停止。为什么? 【发布时间】:2021-07-16 17:21:54 【问题描述】:

我没完没了的前台服务在 3 到 4 小时后自动停止,没有出现任何问题吗? LatLong 在服务器中连续保存了 3 - 4 个小时,但服务随机关闭或销毁,我不知道如何处理这个问题?

class MyEndlessService : Service()  , DatabaseListenerCallback 

private var wakeLock: PowerManager.WakeLock? = null
private var isServiceStarted = false
var TAG = "MyService"
var gps_status = 1
var editor: SharedPreferences.Editor? = null
var previousBestLocation: Location? = null
var apiInterface: GetDataService? = null
private var googleApiClient: GoogleApiClient? = null
private var lastLocation: Location? = null
private var locationRequest: LocationRequest? = null
private val UPDATE_INTERVAL = 10000
private val FASTEST_INTERVAL = UPDATE_INTERVAL / 2
var locationManager: LocationManager? = null
var isFirsttime = true
var sharedPreferences: SharedPreferences? = null

override fun onBind(intent: Intent): IBinder? 
    return null


override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int 
    log("onStartCommand executed with startId: $startId")
    if (intent != null) 
        val action = intent.action
        log("using an intent with action $action")
        when (action) 
            Actions.START.name -> startService()
            Actions.STOP.name -> stopService()
            else -> log("This should never happen. No action in the received intent")
        
     else 
        log(
            "with a null intent. It has been probably restarted by the system."
        )
    
    return START_STICKY


override fun onCreate() 
    super.onCreate()
    log("The service has been created".toUpperCase())
    val notification = createNotification()
    startForeground(1, notification)


override fun onDestroy() 
    super.onDestroy()
    log("The service has been destroyed".toUpperCase())
    val calendar = Calendar.getInstance()
    val mdformat = SimpleDateFormat("HH:mm:ss")
    val strDate = mdformat.format(calendar.time)
    LocalDatabaseForException(applicationContext,strDate,"Service Destroyed").execute()
    val broadcastIntent = Intent()
    broadcastIntent.action = "restartservice"
    broadcastIntent.setClass(this, Restarter::class.java)
    this.sendBroadcast(broadcastIntent)


override fun onTaskRemoved(rootIntent: Intent) 
    val restartServiceIntent = Intent(applicationContext, MyEndlessService::class.java).also 
        it.setPackage(packageName)
    ;
    val restartServicePendingIntent: PendingIntent = PendingIntent.getService(
        this,
        1,
        restartServiceIntent,
        PendingIntent.FLAG_ONE_SHOT
    );
    applicationContext.getSystemService(Context.ALARM_SERVICE);
    val alarmService: AlarmManager = applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager;
    alarmService.set(
        AlarmManager.ELAPSED_REALTIME,
        SystemClock.elapsedRealtime() + 1000,
        restartServicePendingIntent
    );


private fun startService() 

    val calendar = Calendar.getInstance()
    val mdformat = SimpleDateFormat("HH:mm:ss")
    val strDate = mdformat.format(calendar.time)
    LocalDatabaseForException(applicationContext,strDate,"Service Started").execute()

    if (isServiceStarted) return
    log("Starting the foreground service task")
  //  Toast.makeText(this, "Service starting its task", Toast.LENGTH_SHORT).show()
    isServiceStarted = true
    setServiceState(this, ServiceState.STARTED)

    // we need this lock so our service gets not affected by Doze Mode
    wake lock =
        (getSystemService(Context.POWER_SERVICE) as PowerManager).run 
            newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "EndlessService::lock").apply 
                acquire()
            
        
    // we're starting a loop in a coroutine
    GlobalScope.launch(Dispatchers.IO) 
        while (isServiceStarted) 
            launch(Dispatchers.IO) 
                pingFakeServer()
                handleUserTracking()
            
            delay(1000)
        
        log("End of the loop for the service")
    


private fun stopService() 
    log("Stopping the foreground service")
  //  Toast.makeText(this, "Service stopping", Toast.LENGTH_SHORT).show()
    val calendar = Calendar.getInstance()
    val mdformat = SimpleDateFormat("HH:mm:ss")
    val strDate = mdformat.format(calendar.time)
    LocalDatabaseForException(applicationContext,strDate,"Service Stopped").execute()
    try 
        wakeLock?.let 
            if (it.isHeld) 
               // startService()
                it.release()
            
        
      // stopForeground(true)
     //   stopSelf()
     catch (e: Exception) 
        log("Service stopped without being started: $e.message")
        val calendar = Calendar.getInstance()
        val mdformat = SimpleDateFormat("HH:mm:ss")
        val strDate = mdformat.format(calendar.time)
        LocalDatabaseForException(applicationContext,strDate,"Service stopped without being started: $e.message").execute()
    
    isServiceStarted = false
    setServiceState(this, ServiceState.STARTED)


private fun pingFakeServer()  
    SmartLocation.with(applicationContext).location()
        .start(object : OnLocationUpdatedListener 

            override fun onLocationUpdated(location: Location?) 
                val sharedPreferences = applicationContext.getSharedPreferences(
                    "AcessToken",
                    AppCompatActivity.MODE_MULTI_PROCESS
                )
                editor = getSharedPreferences("AcessToken", MODE_PRIVATE).edit()
                editor = getSharedPreferences("AcessToken", MODE_PRIVATE).edit()
                editor!!.putString("temp_lattitude", location!!.getLatitude().toString())
                editor!!.putString("temp_lattitude", location!!.getLatitude().toString())
                editor!!.apply()
                val att = sharedPreferences!!.getString("Attendance_ID", "")
                val userid = sharedPreferences.getInt("User_ID", 0).toString()
                if ((att != "") and (userid != "")) 
                    if (previousBestLocation != null) 
                        if (isBetterLocation(location, previousBestLocation)) 
                            previousBestLocation = location
                            onNewLocationAgainwithLocalDatabase(location)
                        
                     else 
                        previousBestLocation = location
                        onNewLocationAgainwithLocalDatabase(location)
                    



                
            

        )




fun getBatteryPercentage(context: Context): Int 
    return if (Build.VERSION.SDK_INT >= 21) 
        val bm = context.getSystemService(BATTERY_SERVICE) as BatteryManager
        bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
     else 
        val iFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
        val batteryStatus = context.registerReceiver(null, iFilter)
        val level = batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
        val scale = batteryStatus?.getIntExtra(BatteryManager.EXTRA_SCALE, -1) ?: -1
        val batteryPct = level / scale.toDouble()
        (batteryPct * 100).toInt()
    



fun handleUserTracking() 
    val settings = applicationContext.getSharedPreferences("AcessToken", MODE_PRIVATE)
    val manager = getSystemService(LOCATION_SERVICE) as LocationManager
    gps_status = 1
    if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) 
        gps_status = 0
        editor = getSharedPreferences("AcessToken", MODE_PRIVATE).edit()
        editor!!.putString("temp_lattitude", "")
        editor!!.putString("temp_longitude", "")
        editor!!.apply()
    
 
    Log.d("Gps status ", gps_status.toString())


fun hitApitoUpdate(gpsstatus: Int?) 
    val settings = applicationContext.getSharedPreferences("AcessToken", MODE_PRIVATE)
    val lattitude = settings.getString("temp_lattitude", "")
    val longitude = settings.getString("temp_longitude", "")
    val userID = settings.getInt("User_ID", 0).toString()
    val access_token = settings.getString("Access_Token", "")
    val battery_per = getBatteryPercentage(this)
    apiInterface = APIClient.getClient().create(GetDataService::class.java)
    val call = apiInterface!!.updateGpsStatus(
        "Bearer $access_token",
        gpsstatus,
        userID,
        lattitude,
        longitude,
        battery_per
    )
    call.enqueue(object : Callback<GpaStatusPojo> 
        override fun onResponse(call: Call<GpaStatusPojo>, response: Response<GpaStatusPojo>) 
            try 
                // Toast.makeText(applicationContext,""+response.body()!!.message,Toast.LENGTH_SHORT).show()
                Log.d("Gps status ", response.body()!!.message)
             catch (e: java.lang.Exception) 
                val dfgdsdsdsfds = ""
                Log.d("Gps sttaus 22 :", e.toString())
            
        

        override fun onFailure(call: Call<GpaStatusPojo>, t: Throwable) 
            Log.d("Gps sttaus 33 :", t.toString())
        
    )





private fun onNewLocationAgainwithLocalDatabase(location: Location) 
    val calendar = Calendar.getInstance()
    val mdformat = SimpleDateFormat("HH:mm:ss")
    val strDate = mdformat.format(calendar.time)
    LocalDatabaseInsert(location, strDate, applicationContext).execute()
    InsertLocationData(location, false, strDate, applicationContext).execute()
    getLocationList()



private fun getLocationList() 
    GetLocationData(applicationContext, this).execute()


override fun processData(locationData: MutableList<LocationParam>?) 
    hitApitoBulkEnter(locationData!!)


private fun hitApitoBulkEnter(location: List<LocationParam>) 
    if (!Utils.isNetworkConnected(this)) 
        Toast.makeText(
            this,
            "No internet connection available. Please check your internet connection.",
            Toast.LENGTH_SHORT
        ).show()
        startActivity(Intent(this, InternetSettingCheck::class.java))
        return
    
   

     // send location to the server 
  



private fun readWebPageBulkTest(location: String, mTemp: List<LocationParam>) 
    if (!Utils.isNetworkConnected(this)) 
        Toast.makeText(
            this,
            "No internet connection available. Please check your internet connection.",
            Toast.LENGTH_SHORT
        ).show()
        startActivity(Intent(this, InternetSettingCheck::class.java))
        return
    
    val settings = applicationContext.getSharedPreferences("AcessToken", MODE_PRIVATE)
    val access_token = settings.getString("Access_Token", "")
    val user_id = settings.getInt("User_ID", 0).toString()
    var attendance_id = settings.getString("Attendance_ID", "")
    apiInterface = APIClient.getClient().create(GetDataService::class.java)
    if (attendance_id == "") 
        attendance_id = "0"
    
    if (user_id != "") 
        val call: Call<NewLocationPojo> = apiInterface!!.updateLatLongforUserIDBulk(
            "Bearer $access_token",
            location
        )
        Log.e("JsonLocation  :", location)
        call.enqueue(object : Callback<NewLocationPojo> 
            override fun onResponse(
                call: Call<NewLocationPojo>,
                response: Response<NewLocationPojo>
            ) 
                try 
                    if (response.body()!!.status == "success") 
                        Log.e("Api response :", response.body()!!.status)
                        Toast.makeText(
                            this@MyEndlessService,
                            "" + response.body()!!.status.toString(),
                            Toast.LENGTH_SHORT
                        ).show()
                        updatelatlong(location, mTemp)
                     else 
                        Toast.makeText(
                            this@MyEndlessService,
                            "" + response.body()!!.status.toString(),
                            Toast.LENGTH_SHORT
                        ).show()
                    
                 catch (e: java.lang.Exception) 
                    Toast.makeText(this@MyEndlessService, "" + e.toString(), Toast.LENGTH_SHORT)
                        .show()
                    Log.e("ServerSam :", e.toString())
                
            

            override fun onFailure(call: Call<NewLocationPojo>, t: Throwable) 
                Log.e("ServerSam :", t.toString())
                log("The service has been destroyed".toUpperCase())
                val calendar = Calendar.getInstance()
                val mdformat = SimpleDateFormat("HH:mm:ss")
                val strDate = mdformat.format(calendar.time)
                LocalDatabaseForException(applicationContext,strDate,"Api Error :"+t.toString() ).execute()
            
        )
    



private fun getPostFinal() 

    class GetTasks : AsyncTask<Void?, Void?, List<LocationParam>>() 

        override fun onPostExecute(tasks: List<LocationParam>) 
            super.onPostExecute(tasks)
            DeleteLocationData(applicationContext).execute()
            GetLocationData22().execute()
            // testDataAfterDeleteion();
        

        override fun doInBackground(vararg params: Void?): List<LocationParam> 
            val taskList = DatabaseClient
                .getInstance(applicationContext)
                .appDatabase
                .locationDao()
                .all
            Log.e("Database after updation", taskList.toString())
            return taskList
        
    

    val gt = GetTasks()
    gt.execute()



internal class GetLocationData22 : AsyncTask<Void?, Void?, List<LocationParam>>() 
    var mContext: Context? = null


    override fun onPostExecute(tasks: List<LocationParam>) 
        super.onPostExecute(tasks)
    

    override fun doInBackground(vararg params: Void?): List<LocationParam> 
        val taskList = DatabaseClient.getInstance(mContext)
            .appDatabase
            .locationDao()
            .all
        Log.d("GetAfterDeleteion :", taskList.toString())
        return taskList
    



protected fun isBetterLocation(location: Location, currentBestLocation: Location?): Boolean 
    if (currentBestLocation == null) 
        // A new location is always better than no location
        return true
    

    // Check whether the new location fix is newer or older
    val timeDelta = location.time - currentBestLocation.time
    val isSignificantlyNewer = timeDelta > MyService66.TWO_MINUTES
    val isSignificantlyOlder = timeDelta < -MyService66.TWO_MINUTES
    val isNewer = timeDelta > 0

    // If it's been more than two minutes since the current location, use the new location
    // because the user has likely moved
    if (isSignificantlyNewer) 
        return true
        // If the new location is more than two minutes older, it must be worse
     else if (isSignificantlyOlder) 
        return false
    

    // Check whether the new location fix is more or less accurate
    val accuracyDelta = (location.accuracy - currentBestLocation.accuracy).toInt()
    val isLessAccurate = accuracyDelta > 0
    val isMoreAccurate = accuracyDelta < 0
    val isSignificantlyLessAccurate = accuracyDelta > 200
    // Check if the old and new location are from the same provider
    val isFromSameProvider: Boolean =
        isSameProvider(location.provider, currentBestLocation.provider)
    // Determine location quality using a combination of timeliness and accuracy
    if (isMoreAccurate) 
        return true
     else if (isNewer && !isLessAccurate) 
        return true
     else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) 
        return true
    
    return false



/**
 * Checks whether two providers are the same
 */
private fun isSameProvider(provider1: String?, provider2: String?): Boolean 
    return if (provider1 == null) 
        provider2 == null
     else provider1 == provider2


private fun createNotification(): Notification 
    val notificationChannelId = "ENDLESS SERVICE CHANNEL"

    // depending on the android API that we're dealing with we will have
    // to use a specific method to create the notification
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val channel = NotificationChannel(
            notificationChannelId,
            "Endless Service notifications channel",
            NotificationManager.IMPORTANCE_HIGH
        ).let 
            it.description = "Endless Service channel"
            it.enableLights(true)
            it.lightColor = Color.RED
            it.enableVibration(true)
            it.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
            it
        
        notificationManager.createNotificationChannel(channel)
    

    val pendingIntent: PendingIntent = Intent(this, MainActivityFinal2::class.java).let  notificationIntent ->
        PendingIntent.getActivity(this, 0, notificationIntent, 0)
    

    val builder: Notification.Builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Notification.Builder(
        this,
        notificationChannelId
    ) else Notification.Builder(this)

    return builder
        .setContentTitle("Sapphire location on")
       // .setContentText("This is your favorite endless service working")
        .setContentIntent(pendingIntent)
        .setSmallIcon(R.mipmap.app_icon)
     //   .setTicker("Ticker text")
        .setPriority(Notification.PRIORITY_HIGH) // for under android 26 compatibility
        .build()

【问题讨论】:

请更具体。如果您创建了一个重现您的问题的小示例,将会有所帮助。你的班级很大,很难理解究竟是什么导致了这个问题。 【参考方案1】:

服务可以停止,并且很可能会在某个时候被操作系统停止。根据文档

Android 系统仅在内存不足时才会停止服务,并且必须为具有用户焦点的 Activity 恢复系统资源。如果服务绑定到具有用户焦点的活动,则它被杀死的可能性较小;如果服务被声明在前台运行,它很少被杀死。如果服务已启动并长时间运行,系统会随着时间的推移降低其在后台任务列表中的位置,并且该服务变得非常容易被杀死——如果您的服务已启动,您必须将其设计为优雅地处理由系统。如果系统终止了您的服务,它会在资源可用时立即重新启动它,但这也取决于您从 onStartCommand() 返回的值。

重启服务最好的办法就是在onStartCommand()中声明服务为STICKY_SERVICE

【讨论】:

以上是关于几个小时后,我的无尽后台服务自动停止。为啥?的主要内容,如果未能解决你的问题,请参考以下文章

客户下单后未付款时,客服后台显示订单状态为()

一段时间后,android后台服务停止

iOS 8 在 10 分钟后停止在后台流式传输音频

为啥我的 DJS 角色声明消息可以工作几个小时,但突然停止?

一段时间后如何在iOS中停止后台服务

当应用程序进入后台时停止后台服务