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

Android Oreo中JobScheduler调度LocationService时onLocationChanged不触发

解决Android Oreo上JobScheduler调用位置服务时onLocationChanged不触发的问题

嘿,这个问题我太熟悉了——Android Oreo的后台限制确实给位置服务挖了不少坑!让我一步步帮你理清原因和解决方案:

核心原因分析

Oreo(API 26)引入了严格的后台位置权限限制和后台任务生命周期限制,直接导致你的旧代码失效:

  1. 后台位置权限缺失:Oreo及以上版本,后台应用获取位置需要额外申请ACCESS_BACKGROUND_LOCATION权限(API 29+更是强制要求),仅靠ACCESS_FINE_LOCATION/ACCESS_COARSE_LOCATION只能在前台获取位置。
  2. JobScheduler任务生命周期限制:JobScheduler的任务是短暂运行的,你的GoogleApiClient连接可能还没完成,任务就已经结束了,自然触发不了onLocationChanged。
  3. 旧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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:44:43