kotlin ForegroundService无法在后台工作,具体取决于设备

mdfafbf1  于 2023-04-07  发布在  Kotlin
关注(0)|答案(2)|浏览(174)

我想在Android设备中使用Kotlin关闭应用程序或在后台所有时间时计算步数。
所以我使用Servicenotification来运行24/7,如下所示。
onCreateonStartonStartCommand中,我注册了sensorManager来计数步骤和调用通知,以便在后台运行它,应用程序关闭。
步数以onSensorChanged计。
我的服务代码在下面,请仔细查看。

class MyService : Service(), SensorEventListener {

    private val db = Firebase.firestore
    private val userDB = Firebase.firestore.collection("users")
    private val diaryDB = Firebase.firestore.collection("diary")
    private val userId = Firebase.auth.currentUser?.uid

    private var todayStepCountFromDB = 0

    //    lateinit var diaryUpdateBroadcastReceiver: DiaryUpdateBroadcastReceiver
    lateinit var dateChangeBroadcastReceiver: DateChangeBroadcastReceiver
    lateinit var deviceShutdownBroadcastReceiver: DeviceShutdownBroadcastReceiver
    lateinit var stepCountBroadcastReceiver: StepCountBroadcastReceiver

    companion object {
        lateinit var sensorManager: SensorManager
        lateinit var step_sensor: Sensor

        const val ACTION_STEP_COUNTER_NOTIFICATION =
            "com.chungchunon.chunchunon_android.STEP_COUNTER_NOTIFICATION"

        var todayTotalStepCount: Int? = 0
        const val STEP_THRESHOLD: Double = 6.5

        const val stepCountSharedPref = "stepCountSharedPreference"
        const val dateChangeSharedPref = "dateChangeSharedPref"
    }

    lateinit var prefs: SharedPreferences

    private var startingStepCount: Int = 0
    private var stepCount: Int = 0

    override fun onCreate() {
        super.onCreate()

        StepCountNotification(this, todayTotalStepCount)

        // 기본
        sensorManager =
            applicationContext?.getSystemService(Context.SENSOR_SERVICE) as SensorManager
        step_sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
        sensorManager.registerListener(this, step_sensor, SensorManager.SENSOR_DELAY_FASTEST)

        // 오늘 걸음수 초기화
        userDB.document("$userId").get().addOnSuccessListener { document ->
            var todayStepCountFromDB = document.getLong("todayStepCount") ?: 0
            todayTotalStepCount = todayStepCountFromDB.toInt()
            StepCountNotification(this, todayTotalStepCount)
        }

        // shared preference 설정
        prefs = getSharedPreferences(stepCountSharedPref, Context.MODE_PRIVATE)
        var dummyData = prefs.getInt(userId, 0)
        var datePrefs = getSharedPreferences(dateChangeSharedPref, Context.MODE_PRIVATE)

        // DateChangeBroadcastReceiver : 매일 걸음수 0
        dateChangeBroadcastReceiver = DateChangeBroadcastReceiver()
        val dateChangeIntent = IntentFilter()
        dateChangeIntent.addAction(Intent.ACTION_TIME_TICK)
        applicationContext?.registerReceiver(dateChangeBroadcastReceiver, dateChangeIntent)

        // DeviceShutDownBroadcastReceiver : 핸드폰 꺼질 때
        deviceShutdownBroadcastReceiver = DeviceShutdownBroadcastReceiver()

        val deviceShutdownIntent = IntentFilter()
        deviceShutdownIntent.addAction(Intent.ACTION_SHUTDOWN)
        applicationContext?.registerReceiver(deviceShutdownBroadcastReceiver, deviceShutdownIntent)

        // StepCountBroadcastReceiver : 디비 저장

        stepCountBroadcastReceiver = StepCountBroadcastReceiver()

        LocalBroadcastManager.getInstance(applicationContext).registerReceiver(
            stepInitializeReceiver,
            IntentFilter("NEW_DATE_STEP_ZERO")
        );
    }

    override fun onBind(p0: Intent?): IBinder? {
        return null
    }

    override fun onSensorChanged(sensorEvent: SensorEvent?) {

        startingStepCount = prefs.getInt(userId, 0)
        stepCount = sensorEvent!!.values[0].toInt()
        val editor = prefs.edit()

        db.collection("user_step_count").document("$userId")
            .get()
            .addOnSuccessListener { document ->
                if (document.exists()) {
                    var snapShot = document.data

                    var dateFormat = SimpleDateFormat("yyyy-MM-dd")
                    var cal = Calendar.getInstance()
                    cal.add(Calendar.DATE, -1)
                    var yesterday = dateFormat.format(cal.getTime())

                    if (snapShot!!.containsKey("dummy") && snapShot!!.containsKey(yesterday)) {
                        // 더미값 존재
                        var dummyStepCount = (snapShot["dummy"] as Long).toInt()

                        todayTotalStepCount = stepCount - dummyStepCount

                        var intentToFirebase = Intent(this, StepCountBroadcastReceiver::class.java)
                        intentToFirebase.setAction(ACTION_STEP_COUNTER_NOTIFICATION)
                        intentToFirebase.putExtra("todayTotalStepCount", todayTotalStepCount)
                        sendBroadcast(intentToFirebase)

                        StepCountNotification(this, todayTotalStepCount)

                        var intentToMyDiary = Intent(ACTION_STEP_COUNTER_NOTIFICATION).apply {
                            putExtra(
                                "todayTotalStepCount",
                                todayTotalStepCount
                            )
                        }
                        LocalBroadcastManager.getInstance(applicationContext!!)
                            .sendBroadcast(intentToMyDiary)

                        Log.d(
                            "걸음수 체크체크 2",
                            "stepCount: ${stepCount} // startingStepCount: ${dummyStepCount}"
                        )

                    } else {
                        // 더미값 존재 x

                        var newDummySet = hashMapOf(
                            "dummy" to stepCount
                        )
                        db.collection("user_step_count").document("$userId")
                            .set(newDummySet, SetOptions.merge())

                        StepCountNotification(this, 0)

                        var intentToFirebase = Intent(this, StepCountBroadcastReceiver::class.java)
                        intentToFirebase.setAction(ACTION_STEP_COUNTER_NOTIFICATION)
                        intentToFirebase.putExtra("todayTotalStepCount", 0)
                        sendBroadcast(intentToFirebase)

                        var intentToMyDiary = Intent(ACTION_STEP_COUNTER_NOTIFICATION).apply {
                            putExtra(
                                "todayTotalStepCount",
                                0
                            )
                        }
                        LocalBroadcastManager.getInstance(applicationContext!!)
                            .sendBroadcast(intentToMyDiary)
                    }
                }
            }
    }

    override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
        Log.d("서비스", "accuracychanged")
    }

    override fun onStart(intent: Intent?, startId: Int) {
        super.onStart(intent, startId)

        sensorManager.registerListener(this, step_sensor, SensorManager.SENSOR_DELAY_FASTEST)

        StepCountNotification(this, todayTotalStepCount)

        // 기본
        sensorManager =
            applicationContext?.getSystemService(Context.SENSOR_SERVICE) as SensorManager
        step_sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
        sensorManager.registerListener(this, step_sensor, SensorManager.SENSOR_DELAY_FASTEST)

        // 오늘 걸음수 초기화
        userDB.document("$userId").get().addOnSuccessListener { document ->
            var todayStepCountFromDB = document.getLong("todayStepCount") ?: 0
            todayTotalStepCount = todayStepCountFromDB.toInt()
            StepCountNotification(this, todayTotalStepCount)
        }

        // DateChangeBroadcastReceiver : 매일 걸음수 0
        dateChangeBroadcastReceiver = DateChangeBroadcastReceiver()
        val dateChangeIntent = IntentFilter()
        dateChangeIntent.addAction(Intent.ACTION_TIME_TICK)
        applicationContext?.registerReceiver(dateChangeBroadcastReceiver, dateChangeIntent)

        // DeviceShutDownBroadcastReceiver : 핸드폰 꺼질 때
        deviceShutdownBroadcastReceiver = DeviceShutdownBroadcastReceiver()

        val deviceShutdownIntent = IntentFilter()
        deviceShutdownIntent.addAction(Intent.ACTION_SHUTDOWN)
        applicationContext?.registerReceiver(deviceShutdownBroadcastReceiver, deviceShutdownIntent)

        // StepCountBroadcastReceiver : 디비 저장
        stepCountBroadcastReceiver = StepCountBroadcastReceiver()

        LocalBroadcastManager.getInstance(applicationContext).registerReceiver(
            stepInitializeReceiver,
            IntentFilter("NEW_DATE_STEP_ZERO")
        );
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        sensorManager.registerListener(this, step_sensor, SensorManager.SENSOR_DELAY_FASTEST)

        StepCountNotification(this, todayTotalStepCount)

        // 기본
        sensorManager =
            applicationContext?.getSystemService(Context.SENSOR_SERVICE) as SensorManager
        step_sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
        sensorManager.registerListener(this, step_sensor, SensorManager.SENSOR_DELAY_FASTEST)

        // 오늘 걸음수 초기화
        userDB.document("$userId").get().addOnSuccessListener { document ->
            var todayStepCountFromDB = document.getLong("todayStepCount") ?: 0
            todayTotalStepCount = todayStepCountFromDB.toInt()
            StepCountNotification(this, todayTotalStepCount)
        }

        // DateChangeBroadcastReceiver : 매일 걸음수 0
        dateChangeBroadcastReceiver = DateChangeBroadcastReceiver()
        val dateChangeIntent = IntentFilter()
        dateChangeIntent.addAction(Intent.ACTION_TIME_TICK)
        applicationContext?.registerReceiver(dateChangeBroadcastReceiver, dateChangeIntent)

        // DeviceShutDownBroadcastReceiver : 핸드폰 꺼질 때
        deviceShutdownBroadcastReceiver = DeviceShutdownBroadcastReceiver()

        val deviceShutdownIntent = IntentFilter()
        deviceShutdownIntent.addAction(Intent.ACTION_SHUTDOWN)
        applicationContext?.registerReceiver(deviceShutdownBroadcastReceiver, deviceShutdownIntent)

        // StepCountBroadcastReceiver : 디비 저장
        stepCountBroadcastReceiver = StepCountBroadcastReceiver()

        LocalBroadcastManager.getInstance(applicationContext).registerReceiver(
            stepInitializeReceiver,
            IntentFilter("NEW_DATE_STEP_ZERO")
        );
        return START_STICKY
    }

    var stepInitializeReceiver: BroadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            StepCountNotification(context!!, 0)
        }
    }

    private fun StepCountNotification(context: Context, stepCount: Int?) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val CHANNEL_ID = "my_app"
            val channel = NotificationChannel(
                CHANNEL_ID,
                "MyApp", NotificationManager.IMPORTANCE_LOW
            )
            (context.getSystemService(Service.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(
                channel
            )
            var decimal = DecimalFormat("#,###")
            var step = decimal.format(stepCount)

            val notification = NotificationCompat.Builder(context, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_new_alarm_icon)
                .setContentTitle("$step 걸음")
                .setDefaults(Notification.DEFAULT_LIGHTS)
                .setOngoing(true)
                .setShowWhen(false)
                .build()
            startForeground(1, notification)
        }
    }

}

DateChangeBroadcastReceiver的情况下,我创建广播接收器,以便在新日期的00:00时将步数重置为0。
在检查ACTIVITY_RECOGNITION权限后,我在Activity中调用它:

userDB.document("$userId").get()
            .addOnSuccessListener { document ->
                var userType = document.data?.getValue("userType").toString()

                if (userType != "파트너") {
                    if (ContextCompat.checkSelfPermission(
                            this,
                            Manifest.permission.ACTIVITY_RECOGNITION,
                        ) == PackageManager.PERMISSION_DENIED
                    ) {
                        //ask for permission
                        ActivityCompat.requestPermissions(
                            this,
                            arrayOf(Manifest.permission.ACTIVITY_RECOGNITION),
                            100
                        )
                    } else {
                        var startService = Intent(this, MyService::class.java)

                        //오레오 이상부터 동작하는 코드
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                            startForegroundService(startService);
                        } else {
                            startService(startService);
                        }
                    }

                }
            }

所以我在4个Android设备上安装了这个应用程序。
其中三个运行良好,当我关闭应用程序或应用程序进入后台时,步骤很好地计数,设备上显示通知。
但是一个设备在正常工作一段时间后就停止了。
1-2 days工作完美,但几天后,计数停止。
我不知道为什么这不工作,因为我搜索如何使应用程序的工作背景上的所有谷歌和堆栈溢出,并适用于我的代码。
我已经为这个问题挣扎了近两个月。
我真的需要你的帮助,只是一些建议或猜测也是有帮助的。
如果你需要更多信息,我可以真实的添加。
请帮帮我!

thigvfpy

thigvfpy1#

前台服务不会永远持续下去。他们不是故意的。你说它可以工作1-2天,老实说,这比我预期的要长。如果你想让某个东西在后台不断运行而不被杀死-你不希望Android作为你的操作系统。它不允许这样。或者至少,你需要一个定制版本的Android。

ycggw6v2

ycggw6v22#

Android不允许服务无限运行。如果应用程序关闭,服务将被杀死。
你可以尝试这些,但没有一个是适当的解决方案
1.注册不同的广播接收器,当接收到意图时,如果服务不工作,则启动服务。
1.另一种解决方案是创建小部件将运行和计数步骤。小部件不需要一个应用程序运行的所有时间,但小部件需要放置在用户手机开始屏幕上的用户小部件开始工作

相关问题