【问题标题】: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

标签: android gps android-service android-gps


【解决方案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 已开启且可访问。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-30
    • 2021-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多