Java 如何在后台接收位置更新?(API 26)
我需要从应用程序中获取我的应用程序的连续位置更新。为此,我遵循了获取位置更新的步骤。因为在API 26中无法在后台接收位置更新,所以我添加了一个前台服务()。但是,我仍然只在前台有其他请求位置更新的活动时接收更新 定位服务:Java 如何在后台接收位置更新?(API 26),java,android,geolocation,updates,Java,Android,Geolocation,Updates,我需要从应用程序中获取我的应用程序的连续位置更新。为此,我遵循了获取位置更新的步骤。因为在API 26中无法在后台接收位置更新,所以我添加了一个前台服务()。但是,我仍然只在前台有其他请求位置更新的活动时接收更新 定位服务: public class LocationUpdateService extends Service { private static final String TAG = LocationUpdateService.class.getSimpleName(); pri
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));
}
}
Android清单:
<service android:name=".LocationUpdateService" />
EDIT1:我现在在较旧的API版本(22)上测试了它,但问题仍然是一样的:只要一些带有位置请求的应用在前台,它就可以工作,否则就不行。
也许FusedLocationProviderClient有问题,但我不知道是什么问题。我只找到了使用旧FusedLocationProvider API的代码示例,该API现在已被弃用。您是否尝试过调试以确保您的服务被命中 这听起来可能很愚蠢,但您是否检查过您的服务是否已在您的清单中注册?我知道我肯定遇到过这个问题
<service android:name=".LocationService"
android:label="Location Service"
android:exported="true"
android:enabled="true"
android:process=":location_background_service"/>
可以为不同的提供程序创建多个实例。在我的例子中,我最终使用了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);
}
}
然后在ServiceOnCreate函数中,初始化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);
}
}
(很抱歉,如果此代码很糟糕,这是一个快速而肮脏的示例。)您是否尝试过调试以确保您的服务被命中 这听起来可能很愚蠢,但您是否检查过您的服务是否已在您的清单中注册?我知道我肯定遇到过这个问题
<service android:name=".LocationService"
android:label="Location Service"
android:exported="true"
android:enabled="true"
android:process=":location_background_service"/>
可以为不同的提供程序创建多个实例。在我的例子中,我最终使用了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);
}
}
然后在ServiceOnCreate函数中,初始化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);
}
}
(很抱歉,如果此代码很糟糕,这是一个快速而肮脏的示例。)您可以尝试此问题的答案:您可以尝试此问题的答案:是。通知会显示出来,如果我在前台运行像Google maps这样的应用程序,我会得到位置更新。那么进程属性是什么呢?据我所知,由于服务与实际应用程序是分开的,这是一个任意的名字,你给你的前台服务,这样你可以调试它更容易。我在控制台上得到所有调试信息。如果我在前台运行“地图”,我会收到消息“获取位置更新”。但只有到那时。看起来问题出在Android的后台限制上,但我不确定。你试过用这个调试它吗?控制台不会显示来自前台服务的消息,除非您更改正在侦听的进程,因为它们在技术上是独立的进程。是的。通知会显示出来,如果我在前台运行像Google maps这样的应用程序,我会得到位置更新。那么进程属性是什么呢?据我所知,由于服务与实际应用程序是分开的,这是一个任意的名字,你给你的前台服务,这样你可以调试它更容易。我在控制台上得到所有调试信息。如果我在前台运行“地图”,我会收到消息“获取位置更新”。但只有到那时。看起来问题出在Android的后台限制上,但我不确定。你试过用这个调试它吗?控制台不会显示来自前台服务的消息,除非您更改正在侦听的进程,因为它们在技术上是独立的进程。