Android后台Location Listener无法工作问题求助
解决应用关闭后Service无法获取位置更新的问题
我来帮你拆解下这个问题——你的Service在应用关闭后停止获取位置更新,主要是没适配Android的后台限制机制,还有一些API使用上的小坑,咱们一步步来解决:
问题根源分析
- Android后台Service管控:从Android 8.0(API 26)开始,系统对普通后台Service的限制非常严格,哪怕你返回了
START_STICKY,也很容易被系统回收。 - 权限缺失:Android 10(API 29)及以上版本,想要在后台持续获取位置,必须申请
ACCESS_BACKGROUND_LOCATION权限,你的Manifest里没加这个关键权限。 - 原生LocationManager的局限性:原生
LocationManager在后台场景下的稳定性不如谷歌官方推荐的FusedLocationProviderClient,系统更容易限制它的位置更新频率。 - 独立进程Service:你给Service设置了
:my_service独立进程,这种进程的优先级更低,系统资源紧张时会优先被回收。
具体修改步骤
1. 更新Manifest配置
补充必要权限,调整Service的配置:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.kanwarpreet.location_manager"> <!-- 基础定位权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- Android 10+ 后台定位权限 --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".Activity_Location_Tracker"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- 去掉独立进程,添加前台服务类型声明 --> <service android:name=".utils.MyService" android:enabled="true" android:foregroundServiceType="location" /> </application> </manifest>
2. 添加谷歌定位服务依赖
在app/build.gradle(Module级别)中添加Fused Location Provider的依赖:
dependencies { // 谷歌定位服务 implementation 'com.google.android.gms:play-services-location:21.0.1' }
3. 重写Service类(改用FusedLocationProviderClient+前台服务)
替换原来的MyService代码,用更稳定的Fused定位API,同时把Service转为前台服务:
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.os.IBinder; import android.util.Log; 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; public class MyService extends Service { private static final String TAG = "BOOMBOOMTESTGPS"; private static final int NOTIFICATION_ID = 12345; private static final String CHANNEL_ID = "LOCATION_SERVICE_CHANNEL"; private FusedLocationProviderClient fusedLocationClient; private LocationCallback locationCallback; private LocationRequest locationRequest; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); Log.e(TAG, "onCreate"); // 创建通知渠道(Android O+ 必需) createNotificationChannel(); // 启动前台服务,避免被系统回收 startForeground(NOTIFICATION_ID, getTrackingNotification()); // 初始化Fused定位客户端 fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); // 配置位置更新参数 locationRequest = LocationRequest.create(); locationRequest.setInterval(1000); // 1秒更新一次 locationRequest.setFastestInterval(500); // 最快500毫秒更新 locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); // 高精度模式 // 设置位置回调 locationCallback = new LocationCallback() { @Override public void onLocationResult(LocationResult locationResult) { super.onLocationResult(locationResult); if (locationResult == null) return; // 处理位置更新 for (Location location : locationResult.getLocations()) { Log.e(TAG, "onLocationChanged: " + location.getLatitude() + ", " + location.getLongitude()); } } }; // 开始请求位置更新 startLocationUpdates(); } private void startLocationUpdates() { // 检查权限 if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "缺少定位权限,无法获取位置"); return; } fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e(TAG, "onStartCommand"); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); Log.e(TAG, "onDestroy"); // 停止位置更新 if (fusedLocationClient != null && locationCallback != null) { fusedLocationClient.removeLocationUpdates(locationCallback); } } // 创建前台服务通知渠道 private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "位置追踪服务", NotificationManager.IMPORTANCE_LOW // 低优先级,不打扰用户 ); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } // 创建前台服务显示的通知 private Notification getTrackingNotification() { Notification.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder = new Notification.Builder(this, CHANNEL_ID); } else { builder = new Notification.Builder(this); } return builder.setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("位置追踪中") .setContentText("正在获取您的实时位置") .setPriority(Notification.PRIORITY_LOW) .build(); } }
4. 在Activity中动态申请权限
启动Service前,向用户申请必要的定位权限(尤其是后台权限):
import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; public class Activity_Location_Tracker extends AppCompatActivity { private static final int LOCATION_PERMISSION_REQUEST_CODE = 1001; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_location_tracker); // 检查权限,没有就申请 if (checkLocationPermissions()) { startLocationService(); } else { requestLocationPermissions(); } } private boolean checkLocationPermissions() { boolean hasFineLocation = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { boolean hasBackgroundLocation = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED; return hasFineLocation && hasBackgroundLocation; } return hasFineLocation; } private void requestLocationPermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION }, LOCATION_PERMISSION_REQUEST_CODE); } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) { if (checkLocationPermissions()) { startLocationService(); } else { // 用户拒绝了权限,提示无法进行位置追踪 // 可以弹出Toast或者对话框 } } } private void startLocationService() { Intent serviceIntent = new Intent(this, MyService.class); startService(serviceIntent); } }
关键修改说明
- 前台服务:通过
startForeground把Service转为前台服务,系统会认为这是用户关注的服务,不会轻易回收,同时显示一个低优先级通知避免打扰用户。 - FusedLocationProviderClient:谷歌官方维护的定位API,比原生LocationManager更适配Android后台机制,能更稳定地获取位置更新。
- 后台定位权限:Android 10+必须申请
ACCESS_BACKGROUND_LOCATION,否则应用进入后台后会被禁止获取位置。 - 去掉独立进程:去掉
:my_service让Service和应用同进程,提升进程优先级,减少被回收的概率。
按照这些步骤修改后,你的应用关闭(退到后台)后,Service应该就能持续获取位置更新了。
内容的提问来源于stack exchange,提问作者Kanwarpreet Singh
相关产品推荐
相关产品推荐

