我想在Android设备中使用Kotlin
关闭应用程序或在后台所有时间时计算步数。
所以我使用Service
和notification
来运行24/7,如下所示。
在onCreate
和onStart
和onStartCommand
中,我注册了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工作完美,但几天后,计数停止。
我不知道为什么这不工作,因为我搜索如何使应用程序的工作背景上的所有谷歌和堆栈溢出,并适用于我的代码。
我已经为这个问题挣扎了近两个月。
我真的需要你的帮助,只是一些建议或猜测也是有帮助的。
如果你需要更多信息,我可以真实的添加。
请帮帮我!
2条答案
按热度按时间thigvfpy1#
前台服务不会永远持续下去。他们不是故意的。你说它可以工作1-2天,老实说,这比我预期的要长。如果你想让某个东西在后台不断运行而不被杀死-你不希望Android作为你的操作系统。它不允许这样。或者至少,你需要一个定制版本的Android。
ycggw6v22#
Android不允许服务无限运行。如果应用程序关闭,服务将被杀死。
你可以尝试这些,但没有一个是适当的解决方案
1.注册不同的广播接收器,当接收到意图时,如果服务不工作,则启动服务。
1.另一种解决方案是创建小部件将运行和计数步骤。小部件不需要一个应用程序运行的所有时间,但小部件需要放置在用户手机开始屏幕上的用户小部件开始工作