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

Android中Geofencing搭配JobIntentService无法正常工作的问题

Fixing Geofence Issues When Migrating from IntentService to JobIntentService

I’ve run into this exact issue before—Android 8.0+ background restrictions make direct Service launches from Geofence triggers unreliable. Here’s a step-by-step solution to get your Geofence logic working with JobIntentService:

Core Problem

GeofencingClient uses a PendingIntent to trigger your service, but Android 8+ blocks implicit service starts in the background. JobIntentService relies on enqueueWork() instead of direct startService() calls, so we need to add a BroadcastReceiver as a middle layer to bridge the gap.

Step 1: Update Geofence PendingIntent to Use a BroadcastReceiver

Instead of pointing the PendingIntent directly to your JobIntentService, create one that triggers a BroadcastReceiver:

private PendingIntent getGeofencePendingIntent(Context context) {
    Intent intent = new Intent(context, GeofenceBroadcastReceiver.class);
    // Use FLAG_IMMUTABLE for Android 12+ compliance, FLAG_UPDATE_CURRENT to reuse the intent
    return PendingIntent.getBroadcast(
        context, 
        0, 
        intent, 
        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
    );
}

Step 2: Create the BroadcastReceiver to Enqueue Work

This receiver will pass the Geofence intent to your JobIntentService using the required enqueueWork() method:

public class GeofenceBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // Forward the Geofence event to the JobIntentService
        GeofenceJobIntentService.enqueueWork(context, intent);
    }
}

Step 3: Rewrite Your Service as JobIntentService

Replace your IntentService with JobIntentService, moving your onHandleIntent() logic to onHandleWork() (the JobIntentService equivalent):

public class GeofenceJobIntentService extends JobIntentService {
    public static final int GEOFENCE_JOB_ID = 1001;
    private GeofencingClient mGeofencingClient;
    private boolean isSettingGeofence = false;
    private String geofenceRequestID = "YOUR_GEOFENCE_ID"; // Use your existing ID

    // Static method to enqueue work (required for JobIntentService)
    public static void enqueueWork(Context context, Intent intent) {
        enqueueWork(
            context, 
            GeofenceJobIntentService.class, 
            GEOFENCE_JOB_ID, 
            intent
        );
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        // Copy your original onHandleIntent() logic here
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        if (geofencingEvent != null) {
            int transitionType = geofencingEvent.getGeofenceTransition();
            if (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
                Location triggering = geofencingEvent.getTriggeringLocation();
                if (triggering != null) {
                    Utils.appendLog(
                        "GEOFENCE onHandleWork got an EXIT transition: " + triggering.getLatitude() + ", " + triggering.getLongitude() + " / accuracy: " + triggering.getAccuracy(), 
                        "D", 
                        "#updategeo " + Constants.GEOFENCE
                    );
                } else {
                    Utils.appendLog(
                        "GEOFENCE onHandleWork got an EXIT transition null", 
                        "D", 
                        "#updategeo " + Constants.GEOFENCE
                    );
                }
                Utils.appendLog("removing geofence after exit", "I", Constants.TRACKER + "#updategeo");
                removeGeofencesAndStartTracking();
            }
        }
    }

    // Keep your existing addGeofence logic, with small adjustments
    public void addGeofence(final Context context, final Location location, float radius) {
        if (!isSettingGeofence) {
            isSettingGeofence = true;
            ArrayList<Geofence> geofences = new ArrayList<>();
            geofences.add(new Geofence.Builder()
                    .setRequestId(geofenceRequestID)
                    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_EXIT)
                    .setCircularRegion(location.getLatitude(), location.getLongitude(), radius)
                    .setExpirationDuration(Geofence.NEVER_EXPIRE)
                    .build());

            if (geofences.size() > 0) {
                if (mGeofencingClient == null)
                    mGeofencingClient = LocationServices.getGeofencingClient(context);
                
                if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    Utils.appendLog("Geofence Transitions Service addGeofence checkSelfPermission ERROR", "E", Constants.GEOFENCE);
                    isSettingGeofence = false; // Reset state to allow future attempts
                    return;
                }

                Utils.appendLog("addGeofences is being called, wait for success or failure callbacks", "I", Constants.TRACKER + "#updategeo");
                mGeofencingClient.addGeofences(getGeofencingRequest(geofences), getGeofencePendingIntent(context))
                        .addOnSuccessListener(aVoid -> {
                            PSLocationService.getInstance(context).lastTimeDidExitRegion = null;
                            isSettingGeofence = false;

                            // Realm logic (updated to use try-with-resources for safety)
                            final RealmLocation realmLocation = new RealmLocation(location.getLatitude(), location.getLongitude(), location.getTime(), null, true);
                            realmLocation.setAccuracy(location.getAccuracy());
                            realmLocation.setSpeed(location.getSpeed());
                            final GeofenceRealmLocation geofenceRealmLocation = new GeofenceRealmLocation(geofenceRequestID, realmLocation);

                            try (Realm realm = Realm.getInstance(PSApplicationClass.Config)) {
                                realm.executeTransaction(r -> r.copyToRealmOrUpdate(geofenceRealmLocation));
                            }

                            // Clear notification
                            try {
                                NotificationManager notifManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
                                notifManager.cancel(1010);
                            } catch (Exception e) {
                                Utils.appendLog("Error removing notification: " + e.getMessage(), "I", Constants.GEOFENCE);
                            }

                            Utils.appendLog("Geofence Transitions Service setGeofenceRequest Success adding geofences!" + location.getLatitude() + " , " + location.getLongitude() + " / accuracy: " + location.getAccuracy(), "I", "#updategeo " + Constants.GEOFENCE);
                        })
                        .addOnFailureListener(e -> {
                            Utils.appendLog("GEOFENCETEST addGeofences FAILURE: " + e.getMessage(), "E", Constants.GEOFENCE);
                            isSettingGeofence = false;

                            // Complete your incomplete location provider check logic here
                            try {
                                LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
                                boolean networkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
                                if (!networkEnabled) {
                                    new Handler(Looper.getMainLooper()).post(() -> {
                                        // Add logic to prompt user to enable location services
                                    });
                                }
                            } catch (Exception ex) {
                                Utils.appendLog("Error checking location provider: " + ex.getMessage(), "E", Constants.GEOFENCE);
                            }
                        });
            }
        }
    }

    // Helper methods (adjusted to fit JobIntentService)
    private GeofencingRequest getGeofencingRequest(ArrayList<Geofence> geofences) {
        return new GeofencingRequest.Builder()
                .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_EXIT)
                .addGeofences(geofences)
                .build();
    }

    private void removeGeofencesAndStartTracking() {
        if (mGeofencingClient != null) {
            mGeofencingClient.removeGeofences(getGeofencePendingIntent(this))
                    .addOnSuccessListener(aVoid -> {
                        Utils.appendLog("Geofence removed successfully", "I", Constants.TRACKER + "#updategeo");
                        PSLocationService.getInstance(this).startTracking();
                    })
                    .addOnFailureListener(e -> {
                        Utils.appendLog("Failed to remove geofence: " + e.getMessage(), "E", Constants.GEOFENCE);
                    });
        }
    }
}

Step 4: Register Components in AndroidManifest.xml

Make sure to add the BroadcastReceiver and JobIntentService with the required permission:

<receiver android:name=".GeofenceBroadcastReceiver" />

<service
    android:name=".GeofenceJobIntentService"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:exported="false" />

Key Fixes & Best Practices

  • PendingIntent Type: Using getBroadcast() instead of getService() avoids Android 8+ background service restrictions.
  • JobIntentService Launch: Always use enqueueWork() instead of direct service starts—this is non-negotiable for JobIntentService.
  • Android 12+ Compliance: Added FLAG_IMMUTABLE to the PendingIntent to avoid runtime crashes.
  • State Reset: Reset isSettingGeofence on failure to ensure future Geofence attempts work.
  • Resource Management: Used try-with-resources for Realm to prevent memory leaks.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:46:41