Android 手机旋转时我的前台服务的多个实例

如何解决Android 手机旋转时我的前台服务的多个实例

我正在编写 Android 位置应用和前台服务。该应用程序会定期从服务中获取位置更新。我正在使用 Android Studio 并在 Kotlin 中编写应用程序。

问题在于,当手机旋转时,会创建一个新的前台服务实例。这通过显示位置更新计数以及服务哈希码的日志输出进行了演示:

MainActivity.onCreate(Buddle)
MainActivity.onStart
MainActivity.onResume
>>>>>>>>>>>>>>>>>>>  LocationService: onStartCommand
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 1     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 2     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 3     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 4     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 5     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 6     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 7     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
MainActivity.onPause
MainActivity.onStop
MainActivity.onSaveInstanceState
MainActivity.onCreate(Buddle)
MainActivity.onStart
MainActivity:   ======= Location Changed  ======= 
MainActivity.onResume
LocationService: onDestroy  ========================================================
>>>>>>>>>>>>>>>>>>>  LocationService: onStartCommand
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 8     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 1     Service Hash Code: 233645514 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 9     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 2     Service Hash Code: 233645514 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 10     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 3     Service Hash Code: 233645514 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 11     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 4     Service Hash Code: 233645514 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 12     Service Hash Code: 185408788 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  ======= 
LocationService:  ******* onLocation Change *******   -  Count: 5     Service Hash Code: 233645514 <<<<<<<<<<<<<<
MainActivity:   ======= Location Changed  =======

手机轮换发生在生命周期活动发生的中途。我一直在研究这个问题,并在过去两天在线搜索寻求帮助,但我看到的与此问题相关的唯一答案是,“您无法创建服务的多个实例”但是日志 似乎 证明不然。我一直不愿意在堆栈溢出上提问,因为我是新手,不完全知道提问的正确方法,但我不知道还能尝试什么。 如果我忘记在我的问题中包含某些内容,请告诉我,谢谢。

主要:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.considerateapps.logger">
    
    <!-- To request foreground location access,declare one of these permissions. -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    
    <application
        android:allowBackup="true"
        android:fullBackupContent="true"
        android:hasFragileUserData="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Logger">
        <service
            android:name=".ui.main.LocationService"
            android:foregroundServiceType="location"
            android:exported="false"
            android:stopWithTask="true"
            />

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="AIzaSyBybVSRyyvJbnHUfYRRi3PJEsodusgKX78" />
        
        <activity
            android:name=".MainActivity"
            android:label="Logger"
            android:theme="@style/Theme.Logger.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

主要活动:

package com.considerateapps.logger

import android.Manifest
import android.content.pm.PackageManager
import android.media.MediaPlayer
import android.os.Bundle
import android.util.Log

import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.observe
import com.considerateapps.logger.ui.main.*


class MainActivity : AppCompatActivity()
{
    public  val TAG = "Logger"
    private val locationPermissionCode = 2

    override fun onCreate(savedInstanceState: Bundle?)
    {
        Log.i(TAG,"MainActivity.onCreate(Buddle)")
        checkLocationPermission()
        startForegroundService()

        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        setSupportActionBar(findViewById(R.id.toolbar))

        setupObservers()
    }

    private fun setupObservers()
    {
        LocationService.location.observe(this as LifecycleOwner)
        {
            Log.i(TAG,"MainFragment:   ======= database Updated Observer  ======= ")
            val mediaPlayerButtonSound: MediaPlayer = MediaPlayer.create(this,R.raw.ding)
            mediaPlayerButtonSound.start()

        }
    }

    private fun checkLocationPermission()
    {
        if ((ContextCompat.checkSelfPermission(
                        application,Manifest.permission.ACCESS_FINE_LOCATION
                ) != PackageManager.PERMISSION_GRANTED)
        ) {
            ActivityCompat.requestPermissions(
                    this,arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),locationPermissionCode
            )
        }
    }

    private fun startForegroundService()
    {
        LocationService.startService(this,"Foreground Service is running...")
    }
    override fun onStart()
    {
        Log.i(TAG,"MainActivity.onStart")
        super.onStart()
    }

    override fun onResume()
    {
        Log.i(TAG,"MainActivity.onResume")
        super.onResume()
    }

    override fun onPause()
    {
        Log.i(TAG,"MainActivity.onPause")
        super.onPause()
    }

    override fun onStop()
    {
        Log.i(TAG,"MainActivity.onStop")
        super.onStop()
    }

    override fun onSaveInstanceState(outState: Bundle)
    {
        Log.i(TAG,"MainActivity.onSaveInstanceState")
        super.onSaveInstanceState(outState)
    }

    override fun onLowMemory()
    {
        Log.i(TAG,"$localClassName.onLowMemory")
        super.onLowMemory()
    }

    override fun onDestroy()
    {
        LocationService.stopService(this)
        super.onDestroy()
    }
}

LocationService.kt:

package com.considerateapps.logger.ui.main

import android.Manifest
import android.app.*
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.MutableLiveData
import com.considerateapps.logger.MainActivity
import com.considerateapps.logger.R


class LocationService : Service(),LocationListener
{
    val TAG = "Logger"
    val NOTIF_ID = 1

    private lateinit var locationManager: LocationManager
    private var locationUpdateCount = 0

    private val CHANNEL_ID = "ForegroundService Kotlin"
    companion object
    {
        val location:MutableLiveData<Location> = MutableLiveData<Location>()

        fun startService(context: Context,message: String)
        {
            val startIntent = Intent(context,LocationService::class.java)
            startIntent.putExtra("inputExtra",message)
            ContextCompat.startForegroundService(context,startIntent)
        }

        fun stopService(context: Context)
        {

            val stopIntent = Intent(context,LocationService::class.java)
            context.stopService(stopIntent)
        }
    }

    override fun onStartCommand(intent: Intent?,flags: Int,startId: Int): Int
    {
        Log.i(TAG,">>>>>>>>>>>>>>>>>>>  LocationService: onStartCommand")
        locationManager = application.getSystemService(Context.LOCATION_SERVICE) as LocationManager

        //do heavy work on a background thread
        val input = intent?.getStringExtra("inputExtra")
        createNotificationChannel()
        val notification = getNotification(input!!)

        startForeground(NOTIF_ID,notification)
        requestLocationUpdates()
        super.onStartCommand(intent,flags,startId)
        return START_STICKY
    }

    private fun getNotification(contentText: String):Notification
    {
        val notificationIntent = Intent(this,MainActivity::class.java)
        notificationIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
        val pendingIntent = PendingIntent.getActivity(
                this,notificationIntent,PendingIntent.FLAG_UPDATE_CURRENT
        )

        val notification = NotificationCompat.Builder(this,CHANNEL_ID)
                .setOngoing(false)
                .setAutoCancel(false)
                .setContentTitle("Location Service")
                .setContentText(contentText)
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setContentIntent(pendingIntent)
                .setOnlyAlertOnce(true)
                .build()
        return notification
    }

    private fun updateNotification(contentText: String)
    {
        val notification: Notification = getNotification(contentText)
        val mNotificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        mNotificationManager.notify(NOTIF_ID,notification)
    }

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

    private fun createNotificationChannel() {
        val serviceChannel = NotificationChannel(CHANNEL_ID,"Foreground Service Channel",NotificationManager.IMPORTANCE_DEFAULT)
        val manager = getSystemService(NotificationManager::class.java)
        manager!!.createNotificationChannel(serviceChannel)
    }

    private fun requestLocationUpdates()
    {
        if((ContextCompat.checkSelfPermission(application,Manifest.permission.ACCESS_FINE_LOCATION)) ==PackageManager.PERMISSION_GRANTED)
        {
            location.value = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,200,0f,this)
        }
    }

    override fun onLocationChanged(i_location: Location) {
        locationUpdateCount++
        Log.i(TAG,"LocationService:  ******* onLocation Change *******   -  Count: $locationUpdateCount     Service Hash Code: ${hashCode()} <<<<<<<<<<<<<<")
        // Toast.makeText(this,"Count:$locationUpdateCount",Toast.LENGTH_SHORT).show();

        updateNotification("Count: $locationUpdateCount")
        location.value = i_location
    }

    override fun onDestroy()
    {
        Log.i(TAG,"LocationService: onDestroy  ========================================================")
        super.onDestroy()
    }
}

解决方法

经过几个小时的调试,我发现了问题:

在 MainActivity.onDestroy() 中,我试图停止我的服务 LocationService。问题是我将位置回调附加到服务,因此当手机旋转时服务泄漏,因为调用 MainActivity.onDestroy() 试图停止服务但显然不能因为附加的位置侦听器和 Android似乎假设它成功停止了服务,因此在下次创建它的新实例。

*** 似乎是 Android 操作系统的错误 ***

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-