You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在OnCreate/Activity外启动Service?GPS服务按钮控制报错求助

安卓GPS定位服务启停时触发NullPointerException问题解决

问题描述

开发安卓应用GPS定位功能,通过按钮启动/停止LocationService时触发NullPointerException,错误日志如下:

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
                                                                                                        at android.content.ComponentName.<init>(ComponentName.java:131)
                                                                                                        at android.content.Intent.<init>(Intent.java:6858)
                                                                                                        at red.button.flipper_navigator.ui.MainScreen.start(MainScreen.kt:58)
                                                                                                        at red.button.flipper_navigator.ui_logic.MainScreenLogic.StartNavBt(Main_screen_logic.kt:47)
                                                                                                        at red.button.flipper_navigator.databinding.ScreenMainBindingImpl$OnClickListenerImpl1.onClick(ScreenMainBindingImpl.java:189)
                                                                                                        at android.view.View.performClick(View.java:7448)
                                                                                                        at android.view.View.performClickInternal(View.java:7425)
                                                                                                        at android.view.View.access$3600(View.java:810)
                                                                                                        at android.view.View$PerformClick.run(View.java:28305)
                                                                                                        at android.os.Handler.handleCallback(Handler.java:938)
                                                                                                        at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                        at android.os.Looper.loop(Looper.java:223)
                                                                                                        at android.app.ActivityThread.main(ActivityThread.java:7656)
                                                                                                        at java.lang.reflect.Method.invoke(Native Method)
                                                                                                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
                                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

相关代码

MainScreen Activity

package red.button.flipper_navigator.ui

import android.Manifest
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.lifecycle.ViewModel

import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import red.button.flipper_navigator.ui_logic.MainScreenLogic
import red.button.flipper_navigator.R
import red.button.flipper_navigator.databinding.ScreenMainBinding
import red.button.flipper_navigator.databinding.TextFieldBinding
import red.button.flipper_navigator.gps.LocationService
import red.button.flipper_navigator.gps.gps_activation


@Suppress("DEPRECATION")
class MainScreen : AppCompatActivity(), gps_activation {
    private lateinit var bindingMain: ScreenMainBinding
    private lateinit var bindingField: TextFieldBinding
    private var context: Context? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ActivityCompat.requestPermissions(
            this,
            arrayOf(
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.POST_NOTIFICATIONS
            ),
            0
        )

        bindingMain = DataBindingUtil.setContentView(this, R.layout.screen_main)
        bindingField = bindingMain.textField

        val viewModel = ViewModelProvider(this)[MainScreenLogic::class.java]

        bindingMain.mainview = viewModel
        bindingField.textfieldview = viewModel
        context =  getApplicationContext()

//        Intent(context, LocationService::class.java).apply {
//            action = LocationService.ACTION_START
//            startService(this)
//        }

    }

    override fun start() {
        Intent(context, LocationService::class.java).apply {
            action = LocationService.ACTION_START
            startService(this)
        }
    }

    override fun stop() {
        Intent(context, LocationService::class.java).apply {
            action = LocationService.ACTION_STOP
            startService(this)
        }
    }
}

gps_activation接口

package red.button.flipper_navigator.gps

import android.content.Context
import red.button.flipper_navigator.ui.MainScreen

interface gps_activation {
    fun start()
    fun stop()

    companion object {
        fun create(): gps_activation {
            return MainScreen()
        }
    }
}

MainScreenLogic ViewModel

package red.button.flipper_navigator.ui_logic

import android.app.Application
import android.content.Context
import android.content.Intent
import android.util.Log
import android.view.View
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import red.button.flipper_navigator.core.impl.link_logic.TextRef
import red.button.flipper_navigator.data.remote.GetLink
import red.button.flipper_navigator.gps.LocationService
import red.button.flipper_navigator.gps.gps_activation

@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("UNUSED_PARAMETER")
class MainScreenLogic() : ViewModel() {
    var link: String = ""

    val gpsSet = gps_activation.create()
    fun generateBt(view: View) {

        val linkUnzip = TextRef.create()
        val service = GetLink.create()

        val glink = viewModelScope.async {
            service.getPosts(linkUnzip.linkUnfolder(link))
        }
        glink.invokeOnCompletion {
            if (it == null) {
                Log.d("link111", linkUnzip.placeFind(glink.getCompleted())[1])

            }
        }
        Log.d("link111", "ok")

    }

    fun connectBt(view: View) {
    }


// This Button i'm trying to use to start gps //
    fun StartNavBt(view: View) {

        Log.d("gps", "ok")
        gpsSet.start()

    }

}

问题根源

  1. 手动实例化Activity导致Context为空:gps_activation接口的create()方法直接return MainScreen(),这是手动创建Activity实例,而非系统通过生命周期流程生成的实例。手动创建的Activity不会执行onCreate(),导致context变量始终为null,调用Intent(context, ...)时触发空指针。
  2. ViewModel持有Activity实例存在内存泄漏风险:ViewModel的生命周期长于Activity,直接持有Activity实例会导致内存泄漏。

修复方案

1. 重构接口定义

移除接口中手动创建Activity的逻辑,仅保留行为定义:

interface gps_activation {
    fun start()
    fun stop()
}

2. 修改ViewModel,用LiveData传递指令

ViewModel通过LiveData向Activity发送服务启停指令,避免直接持有Activity实例:

@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("UNUSED_PARAMETER")
class MainScreenLogic : ViewModel() {
    var link: String = ""
    
    // 用于发送服务启停指令的LiveData
    private val _serviceAction = MutableLiveData<String?>()
    val serviceAction: LiveData<String?> = _serviceAction

    fun generateBt(view: View) {
        // 原有逻辑保持不变
        val linkUnzip = TextRef.create()
        val service = GetLink.create()

        val glink = viewModelScope.async {
            service.getPosts(linkUnzip.linkUnfolder(link))
        }
        glink.invokeOnCompletion {
            if (it == null) {
                Log.d("link111", linkUnzip.placeFind(glink.getCompleted())[1])
            }
        }
        Log.d("link111", "ok")
    }

    fun connectBt(view: View) {
    }

    fun StartNavBt(view: View) {
        Log.d("gps", "ok")
        // 发送启动服务指令
        _serviceAction.value = LocationService.ACTION_START
    }

    fun StopNavBt(view: View) {
        Log.d("gps", "stop")
        // 发送停止服务指令
        _serviceAction.value = LocationService.ACTION_STOP
    }
}

3. 修改Activity,响应ViewModel指令

  • 移除手动维护的context变量,直接用Activity本身(Activity继承自Context)
  • 观察ViewModel的LiveData,执行服务启停操作:
@Suppress("DEPRECATION")
class MainScreen : AppCompatActivity(), gps_activation {
    private lateinit var bindingMain: ScreenMainBinding
    private lateinit var bindingField: TextFieldBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ActivityCompat.requestPermissions(
            this,
            arrayOf(
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.POST_NOTIFICATIONS
            ),
            0
        )

        bindingMain = DataBindingUtil.setContentView(this, R.layout.screen_main)
        bindingField = bindingMain.textField

        val viewModel = ViewModelProvider(this)[MainScreenLogic::class.java]

        bindingMain.mainview = viewModel
        bindingField.textfieldview = viewModel

        // 监听ViewModel的服务指令
        viewModel.serviceAction.observe(this) { action ->
            action?.let {
                when(it) {
                    LocationService.ACTION_START -> start()
                    LocationService.ACTION_STOP -> stop()
                }
                // 重置指令,避免重复触发
                viewModel._serviceAction.value = null
            }
        }
    }

    override fun start() {
        Intent(this, LocationService::class.java).apply {
            action = LocationService.ACTION_START
            // Android 8.0+建议使用startForegroundService
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                startForegroundService(this)
            } else {
                startService(this)
            }
        }
    }

    override fun stop() {
        Intent(this, LocationService::class.java).apply {
            action = LocationService.ACTION_STOP
            startService(this)
        }
    }
}

4. 额外注意事项

  • 确保LocationService已在AndroidManifest.xml中注册:
<service android:name=".gps.LocationService" />
  • Android 8.0及以上版本,后台服务需调用startForeground()显示通知,避免被系统杀死。

内容的提问来源于stack exchange,提问作者Red_button

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 07:17:05