Как оптимизировать батарею с помощью FUSED LOCATION API - Android

Привет, я столкнулся с проблемой / проблемой ниже с API местоположения в Android

  • Расход заряда батареи достигает 30–40%, что приводит к значительному разряду батареи.

  • Значок местоположения в строке состояния всегда включен, даже когда приложение закрыто, а при удалении приложения гаснет автоматически.

Требование:

  • Требуется местоположение пользователя при открытии приложения.
  • Мне нужно, чтобы местоположение пользователя было доступно, даже если приложение не открыто или не используется в зависимости от расстояния - требуется местоположение пользователя в фоновом режиме.

Подход:

  1. с GPS

  2. API использовал FUSED LOCATION API с ожидаемым намерением.

  3. LocationManager - для проверки состояния включения / выключения GPS.

Прохождение кода:

  1. в OnCreate я получаю экземпляр диспетчера местоположения - получение экземпляра диспетчера местоположения.

  2. проверка, включен ли GPS или доступно ли состояние сети, иначе покажите диалоговое окно для включения местоположения: КОД: -

    // get GPS state.
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    
    
        if (isGPSLocationEnabled(locationManager)) {
            buildGooleLocationApiClient();
        } else if (isNetworkLocationEnabled(locationManager)) {
            buildGooleLocationApiClient();
        } else {
            showAlert();
        }
    

Код для goolgeLocationAPiClient: в этом методе я проверяю версию Android, запрашиваю разрешение и включаю службы

private void buildGooleLocationApiClient() {

        if (Build.VERSION.SDK_INT >= 23) {

            int isFineLocationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
            int isCoarseLocationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION);

            if (isFineLocationPermission == PackageManager.PERMISSION_DENIED || isCoarseLocationPermission == PackageManager.PERMISSION_DENIED) {
                requestPermission();
            } else {
                checkGoogleLocationApiClient();
            }

        } else {
            checkGoogleLocationApiClient();
        }
    }

Создание клиента GoogleAPI:

private void checkGoogleLocationApiClient() {
        try {
            if (mGoogleApiClient != null) {
                if (mGoogleApiClient.isConnected()) {
                    getMyLocationCampaigns();
                } else {
                    mGoogleApiClient.connect();
                }
            } else {
                buildGoogleApiClient();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void getMyLocationCampaigns() {
        if (mCurrentLocation != null) {
            getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
        } else {
            try {
                mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
                getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
            } catch (SecurityException ex) {
                ex.printStackTrace();
                getData("","");
            }
        }
    }

private synchronized void buildGoogleApiClient() {
        try {
            Log.i(TAG, "activity Building GoogleApiClient===");
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();

            createLocationRequest();
        } catch (Exception e) {
            e.printStackTrace();
            getData("","");
        }
    }


    private void createLocationRequest() {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(60 * 60 * 1000);
        mLocationRequest.setFastestInterval(60 * 1000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        mLocationRequest.setSmallestDisplacement(100);

        connectGoogleApiClient();
    }


private void connectGoogleApiClient() {
        if (mGoogleApiClient != null) {
            if (!mGoogleApiClient.isConnected())
                mGoogleApiClient.connect();
        }
    }


@Override
    public void onLocationChanged(Location location) {
        mCurrentLocation = location;
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {

        if (mCurrentLocation == null) {
            try {
                mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
                if (mCurrentLocation != null) {
// MyAPICALL                    getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
                } else {
                    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,mLocationRequest, this);
                    mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
                    if (mCurrentLocation == null) {
                        if (locationManager != null) {
                            String provider = Utils.getUserLastLocation(locationManager);
                            if (provider != null) {
                                try {
                                    Location location = locationManager.getLastKnownLocation(provider);
                                    if (location != null) {
                                        getData(location.getLatitude() + "", location.getLongitude() + "");
                                    } else {
                                        getData("", "");
                                    }
                                } catch (SecurityException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    } else {
                        getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
                    }
                }
            } catch (SecurityException ex) {
                ex.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
                getData("","");
            }
        }
    }

Метод получения местоположения в фоновом режиме с ожидаемым намерением

private void startLocationUpdates() {
        try {


            Intent receiverIntentService = new Intent(this, LocationIntentService.class);
            PendingIntent pendingIntent = PendingIntent.getService(this, 1, receiverIntentService, 0);

            if (mGoogleApiClient != null) {
                if (mGoogleApiClient.isConnected()) {
                    LocationServices.FusedLocationApi.requestLocationUpdates(
                            mGoogleApiClient, mLocationRequest, pendingIntent);
                }

            }
        } catch (SecurityException se) {
            se.printStackTrace();
        }
    }

BroadCastReceiver: В случае перезапуска устройства:

public class LocationBroadcastReceiver extends BroadcastReceiver implements
        GoogleApiClient.ConnectionCallbacks,GoogleApiClient.OnConnectionFailedListener, LocationListener {


    Context context;
    protected GoogleApiClient mGoogleApiClient;
    protected LocationRequest mLocationRequest;
    protected Location mCurrentLocation;
    public static Boolean mRequestingLocationUpdates = false;

    SharedPreferences checkUserStatus;

    public LocationBroadcastReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        try {
            this.context = context;

            checkUserStatus = context.getSharedPreferences(Params.LOGIN_DETAILS_PREFERENCE, 0);
            String isUserLogedIn = checkUserStatus.getString(Params.TOKEN,"");

// if user is still logged in then only trigger background service
            if (!isUserLogedIn.equals("")) {
                buildGoogleApiClient();
                if (mGoogleApiClient != null) {
                    if (mGoogleApiClient.isConnected() && mRequestingLocationUpdates) {
                        startLocationUpdates();
                    } else {
                        buildGoogleApiClient();
                    }
                } else {
                    buildGoogleApiClient();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


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

    @Override
    public void onConnectionSuspended(int i) {
        mGoogleApiClient.connect();
    }


    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.i("Broadcast receiver", "Connection failed: ConnectionResult.getErrorCode() = " + connectionResult.getErrorCode());
    }


    @Override
    public void onLocationChanged(Location location) {
        mCurrentLocation = location;
    }


    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(context)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();

        createLocationRequest();
    }



    protected void createLocationRequest() {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(60 * 60 * 1000);
        mLocationRequest.setFastestInterval(60 * 1000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        mLocationRequest.setSmallestDisplacement(100);

    }

    protected void startLocationUpdates() {
        try {


            Intent receiverIntentService = new Intent(context,LocationIntentService.class);
            PendingIntent pendingIntent = PendingIntent.getService(context,1,receiverIntentService,0);

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


        }catch (SecurityException se) {
            se.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Мое намерение класс обслуживания: получить обновленное местоположение пользователя и выполнить вызов API

public class LocationIntentService extends IntentService {

    Context context;
    Bitmap myBitmap;

    URL url;

    SharedPreferences.Editor mMyLastLocationHolder;
    SharedPreferences mMyLastLocation;

    SharedPreferences checkUserStatus;


    public LocationIntentService() {
        super("LocationIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        if (intent != null) {
            Bundle bundle = intent.getExtras();
            if (bundle != null) {
                Location location = bundle.getParcelable("com.google.android.location.LOCATION");
                if (location != null) {
                    context = getApplicationContext();
// API call to server
                    updateAPI(location.getLatitude()+"",location.getLongitude()+"");

                    Log.v("TAG LOCATION ", " ==== " + location.getLatitude() + " - " + location.getLongitude() + " ==== ");
                    Log.v("TAG LOCATION ", " ==== calling my-campaigns near me ========");
                }
            }
        }

    }




    /**
     * Handle action Foo in the provided background thread with the provided
     * parameters.
     */
    private void handleActionFoo(String param1, String param2) {
        // TODO: Handle action Foo
        throw new UnsupportedOperationException("Not yet implemented");
    }

    /**
     * Handle action Baz in the provided background thread with the provided
     * parameters.
     */
    private void handleActionBaz(String param1, String param2) {
        // TODO: Handle action Baz
        throw new UnsupportedOperationException("Not yet implemented");
    }



}

person MobDev    schedule 19.12.2016    source источник
comment
Я делаю что-то подобное (я использовал долго работающую службу вместо IntentService) с FusedLocationApi, и мое использование батареи минимально (обновление местоположения каждые 3 минуты). Я подозреваю, что ваша проблема, скорее всего, исходит от LocationManager. Можете ли вы обойтись без этого, пытаясь увидеть?   -  person Gary99    schedule 19.12.2016


Ответы (3)


Надеюсь, это поможет вам найти лучшее решение / подход.

Лично предпочитаю использовать GoogleApiClient и LocationRequest с определенным приоритетом и интервалом. Напишите сервис, реализующий следующие интерфейсы:

  1. GoogleApiClient.ConnectionCallbacks
  2. GoogleApiClient.OnConnectionFailedListener
  3. LocationListener

    public class PositionService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {}

Используйте классы GoogleApiClient и LocationRequest.

В onCreate () создайте экземпляр объекта GoogleApiClient, объекта LocationRequest и установите соединение с mGoogleApiClient.

public void onCreate() {
    mGoogleApiClient = new GoogleApiClient.Builder(this)
                        .addConnectionCallbacks(this)
                        .addOnConnectionFailedListener(this)
                        .addApi(LocationServices.API)
                        .build();
    mLocationRequest = LocationRequest.create()
                    .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY)
                 .setInterval(mInterval).setFastestInterval(mFastInterval);
    mGoogleApiClient.connect();
}

В методе onDestroy () отключите mGoogleApiClient

@Override
public void onDestroy() {
    mGoogleApiClient.disconnect();
}

Теперь реализуем интерфейсы

@Override
public void onLocationChanged(Location location) {

    Log.d("NewLocation", location.toString());

}

@Override
public void onConnected(@Nullable Bundle bundle) throws SecurityException {
    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}

@Override
public void onConnectionSuspended(int i) {

}

@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

}

Теперь GoogleApiClient на основе настроек LocationRequest сообщит вам позицию, вызывающую обратный вызов onLocationChanged ().

Ваша бизнес-логика должна быть помещена в метод onLocationChanged (). Просто выберите подходящие интервалы времени и приоритет для LocationRequest. (см. документацию)

Обратитесь к официальной документации о стратегиях определения местоположения, мое решение основано на на том.

Я привык запускать службу на переднем плане, чтобы предотвратить неожиданное поведение операционной системы (например, отключение службы)

person Matt    schedule 19.12.2016
comment
Мэтт - спасибо за ответ, но зачем вам создавать отдельную службу, если вы можете использовать fusedlocationAPI с ожидающим намерением, которое служит этой цели. Фоновая служба потребляет больше энергии и требует явной обработки там, где работает fusedlocation. Я хочу знать, каков мыслительный процесс и почему вы предлагаете предлагаемое вами решение. - person MobDev; 20.12.2016
comment
я сослался на официальную документацию developer.android.com/guide/topics/location/ strategy.html и предлагает использовать LocationListener (или реализовать соответствующий интерфейс). Нет конкретной причины, по которой я создал отдельный сервис. - person Matt; 20.12.2016

Это только объяснит вам лучшую логику

Вместо длительной службы или IntentService просто используйте Firebase JobDispatcher или любую стороннюю библиотеку Jobscheduler API, чтобы переместить весь код обновления местоположения в Jobscheduler (https://github.com/googlesamples/android-JobScheduler/blob/master/Application/src/main/java/com/example/android/jobscheduler/service/MyJobService.java)

Начните задание в соответствии с интервалом обновления вашего местоположения, настройте или измените задание в соответствии с вашими требованиями !! это действительно лучшее решение по сравнению с долго работающей службой !!! (Вы можете использовать eventBus или RxBus для обновления местоположения в Activity или фрагменте !!)

Советы: каждый раз, когда задание запускается с обновлением местоположения до того, как задание закрывает настройку, возникает некоторая системная задержка в 3 секунды или более, потому что иногда Googleapiclient требуется больше времени для обновления нового обновленного времени GPS после задержки, которую вы можете закрыть Googleapiclient все нежелательные обратные вызовы с работающим JobService. Управляйте конфигурацией вакансий с помощью Google Awareness Api или Google Fit API, отслеживая активность пользователя!

All in one Job Jobscheduler Lib: https://github.com/evernote/android-job

P.S: код будет обновлен очень скоро

person LOG_TAG    schedule 21.12.2016

В документации указано

Действиям следует настоятельно рассмотреть возможность удаления всех запросов местоположения при входе в фоновый режим (например, в onPause ()) или, по крайней мере, заменить запрос на больший интервал и более низкое качество.

поэтому, когда я столкнулся с подобной проблемой, я сделал следующее:

  1. Я создал два запроса местоположения, первый имел приоритет PRIORITY_HIGH_ACCURACY и интервал 1 мин, а второй имел приоритет PRIORITY_LOW_POWER с внутренним значением 1 час и наименьшее смещение 1 км
  2. Когда приложение запускается, я использую первый запрос местоположения (высокий приоритет), чтобы получать более частые и точные обновления местоположения.
  3. Когда приложение переходит в фоновый режим, я переключаюсь на второй запрос местоположения (с низким приоритетом), чтобы снизить расход заряда батареи и при этом получать менее частые обновления местоположения.
  4. (Необязательно) Вы также можете получить процент заряда батареи при запуске приложения и выбрать в соответствии с ограничением (например, 15%), какой запрос местоположения вы можете использовать, когда приложение находится на переднем плане.

Эти шаги помогли мне снизить расход заряда батареи моего приложения с> 30% до <3%.

person no_name    schedule 18.01.2017