Периодические обновления местоположения Android с помощью workmanager

Я использую WorkManager следующим образом -

class LocationWorker(
    ctx: Context, params: WorkerParameters
) : CoroutineWorker(ctx, params), KoinComponent {

    private val locationDataRepository: LocationDataRepository by inject()

    override suspend fun doWork(): Result {
        return try {
            locationDataRepository.triggerLocationUpdates()
            Result.success()
        } catch (e: Exception) {
            Result.failure()
        }
    }
}

Я активирую Рабочего как -

val myWorker =
            PeriodicWorkRequestBuilder<LocationWorker>(
                15,
                TimeUnit.MINUTES
            ).addTag(
                "location"
            ).build()
        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "location",
            ExistingPeriodicWorkPolicy.KEEP,
            myWorker
        )

Как видите, минимальный период WorkManager составляет 15 минут. Я хочу отслеживать местоположение с очень короткими интервалами, например, каждые несколько секунд, а также хочу, чтобы местоположение отслеживалось, даже когда экран телефона выключен. Подходит ли WorkManager для моих требований или вы можете предложить мне другой API?


person Ma2340    schedule 20.09.2019    source источник
comment
Я думаю, что да, пожалуйста, прочтите этот пост в блоге, и вы поймете, что это тесто. Удачного кодирования :) medium.com/ Google-разработчики-эксперты /   -  person umer farooq    schedule 20.09.2019
comment
@umerfarooq, спасибо :) Но менеджер работы предоставляет минимальный интервал в 15 минут .. Что, если я хочу делать каждые 1 минуту?   -  person Ma2340    schedule 20.09.2019
comment
WorkManager не предназначен для запуска задач каждую секунду, так как у него есть две опции для создания рабочего запроса: и PeriodicWorkRequest - запускает повторяющуюся задачу каждые 15 минут, даже если мы изменим временной интервал на anyhwere ‹15 минут, по умолчанию он будет выполняться в течение 15 минут. только минут. OneTimeWorkRequest - запускается один раз   -  person umer farooq    schedule 20.09.2019
comment
Вы можете реализовать планировщик заданий, но он разрядит батарею.   -  person umer farooq    schedule 20.09.2019
comment
@umerfarooq Так есть ли другие альтернативы?   -  person Ma2340    schedule 20.09.2019
comment
Я думаю, что планировщик заданий firebase. Но продолжайте поиски и другие люди тоже. :)   -  person umer farooq    schedule 20.09.2019
comment
@Maria Я использую службу переднего плана, чтобы получать обновления местоположения каждые 30 секунд и сохранять их в базе данных, чтобы отправлять их на сервер каждые 15 минут с помощью диспетчера аварийных сигналов.   -  person coderBox    schedule 27.11.2019
comment
@Coderbox да, даже я использую службу переднего плана для получения обновлений местоположения. Однако недостатком является то, что служба переднего плана не может работать, когда приложение убито.   -  person Ma2340    schedule 27.11.2019


Ответы (2)


Это может помочь вам, это работает, даже когда приложение убито ... Меня немного беспокоит, когда устройство переходит в режим ожидания, поскольку gps не может быть доступен, когда устройство неподвижно

Служба переднего плана основная цель - запускаться с постоянным уведомлением при завершении работы приложения

LocationService.class

public class LocationService extends Service implements
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
    public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000 * 30; //30 secs
    public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
            UPDATE_INTERVAL_IN_MILLISECONDS / 2;
    protected static final String TAG = "LocationUpdateService";
    public static final int HORIZONTAL_ACCURACY_IN_METERS = 100;

    /**
     * The identifier for the notification displayed for the foreground service.
     */
    private static final int NOTIFICATION_ID = 12345678;

    public static boolean mRequestingLocationUpdates = false;
    public boolean isEnded = false;
    protected GoogleApiClient mGoogleApiClient;
    protected LocationRequest mLocationRequest;



    private SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
    private double latitude = 0;
    private double longitude = 0;
    private float[] results1 = new float[1];
    private float distanceInMeters = 0;
    private Handler mHandler;


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


        String CHANNEL_ID = "FOREGROUND_CHANNEL";
        if (Build.VERSION.SDK_INT >= 26) {

            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "location_notification",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        }

        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setOngoing(true)
                .setContentTitle("G Star")

                .setSmallIcon(R.drawable.gm_noti_logo)
                .setContentText("Running").build();

        startForeground(NOTIFICATION_ID, notification);
        Utility.getInstance().makeDescriptiveLogs("ON CREATE WAS HIT");

        sendLocationDataToServerPeriodically();


    }

    private void sendLocationDataToServerPeriodically() {


//getting the alarm manager
        AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

        //creating a new intent specifying the broadcast receiver
        Intent intentLR = new Intent(this, PostLocationReceiver.class);

        PendingIntent pi = PendingIntent.getBroadcast(this, 0, intentLR,
                PendingIntent.FLAG_UPDATE_CURRENT);


        if (android.os.Build.VERSION.SDK_INT >= 23) {
            assert am != null;
            am.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
        } else if (Build.VERSION.SDK_INT >= 19) {
            if (am != null) {
                am.setInexactRepeating(AlarmManager.RTC_WAKEUP,
                        System.currentTimeMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
            }
        } else {
            if (am != null) {
                am.setRepeating(AlarmManager.RTC_WAKEUP,
                        System.currentTimeMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
            }
        }


    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        isEnded = false;

        Utility.getInstance().makeDescriptiveLogs("ONSTART COMMAND WAS HIT");
        buildGoogleApiClient();
        if (mGoogleApiClient.isConnected() && mRequestingLocationUpdates) {
            startLocationUpdates();
        }
        mHandler = new Handler();
        return START_STICKY;
    }

    @Override
    public void onConnected(Bundle bundle) {
        startLocationUpdates();
    }

    @Override
    public void onConnectionSuspended(int i) {
        // The connection to Google Play services was lost for some reason. We call connect() to
        // attempt to re-establish the connection.

        mGoogleApiClient.connect();
    }

    @Override
    public void onLocationChanged(Location location) {

        if (location.getAccuracy() < HORIZONTAL_ACCURACY_IN_METERS)
            updateUI(location);
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        // Refer to the javadoc for ConnectionResult to see what error codes might be returned in
        // onConnectionFailed.

    }

    protected synchronized void buildGoogleApiClient() {

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();

        createLocationRequest();
    }

    /**
     * Updates the latitude, the longitude, and the last location time in the UI.
     */
    private void updateUI(Location mCurrentLocation) {



        mHandler.post(() -> {
            /*GET DEVICE CURRENT BATTERY LEVEL*/
            int batteryPercent = Utility.getInstance().getBatteryPercentage(LocationService.this);


            /*  CALCULATE DISTANCE BETWEEN LAT LONG INTERVALS*/
            if (latitude != 0 && longitude != 0) {
                Location.distanceBetween(latitude, longitude, mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(), results1);
                distanceInMeters = results1[0];
            }


            latitude = mCurrentLocation.getLatitude();
            longitude = mCurrentLocation.getLongitude();

            /*CHECK IF DEVICE HAS ACTIVE INTERNET CONNECTION*/
            String networkStatus = Utility.getInstance().checkConnection(LocationService.this) ? "1" : "0";


            /*CHECK NETWORK SIGNAL STRENGTH*/
            String signalStrength = Utility.getInstance().getSignalStrength(LocationService.this);

            SQLiteDBHandler db = SQLiteDBHandler.getInstance(LocationService.this);
            db.insertDeviceLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(), Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? mCurrentLocation.getSpeedAccuracyMetersPerSecond() : mCurrentLocation.getSpeed(), sdf.format(Calendar.getInstance().getTime()), distanceInMeters, batteryPercent, networkStatus, signalStrength);


        });

    }

    protected void createLocationRequest() {
        mGoogleApiClient.connect();
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setSmallestDisplacement(5);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    /**
     * Requests location updates from the FusedLocationApi.
     */
    public void startLocationUpdates() {
        if (!mRequestingLocationUpdates) {
            mRequestingLocationUpdates = true;

            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }


            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, this);

            isEnded = true;
        }
    }

    /**
     * Removes location updates from the FusedLocationApi.
     */
    public void stopLocationUpdates() {
        if (mRequestingLocationUpdates) {
            mRequestingLocationUpdates = false;



            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);

        }
    }

    @Override
    public void onDestroy() {

        mHandler.removeCallbacksAndMessages(null);

        stopLocationUpdates();

        super.onDestroy();
    }


}
person coderBox    schedule 27.11.2019
comment
Так ты говоришь, что нам не нужен WorkManager? - person IgorGanapolsky; 30.04.2020
comment
@IgorGanapolsky Нет, я поделился тем, что работает для меня, чтобы это могло быть полезно для кого-то. Это была бы лучшая реализация с WorkManager вместо AlarmManager ... немного больше сока для устройства - person coderBox; 19.06.2020

Думаю, что эта codelab - лучшее решение. Без WorkManager, но для меня он работает в AccessibilityService (я понял, что это как альтернатива ForegroundService), чтобы избежать постоянного уведомления https://codelabs.developers.google.com/codelabs/background-location-updates-android-o/#0

person Mister Z.    schedule 10.12.2019