【问题标题】:How to receive location updates in background? (API 26)如何在后台接收位置更新? (API 26)
【发布时间】:2018-07-09 19:26:41
【问题描述】:

我需要从应用程序中为我的应用程序获取持续的位置更新。为此,我关注了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 的代码示例,现在已弃用。

【问题讨论】:

标签: java android geolocation updates


【解决方案1】:

您是否尝试过调试以确保您的服务被命中?

这听起来可能很傻,但是您是否检查过您的服务是否已在清单中注册?我知道我肯定遇到过这个问题。

<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)
};

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

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);
    }
}

(对不起,如果这段代码很粗俗,这是一个快速而肮脏的例子。)

【讨论】:

  • 是的。通知正在显示,如果我在前台运行像谷歌地图这样的应用程序,我会得到位置更新。
  • 进程属性呢?
  • 据我了解,由于该服务与实际应用程序是分开的,因此您可以给前台服务起任意名称,以便您可以更轻松地进行调试。
  • 我在控制台上获得了所有调试信息。如果我在前台运行地图,我会收到消息“获取位置更新”。但只有那时。似乎问题出在 Android 背景限制上,但我不确定。
  • 你试过用这个调试它吗? stackoverflow.com/a/32080569/7655390 控制台不会显示来自前台服务的消息,除非您更改正在侦听的进程,因为它们在技术上是独立的进程。
猜你喜欢
  • 1970-01-01
  • 2016-02-18
  • 2020-11-08
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 2013-01-12
  • 1970-01-01
相关资源
最近更新 更多