如何在后台接收位置更新? (API 26)

时间:2018-07-09 19:26:41

标签: java android geolocation updates

我需要为我的应用程序从应用程序获取连续的位置更新。为此,我按照Android Developer Guide进行了位置更新。由于无法在API 26的后台接收位置更新,因此我添加了前台服务(Background location limits)。但是,当其他要求位置更新的活动处于前台时,我仍然只会收到更新。

位置服务:

public class LocationUpdateService extends Service {

private static final String TAG = LocationUpdateService.class.getSimpleName();

private static final String NOTIFICATION_CHANNEL_ID = "TrackNotification";
private static final int FOREGROUND_SERVICE_ID = 1;
private static final int NOTIFICATION_ID = 1;
public static final String STATUS_INTENT = "status";
private static final int CONFIG_CACHE_EXPIRY = 600;

private NotificationManager mNotificationManager;
private NotificationCompat.Builder mNotificationBuilder;

private DatabaseReference mDatabaseReference;

private FusedLocationProviderClient mFusedLocationProviderClient;
private LocationRequest mLocationRequest;

private FirebaseRemoteConfig mFirebaseRemoteConfig;

private String uid;

@Override
public void onCreate() {
    super.onCreate();

    uid = FirebaseAuth.getInstance().getUid();
    if(uid == null)
        stopSelf();

    mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
    FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder().build();
    mFirebaseRemoteConfig.setConfigSettings(configSettings);
    mFirebaseRemoteConfig.setDefaults(R.xml.remode_config_defaults);
    fetchRemoteConfig();

    mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
    mDatabaseReference = FirebaseDatabase.getInstance().getReference();

    mLocationRequest = new LocationRequest()
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
            .setFastestInterval(mFirebaseRemoteConfig.getLong("LOCATION_REQUEST_INTERVAL"))
            .setFastestInterval(mFirebaseRemoteConfig.getLong("LOCATION_REQUEST_INTERVAL_FASTEST"));

    bindNotification();
    setStatusMessage(R.string.connecting);


    startLocationTracking();
}

private LocationCallback mLocationCallback = new LocationCallback() {
    @Override
    public void onLocationResult(LocationResult locationResult) {
        Log.d(TAG,"Got location update!");
        if(locationResult == null)
            return;
        for(Location location : locationResult.getLocations()) {

            CustomLocation customLocation = LocationAdapter.toDatabaseLocation(location);
            mDatabaseReference.child("locations").child(uid).setValue(customLocation);
        }

    }

    @Override
    public void onLocationAvailability(LocationAvailability locationAvailability) {
        locationAvailability.isLocationAvailable();
        // TODO handle no location here
        super.onLocationAvailability(locationAvailability);
    }
};

@SuppressWarnings({"MissingPermission"})
private void startLocationTracking() {
    mFusedLocationProviderClient.requestLocationUpdates(mLocationRequest,mLocationCallback, Looper.myLooper());
}


private void fetchRemoteConfig() {
    mFirebaseRemoteConfig.fetch(CONFIG_CACHE_EXPIRY)
            .addOnCompleteListener(new OnCompleteListener<Void>() {
        @Override
        public void onComplete(@NonNull Task<Void> task) {
            Log.i(TAG,"Remote config fetched");
            mFirebaseRemoteConfig.activateFetched();
        }
    });
}

@Override
public void onDestroy() {
    setStatusMessage(R.string.not_tracking);
    mNotificationManager.cancel(NOTIFICATION_ID);
    mFusedLocationProviderClient.removeLocationUpdates(mLocationCallback);
    super.onDestroy();
}

@Nullable
@Override
public IBinder onBind(Intent intent) { return null; }

private void bindNotification() {
    mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    PendingIntent resultPendingIntent = PendingIntent.getActivity(this,0,
            new Intent(this, MainActivity.class),PendingIntent.FLAG_UPDATE_CURRENT);

    mNotificationBuilder = new NotificationCompat.Builder(this,NOTIFICATION_CHANNEL_ID)
            .setCategory(NotificationCompat.CATEGORY_STATUS)
            .setShowWhen(false)
            .setSmallIcon(R.drawable.ic_car)
    //        .setColor(getColor(R.color.colorPrimary))
            .setContentTitle(getString(R.string.app_name))
            .setOngoing(true)
            .setContentIntent(resultPendingIntent);
    startForeground(FOREGROUND_SERVICE_ID, mNotificationBuilder.build());
}

/**
 *
 * @param message Status message to display
 */
private void setStatusMessage(String message) {
    mNotificationBuilder.setContentText(message);
    mNotificationManager.notify(NOTIFICATION_ID, mNotificationBuilder.build());

    Intent intent = new Intent(STATUS_INTENT);
    intent.putExtra(getString(R.string.status),message);
    LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

private void setStatusMessage(int resID) {
    setStatusMessage(getString(resID));
}
}

我开始使用

startService(new Intent(this,LocationUpdateService.class));

Android清单:

<service android:name=".LocationUpdateService" />

EDIT1: 我现在在较旧的API版本(22)上对其进行了测试,问题仍然存在:只要某些具有位置请求的应用程序处于前台它有效,否则无效。 也许这是FusedLocationProviderClient的问题,但我不知道是什么。我只找到了旧的FusedLocationProvider API的代码示例,现已弃用。

1 个答案:

答案 0 :(得分:4)

您是否尝试过调试以确保服务正常运行?

这听起来很愚蠢,但是您是否检查了清单中是否已注册服务?我知道我肯定已经遇到了这个问题。

<service android:name=".LocationService"
        android:label="Location Service"
        android:exported="true"
        android:enabled="true"
        android:process=":location_background_service"/>

为了获取位置,在设置我的位置时,我创建了一个实现android.location.LocationListener的类。

private class LocationListener implements android.location.LocationListener {
    Location mLastLocation;

    public LocationListener(String provider) {
        Log.e(TAG, "LocationListener " + provider);
        mLastLocation = new Location(provider);
    }

    @Override
    public void onLocationChanged(Location location) {
        Log.e(TAG, "onLocationChanged: " + location);
        mLastLocation.set(location);
    }

    @Override
    public void onProviderDisabled(String provider) {
        Log.e(TAG, "onProviderDisabled: " + provider);
    }

    @Override
    public void onProviderEnabled(String provider) {
        Log.e(TAG, "onProviderEnabled: " + provider);
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        Log.e(TAG, "onStatusChanged: " + provider);
    }
}

您可以为不同的提供程序创建多个实例。就我而言,我最终使用了2。

LocationListener[] mLocationListeners = new LocationListener[]{
        new LocationListener(LocationManager.GPS_PROVIDER),
        new LocationListener(LocationManager.NETWORK_PROVIDER)
};

然后,我初始化了一个LocationManager,可以为每个LocationListener设置轮询速率。

private void initializeLocationManager() {
    Log.e(TAG, "initializeLocationManager");
    if (mLocationManager == null) {
        mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
    }
}

然后在您的Service onCreate函数中,初始化LocationManager,并将其中一个侦听器用作主要来源,另一个作为后备。

try {
        mLocationManager.requestLocationUpdates(
                LocationManager.NETWORK_PROVIDER,
                5 * 60 * 1000, //5 Minutes
                1F /*METERS*/,
                mLocationListeners[0]
        );
    } catch (java.lang.SecurityException ex) {
        Log.e(TAG, "failed to request location update. Insufficient permissions. ", ex);
        try {
            mLocationManager.requestLocationUpdates(
                    LocationManager.NETWORK_PROVIDER,
                    5 * 60 * 1000, //5 Minutes
                    1F /*METERS*/,
                    mLocationListeners[1]
            );
        } catch (java.lang.SecurityException e) {
            Log.e(TAG, "failed to request location update. Insufficient permissions. ", e);
        } catch (IllegalArgumentException e) {
            Log.e(TAG, "Network provider does not exist.", e);
        }
    } catch (IllegalArgumentException ex) {
        Log.e(TAG, "Network provider does not exist.", ex);
    }
}

(很抱歉,如果这段代码很粗糙,这是一个快速而肮脏的示例。)