为何我的Android Studio应用在API 34可用,API 33及以下不可用?
问题描述
我正在开发一款获取用户当前经纬度并弹出坐标提示的应用,在API 34环境下运行正常,但API 33及以下版本中,即使降低依赖版本能成功构建,点击按钮也没有任何反应。这是我第一次用Android Studio,求帮忙。
现有代码
MainActivity.kt
package com.velvetthunder.driverlocation import android.Manifest import android.content.pm.PackageManager import android.location.Location import android.os.Bundle import android.widget.Button import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationServices class MainActivity : AppCompatActivity() { private val REQUEST_CODE = 101 private lateinit var fusedLocationProviderClient: FusedLocationProviderClient private lateinit var currentLocation: Location override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this) fetchLastLocation() } private fun fetchLastLocation() { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_CODE) return } val task = fusedLocationProviderClient.lastLocation task.addOnSuccessListener { location -> if (location != null) { currentLocation = location findViewById<Button>(R.id.button) .setOnClickListener { Toast.makeText(applicationContext, "${currentLocation.latitude},${currentLocation.longitude}", Toast.LENGTH_SHORT).show() } } } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { REQUEST_CODE -> { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { fetchLastLocation() } } } } }
build.gradle.kts
plugins { id("com.android.application") id("org.jetbrains.kotlin.android") } android { namespace = "com.velvetthunder.driverlocation" compileSdk = 33 defaultConfig { applicationId = "com.velvetthunder.driverlocation" minSdk = 21 targetSdk = 33 versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = "1.8" } } dependencies { implementation("com.google.android.gms:play-services-location:18.0.0") implementation("androidx.core:core-ktx:1.10.0") implementation("androidx.appcompat:appcompat:1.6.1") implementation("com.google.android.material:material:1.5.0") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.DriverLocation" tools:targetApi="33" android:requestLegacyExternalStorage="true"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
问题分析与修复方案
核心问题
- 按钮点击事件绑定时机错误:当前仅在
lastLocation获取成功时才给按钮绑定点击事件,如果设备没有最近的定位记录(API33及以下常见场景),location会返回null,按钮就没有任何点击事件,导致点击无反应。 - 未初始化风险:
lateinit var currentLocation如果lastLocation返回null,后续调用会直接抛出未初始化异常。 - 依赖旧定位记录:
lastLocation依赖设备的最近定位缓存,旧版本设备如果从未定位过或缓存过期,就无法获取到位置。
修改后的代码
MainActivity.kt
package com.velvetthunder.driverlocation import android.Manifest import android.content.pm.PackageManager import android.location.Location import android.os.Bundle import android.widget.Button import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationCallback import com.google.android.gms.location.LocationRequest import com.google.android.gms.location.LocationResult import com.google.android.gms.location.LocationServices class MainActivity : AppCompatActivity() { private val REQUEST_CODE = 101 private lateinit var fusedLocationProviderClient: FusedLocationProviderClient private var currentLocation: Location? = null private lateinit var locationCallback: LocationCallback override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this) // 提前绑定按钮点击事件,不管定位是否成功都能响应 findViewById<Button>(R.id.button).setOnClickListener { currentLocation?.let { loc -> Toast.makeText(applicationContext, "${loc.latitude},${loc.longitude}", Toast.LENGTH_SHORT).show() } ?: run { Toast.makeText(applicationContext, "无法获取位置,请检查定位权限和设置", Toast.LENGTH_SHORT).show() } } fetchLocation() } private fun fetchLocation() { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_CODE) return } // 先尝试获取最后已知位置 fusedLocationProviderClient.lastLocation.addOnSuccessListener { location -> if (location != null) { currentLocation = location } else { // 没有缓存位置时,请求实时定位 requestRealTimeLocation() } } } private fun requestRealTimeLocation() { val locationRequest = LocationRequest.create().apply { priority = LocationRequest.PRIORITY_HIGH_ACCURACY interval = 10000 fastestInterval = 5000 } locationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { super.onLocationResult(locationResult) locationResult.lastLocation?.let { currentLocation = it // 获取到位置后停止定位更新 fusedLocationProviderClient.removeLocationUpdates(this) } } } if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, null) } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { REQUEST_CODE -> { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { fetchLocation() } else { Toast.makeText(this, "需要定位权限才能获取位置", Toast.LENGTH_SHORT).show() } } } } override fun onDestroy() { super.onDestroy() // 销毁时移除定位回调,避免内存泄漏 if (::locationCallback.isInitialized) { fusedLocationProviderClient.removeLocationUpdates(locationCallback) } } }
额外调整建议
- 对于API29及以上版本,如果需要在后台获取位置,需要添加
ACCESS_BACKGROUND_LOCATION权限,但当前场景是前台点击获取,暂时不需要。 - 可以在布局中添加提示文本,告诉用户当前定位状态。
内容的提问来源于stack exchange,提问作者Jayman
相关产品推荐
相关产品推荐

