Android Oreo中JobScheduler调度LocationService时onLocationChanged不触发
解决Android Oreo上JobScheduler调用位置服务时onLocationChanged不触发的问题
嘿,这个问题我太熟悉了——Android Oreo的后台限制确实给位置服务挖了不少坑!让我一步步帮你理清原因和解决方案:
核心原因分析
Oreo(API 26)引入了严格的后台位置权限限制和后台任务生命周期限制,直接导致你的旧代码失效:
- 后台位置权限缺失:Oreo及以上版本,后台应用获取位置需要额外申请
ACCESS_BACKGROUND_LOCATION权限(API 29+更是强制要求),仅靠ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATION只能在前台获取位置。 - JobScheduler任务生命周期限制:JobScheduler的任务是短暂运行的,你的
GoogleApiClient连接可能还没完成,任务就已经结束了,自然触发不了onLocationChanged。 - 旧API适配问题:你使用的
GoogleApiClient已经被Google废弃,旧API在Oreo的后台环境下兼容性很差,推荐使用更适配新系统的FusedLocationProviderClient。
具体解决方案
1. 补充后台位置权限
在AndroidManifest.xml中添加所需权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 适配Oreo及以上的后台位置权限 --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" android:maxSdkVersion="32" /> <!-- API 33+需配合通知权限获取后台位置 --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
同时要在运行时动态请求这些权限,尤其是ACCESS_BACKGROUND_LOCATION,需要用户明确授权。
2. 替换为FusedLocationProviderClient
放弃旧的GoogleApiClient,改用更简洁适配的FusedLocationProviderClient,修改你的LocationUpdateService:
public class LocationUpdateService extends JobService { private static final String TAG = "LocationUpdateService"; private FusedLocationProviderClient fusedLocationClient; private LocationCallback locationCallback; private JobParameters currentJobParams; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "onCreateCalled: "); fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); // 创建位置回调,处理位置更新 locationCallback = new LocationCallback() { @Override public void onLocationResult(LocationResult locationResult) { if (locationResult == null) { jobFinished(currentJobParams, false); return; } for (Location location : locationResult.getLocations()) { // 在这里处理你的位置逻辑 Log.i(TAG, "获取到位置: " + location.getLatitude() + ", " + location.getLongitude()); } // 任务完成后标记结束 jobFinished(currentJobParams, false); } }; } @Override public boolean onStartJob(JobParameters params) { Log.i(TAG, "onStartJob: "); currentJobParams = params; requestLocationUpdates(); return true; // 表示任务仍在进行中 } private void requestLocationUpdates() { LocationRequest locationRequest = LocationRequest.create(); locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); locationRequest.setInterval(1000); // 测试用间隔,实际按需调整 locationRequest.setFastestInterval(500); // 检查权限是否已授予 if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { jobFinished(currentJobParams, false); return; } try { fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()); } catch (SecurityException e) { e.printStackTrace(); jobFinished(currentJobParams, false); } } @Override public boolean onStopJob(JobParameters params) { Log.d("JobStopped", "JobStopped"); // 移除位置回调,避免内存泄漏 if (fusedLocationClient != null && locationCallback != null) { fusedLocationClient.removeLocationUpdates(locationCallback); } return true; // 是否需要重新调度任务 } }
3. 调整JobScheduler配置
Oreo上setPeriodic()的最小间隔是15分钟(900000毫秒),这个是符合要求的,建议添加优化参数避免任务被系统轻易杀死:
JobInfo.Builder builder = new JobInfo.Builder(11, componentName); builder.setPeriodic(900000); builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); builder.setPersisted(true); // 添加适配Oreo的优化参数 builder.setRequiresBatteryNotLow(true); builder.setRequiresStorageNotLow(true); jobInfo = builder.build();
4. 解决获取到(0,0)的问题
你之前用new Location("")创建的是一个空的Location对象,没有任何真实位置数据,自然返回(0,0)。必须通过FusedLocationProviderClient获取系统提供的真实位置,就像上面代码中那样。
额外注意事项
- 如果需要更频繁的后台位置更新,Oreo及以上版本建议使用前台服务,因为后台任务的位置更新频率被严格限制(每小时最多几次)。
- 升级
play-services-location版本:你当前用的11.0.4太旧了,很多新特性和兼容性修复都没有,建议更新到最新稳定版:
implementation 'com.google.android.gms:play-services-location:21.0.1'
内容的提问来源于stack exchange,提问作者dev90
相关产品推荐
相关产品推荐

