Почему onLocationChanged не вызывается в моем фоновом потоке [дубликат]Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Почему onLocationChanged не вызывается в моем фоновом потоке [дубликат]

Сообщение Anonymous »

Я хочу, чтобы мой LocationListener работал в собственном потоке, отдельном от моего ForegroundService:
Поэтому я создал класс под названием ForegroundService

Код: Выделить всё

class ForegroundService : Service() {
private lateinit var notificationManager: NotificationManager
private lateinit var notificationBuilder: NotificationCompat.Builder
private lateinit var firstname: String
private lateinit var lastname: String
private lateinit var birthdate: String
private lateinit var height: String
private lateinit var weight: String
private lateinit var eventname: String
private lateinit var eventdate: String
private lateinit var artofsport: String
private lateinit var comment: String
private lateinit var clothing: String
private var speed: Float = 0.0f
private var eventId: Int = 0
private var distance: Double = 0.0
private var altitude: Double = 0.0
private var latitude: Double = 0.0
private var longitude: Double = 0.0
private var lap: Int = 0
private val oldLatitude: Double = -999.0
private val oldLongitude: Double = -999.0
private var lazySegmentStartTimeNanos: Long = 0L
private var lazyTotalElapsedSeconds: Long = 0L
private var lazyFormattedTime: String = "00:00:00"
private var movementSegmentStartTimeNanos: Long = 0L
private var movementTotalElapsedSeconds: Long = 0L
private var movementFormattedTime: String = "00:00:00"
private val serviceJob = Job()
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
private lateinit var wakeLock: PowerManager.WakeLock

override fun onCreate() {
super.onCreate()
loadSharedPreferences()
//displayDatabaseContents()
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}

private fun loadSharedPreferences() {
val sharedPreferences = this.getSharedPreferences("UserSettings", Context.MODE_PRIVATE)
firstname = sharedPreferences.getString("firstname", "") ?: ""
lastname = sharedPreferences.getString("lastname", "") ?: ""
birthdate = sharedPreferences.getString("birthdate", "") ?: ""
height = sharedPreferences.getString("height", "") ?: ""
weight = sharedPreferences.getString("weight", "") ?: ""
}

private fun acquireWakeLock() {
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "GeoTracker:ForegroundService:WakeLock")
wakeLock.acquire()
}

private fun releaseWakeLock() {
if (::wakeLock.isInitialized && wakeLock.isHeld) {
wakeLock.release()
}
}

private val database: FitnessTrackerDatabase by lazy {
FitnessTrackerDatabase.getInstance(applicationContext)
}

private fun createBackgroundCoroutine() {
serviceScope.launch {
val customLocationListener = CustomLocationListener(applicationContext)
customLocationListener.startDateTime = LocalDateTime.now()
customLocationListener.startListener()
val userId = database.userDao().getUserIdByFirstNameLastName(firstname, lastname)
eventId = createNewEvent(database,userId)
while (isActive) {
// if speed is higher than some value,
// we do not need to compare latitude and longitude (method compareLatitudeLongitude)
// anymore because you cannot move without changing your position
if (speed >= MIN_SPEED_THRESHOLD) {
showStopWatch()
} else {
showLazyStopWatch()
}
showNotification()
delay(1000)
}
}
}

//    private fun compareLatitudeLongitude(latitude: Double, longitude: Double): Boolean {
//        val comparisonLat = oldLatitude.compareTo(latitude)
//        val comparisonLng = oldLongitude.compareTo(longitude)
//
//        return comparisonLat != 0 || comparisonLng != 0
//    }

private fun showNotification() {
updateNotification(
"Activity: " + movementFormattedTime +
"\nCovered Distance: "  + String.format("%.2f", distance / 1000) + " Km" +
"\nSpeed: " + String.format("%.2f", speed) + " km/h" +
"\nAltitude: " + String.format("%.2f", altitude) + " meter" +
"\nLap: " + String.format("%2d", lap) +
"\nInactivity: " + lazyFormattedTime
)
}

private fun showStopWatch() {
if (speed >= 2.5) {
if (movementSegmentStartTimeNanos == 0L) {
movementSegmentStartTimeNanos = System.nanoTime()
}

val movementSegmentElapsedNanos = System.nanoTime() - movementSegmentStartTimeNanos
val movementSegmentElapsedSeconds = TimeUnit.NANOSECONDS.toSeconds(movementSegmentElapsedNanos)

if (movementSegmentElapsedSeconds > 0) {
movementTotalElapsedSeconds += movementSegmentElapsedSeconds
movementSegmentStartTimeNanos = System.nanoTime()
}

val movementHours = (movementTotalElapsedSeconds / 3600).toInt()
val movementMinutes = ((movementTotalElapsedSeconds % 3600) / 60).toInt()
val movementSeconds = (movementTotalElapsedSeconds % 60).toInt()
movementFormattedTime = String.format("%02d:%02d:%02d", movementHours, movementMinutes, movementSeconds)
} else {
movementSegmentStartTimeNanos = 0L
}
}

private fun showLazyStopWatch() {
if (speed < 2.5) {
if (lazySegmentStartTimeNanos == 0L) {
lazySegmentStartTimeNanos = System.nanoTime()
}

val lazySegmentElapsedNanos = System.nanoTime() - lazySegmentStartTimeNanos
val lazySegmentElapsedSeconds = TimeUnit.NANOSECONDS.toSeconds(lazySegmentElapsedNanos)

if (lazySegmentElapsedSeconds > 0) {
lazyTotalElapsedSeconds += lazySegmentElapsedSeconds
lazySegmentStartTimeNanos = System.nanoTime()
}
} else {
lazySegmentStartTimeNanos = 0L
}

// Format total inactivity time
val lazyHours = (lazyTotalElapsedSeconds / 3600).toInt()
val lazyMinutes = ((lazyTotalElapsedSeconds % 3600) / 60).toInt()
val lazySeconds = (lazyTotalElapsedSeconds % 60).toInt()
lazyFormattedTime = String.format("%02d:%02d:%02d", lazyHours, lazyMinutes, lazySeconds)
}

private fun compareLatitudeLongitude(): Boolean {
val res: Int = when {
oldLatitude < latitude -> -1
oldLongitude > latitude -> 1
else -> 0
}
return res == 0
}

private suspend fun createNewEvent(database: FitnessTrackerDatabase, userId: Int): Int {
val newEvent = Event(
userId = userId,
eventName = eventname,
eventDate = eventdate,
artOfSport = artofsport,
comment = comment
)

val eventId = database.eventDao().insertEvent(newEvent).toInt()
println("New Event ID: $eventId")
return eventId
}

private fun displayDatabaseContents() {
GlobalScope.launch(Dispatchers.IO) {
val users = database.userDao().getAllUsers()
users.forEach { user ->
Log.d("DatabaseDebug", "User: $user")
}
}
}

// Publisher/Subscriber
@Subscribe(threadMode = ThreadMode.BACKGROUND)
fun onLocationEvent(event: LocationEvent) {
Log.d("ForegroundService",
"Latitude: ${event.latitude}," +
" Longitude: ${event.longitude}," +
" Speed: ${event.speed}," +
" SpeedAccuracyInMeters: ${event.speedAccuracyMetersPerSecond}," +
" Altitude: ${event.altitude}," +
" HorizontalAccuracyInMeters: ${event.horizontalAccuracy}," +
" VerticalAccuracyInMeters: ${event.verticalAccuracyMeters}" +
"  CoveredDistance: ${event.coveredDistance}")
speed = event.speed
altitude = event.altitude
latitude = event.latitude
longitude = event.longitude
distance = event.coveredDistance
lap = event.lap
insertDatabase(database)
}

private fun updateNotification(newContent: String) {
runOnUiThread {
notificationBuilder.setStyle(NotificationCompat.BigTextStyle().bigText(newContent))
notificationManager.notify(1, notificationBuilder.build())
}
}

private fun insertDatabase(database: FitnessTrackerDatabase) {
GlobalScope.launch(Dispatchers.IO) {
val metric = Metric(
eventId = eventId,
heartRate = 0,
heartRateDevice = "Chest Strap",
speed = speed,
distance = distance,
cadence = 0,
lap = lap,
timeInMilliseconds = System.currentTimeMillis(),
unity = "metric"
)
database.metricDao().insertMetric(metric)

// Example location data
val location = Location(
eventId = eventId,
latitude = latitude,
longitude = longitude,
altitude = altitude
)
database.locationDao().insertLocation(location)

val weather = Weather(
eventId = eventId,
weatherRestApi = "OpenWeatherAPI",
temperature = 0f,
windSpeed = 0f,
windDirection = "NE",
relativeHumidity = 0
)
database.weatherDao().insertWeather(weather)

val deviceStatus = DeviceStatus(
eventId = eventId,
numberOfSatellites = 8,
sensorAccuracy = "High",
signalStrength = "Strong",
batteryLevel = "80%",
connectionStatus = "Connected"
)
database.deviceStatusDao().insertDeviceStatus(deviceStatus)
}
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
eventname = intent?.getStringExtra("eventName") ?: "Unknown Event"
eventdate = intent?.getStringExtra("eventDate") ?: "Unknown Date"
artofsport = intent?.getStringExtra("artOfSport") ?: "Unknown Sport"
comment = intent?.getStringExtra("comment") ?: "No Comment"
clothing = intent?.getStringExtra("clothing") ?: "No Clothing Info"

acquireWakeLock()

EventBus.getDefault().register(this)
createNotificationChannel()
createBackgroundCoroutine()

notificationBuilder = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("GeoTracker")
//            .setContentText("Time, covered distance, ...  will be shown here!")
.setSmallIcon(R.drawable.ic_launcher_background)
.setOnlyAlertOnce(true)
//show notification on home screen to everyone
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
//without FOREGROUND_SERVICE_IMMEDIATE, notification can take up to 10 secs to be shown
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)

val notification = notificationBuilder.build()

startForeground(1, notification)

return START_STICKY
}

override fun onDestroy() {
super.onDestroy()
releaseWakeLock()
EventBus.getDefault().unregister(this)
// Stop infinite loop
serviceJob.cancel()
// Stop foreground mode and cancel the notification immediately
stopForeground(STOP_FOREGROUND_REMOVE)
}

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

private fun createNotificationChannel() {
val channel = NotificationChannel(
CHANNEL_ID,
"GeoTracker",
NotificationManager.IMPORTANCE_HIGH
)
notificationManager.createNotificationChannel(channel)
}

// Companion
companion object {
private const val CHANNEL_ID = "ForegroundServiceChannel"
private const val MIN_SPEED_THRESHOLD: Double = 2.5
}

private fun runOnUiThread(action: () -> Unit) {
if (Looper.myLooper() == Looper.getMainLooper()) {
action()
} else {
Handler(Looper.getMainLooper()).post(action)
}
}
}
Затем у меня есть класс с реализованным LocationListener, однако, как только я выключаю экран, onLocationChanged больше не вызывается.
CustomLocationListener< /p>

Код: Выделить всё

class CustomLocationListener: LocationListener {
private lateinit var totalDateTime: LocalDateTime
lateinit var startDateTime: LocalDateTime
private var context: Context
private var locationManager: LocationManager? = null
private var oldLatitude: Double = 99.0
private var oldLongitude: Double = 99.0
private var coveredDistance: Double = 0.0
private var lapCounter : Double = 0.0
private var lap : Int = 0
private var averageSpeed : Double = 0.0

constructor(context: Context) {
this.context = context
}

fun startListener() {
createLocationManager()
createLocationUpdates()
}

private fun createLocationUpdates() {
if (ActivityCompat.checkSelfPermission(
this.context,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this.context,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
return
}
Handler(Looper.getMainLooper()).post {
locationManager?.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_BETWEEN_UPDATES, MIN_DISTANCE_BETWEEN_UPDATES, this)
}
}

private fun createLocationManager() {
locationManager = this.context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
}

override fun onLocationChanged(location: Location) {
location?.let {
Log.d("CustomLocationListener", "Latitude: ${location!!.latitude} / Longitude: ${location!!.longitude}")
coveredDistance = checkSpeedAndCalculateDistance(it)
averageSpeed = calculateAverageSpeed(coveredDistance)
lap = calculateLap(coveredDistance)
EventBus.getDefault().post(LocationEvent(it.latitude, it.longitude, it.speed, it.speedAccuracyMetersPerSecond, it.altitude, it.accuracy, it.verticalAccuracyMeters, coveredDistance, lap, startDateTime, averageSpeed))
}
}

private fun calculateAverageSpeed(coveredDistance: Double): Double{
totalDateTime = LocalDateTime.now()
var duration = Duration.between(startDateTime, totalDateTime)
return coveredDistance / (duration.toNanos()/1_000_000_000.0)
}

private fun checkSpeedAndCalculateDistance(it: Location) : Double {
if(oldLatitude!=99.0 &&  oldLongitude!=99.0) {
if(checkSpeed(it.speed)) {
coveredDistance = calculateDistance(oldLatitude, oldLongitude, it.latitude, it.longitude)
oldLatitude = it.latitude
oldLongitude = it.longitude
}
} else {
oldLatitude = it.latitude
oldLongitude = it.longitude
}
return coveredDistance
}

private fun calculateLap(coveredDistance: Double) : Int {
lapCounter += coveredDistance;
if(lapCounter>=1000) {
lap+=1;
lapCounter=0.0;
}
return lap
}

private fun calculateDistance(
oldLatitude: Double,
oldLongitude: Double,
newLatitude: Double,
newLongitude: Double
): Double {
val result = FloatArray(1)
Location.distanceBetween(
//older location
oldLatitude,
oldLongitude,
//current location
newLatitude,
newLongitude,
result);
coveredDistance += result[0]
return coveredDistance
}

private fun checkSpeed(speed: Float): Boolean {
return if(speed>=MIN_SPEED_THRESHOLD) {
true
} else {
false
}
}

companion object {
private const val MIN_TIME_BETWEEN_UPDATES: Long = 1000
private const val MIN_DISTANCE_BETWEEN_UPDATES: Float = 1f
private const val MIN_SPEED_THRESHOLD: Double = 2.5
}
}
У меня заявлено все необходимое, например (оптимизация батареи отключена!)
AndroidManifest.xml Может быть, кто-нибудь подскажет мне, чего мне не хватает?

Подробнее здесь: https://stackoverflow.com/questions/792 ... und-thread
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»