不在前台时位置服务被杀死
Posted
技术标签:
【中文标题】不在前台时位置服务被杀死【英文标题】:Location service is being killed when not in foreground 【发布时间】:2021-11-16 21:52:29 【问题描述】:我正在为出租车司机开发一个应用程序,该应用程序将车辆的位置报告给调度 web 应用程序。我已经设法使用 FusedLocationProviderClient 可靠地获取设备位置,并通过前台服务发送位置更新。不幸的是,我的平板电脑(Huawei MediaPad T5 - android Oreo 8.0)在应用程序不在前台时主动终止服务。根据 Android 文档,我这样做的方式是正确的(如果不是,请随时纠正我)。
我在下面附上服务源代码:
import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import driver.taxis.dcsoft.cz.R;
import driver.taxis.dcsoft.cz.communication.CommunicatorManager;
import driver.taxis.dcsoft.cz.gui.activities.MainActivity;
import driver.taxis.dcsoft.cz.preferences.PreferencesConstants;
import driver.taxis.dcsoft.cz.preferences.PreferencesManager;
public class UpdateLocationService extends Service
private static final String TAG = "LocationService";
private FusedLocationProviderClient mFusedLocationClient;
private final static long UPDATE_INTERVAL = 15 * 1000; /* 4 secs */
private final static long FASTEST_INTERVAL = 7 * 1000; /* 2 sec */
private LocationCallback locationCallback;
@Override
public void onCreate()
super.onCreate();
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
if (Build.VERSION.SDK_INT >= 26)
String CHANNEL_ID = "my_channel_01";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
"My Channel",
NotificationManager.IMPORTANCE_DEFAULT);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setOngoing(true)
.setContentTitle("Aplikace " + MainActivity.getContext().getString(R.string.app_name) + " odesílá polohu na pozadí.")
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
@Override
public int onStartCommand(Intent intent, int flags, int startId)
Log.d(TAG, "onStartCommand: called.");
getLocation();
return START_NOT_STICKY;
@Override
public void onDestroy()
super.onDestroy();
if(locationCallback != null)
mFusedLocationClient.removeLocationUpdates(locationCallback);
@Nullable
@Override
public IBinder onBind(Intent intent)
return null;
private void getLocation()
// Create the location request to start receiving updates
LocationRequest mLocationRequestHighAccuracy = new LocationRequest();
mLocationRequestHighAccuracy.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequestHighAccuracy.setInterval(UPDATE_INTERVAL);
mLocationRequestHighAccuracy.setFastestInterval(FASTEST_INTERVAL);
// new Google API SDK v11 uses getFusedLocationProviderClient(this)
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
Log.d(TAG, "getLocation: stopping the location service.");
stopSelf();
return;
Log.d(TAG, "getLocation: getting location information.");
locationCallback = new LocationCallback()
@Override
public void onLocationResult(LocationResult locationResult)
Log.d(TAG, "onLocationResult: got location result.");
Location location = locationResult.getLastLocation();
if (location != null)
String vehicleId = PreferencesManager.loadStringPreference(PreferencesConstants.SPZ_STRING, "");
if(vehicleId.isEmpty() || vehicleId == null)
return;
CommunicatorManager.INSTANCE.sendPositionRequest(location);
;
mFusedLocationClient.requestLocationUpdates(mLocationRequestHighAccuracy, locationCallback,
Looper.myLooper()); // Looper.myLooper tells this to repeat forever until thread is destroyed
【问题讨论】:
尝试改用 ContextCompat.startForeground 【参考方案1】:很遗憾,问题不在您的代码中。
在 Android (6+) 中,没有任何服务可以在所有设备品牌/类型上无限期地以安全的方式运行,而无需用户采取任何预防措施,因为任何设备品牌都以不同的方式实施限制、终止、或确保后台服务/应用程序的安全(是的,这很混乱)。
作为第一步,用户应转到 Android 设置并关闭您应用的所有电池监控和优化。在 Android 9+ 上,他们还应检查应用程序是否不受后台限制,并验证是否允许后台活动。
在某些品牌上,他们必须将后台应用程序列入白名单,而在另一些品牌上,您必须设置“高性能”省电模式。
举个具体的例子,在 Android 11 上,三星将默认阻止应用在后台运行,除非用户将应用排除在电池优化之外:他应该继续 Android 设置 -> 应用 -> YOUR_APP -> 电池 - > 电池优化 -> 所有应用 -> YOUR_APP -> 不优化。
此外,其他电池优化器(例如 Digibites 的 AccuBattery)可能会限制后台使用,而且一些防病毒软件对长时间运行的应用程序非常具有攻击性,因此必须正确设置。
也许在您的情况下,更新时间并不重要,您可以使用普通的粘性服务 (START_STICKY
);
作为替代方案,您应该检查前台服务是否具备在后台安全运行的所有条件(其中一些是可检查的,有些则不是)。
您可以查看 GPS 前台服务的完整示例here。 它可以在后台可靠地工作(它还包括一个唤醒锁以防止手机休眠)。 它运行良好,但在某些设备上,用户必须禁用上述优化。
app 负责检查是否允许后台活动:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
ActivityManager activityManager = (ActivityManager)getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
if ((activityManager != null) && (activityManager.isBackgroundRestricted()))
isBackgroundActivityRestricted = true;
else
isBackgroundActivityRestricted = false;
else
isBackgroundActivityRestricted = false;
并且 GPS 已开启且可访问。
【讨论】:
以上是关于不在前台时位置服务被杀死的主要内容,如果未能解决你的问题,请参考以下文章
当应用程序切换到后台时,我的 android 位置服务被终止