首页 > 解决方案 > 地理围栏 PendingIntent 始终为空

问题描述

我已经在应用程序中实现了地理围栏。但问题是每当我打开应用程序时,都会再次添加地理围栏。因此,如果我们在地理围栏区域内,它会立即触发进入警报。

当前实施 - 工作流程:

  1. 用户打开应用
  2. 一个被调用的服务GeoFenceMakerService是从主活动开始的。
  3. GeoFenceMakerService调用 API 并从服务器获取地点的详细信息(纬度、经度、地点 ID、地点名称、半径等)。可以有一个或多个地方。
  4. 地点详细信息(API 结果)将保存到本地数据库。
  5. 如果有 1 个或多个地点,则它为每个地点构建地理围栏对象 (initializeGeoFence)。
  6. 构建地理围栏请求并创建待处理的意图。这将传递给地理围栏客户端以添加地理围栏。( geofencingClient.addGeofences(getGeofencingRequest(), createGeoFencePendingIntent()))
  7. GeoFenceMakerService添加地理围栏后停止。

问题:总是 在createGeoFencePendingIntent()创建新的待处理意图,如果我们在打开应用程序时处于地理围栏区域内,可能会触发进入警报。

从堆栈溢出中找到了类似的解决方案:是使用共享首选项来执行检查未决意图。您可以从这里查看完整的解决方案。但我对此并不满意,因为“最好检查未决意图是否存在,而不是在我们添加了地理围栏的共享首选项中写入,因为当我们重新启动手机或更改位置精度时,它们会被删除并且未决意图不再存在,但不会更新共享首选项” - (其中一个已经提到作为对提到的解决方案的评论)。

当前需要的解决方案: 需要检查地理围栏是否已经添加,只有在不存在时才添加地理围栏。

我附上整个GeoFenceMakerService代码以供参考。

package com.****.*****.services;

import android.Manifest;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;

import com.****.****.api.Api;
import com.****.****.api.ApiMethods;
import com.****.****.models.Place;
import com.****.****.models.response.PlacesListResponse;
import com.****.****.receiver.GeofenceBroadcastReceiver;
import com.****.****.utils.DatabaseManager;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingClient;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;

import java.util.ArrayList;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class GeoFenceMakerService extends Service {

    public static final String TAG = "GeoFenceMakerService";
    private ArrayList<Place> places = null;
    private GeofencingClient geofencingClient = null;
    private ArrayList<Geofence> geofenceList = null;
    private PendingIntent geoFencePendingIntent;

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

    @Override
    public void onCreate() {
        Log.d(TAG, "GeoFenceMakerService Created");
        geofencingClient = LocationServices.getGeofencingClient(this);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //handleStart(intent, startId);
        Log.d(TAG, "Inside onStartCommand");
        getPlacesList();
        return START_NOT_STICKY;
    }

    private void getPlacesList() {
        Log.i(TAG, "getPlacesList: ");
        ApiMethods apiMethods;
        Call<PlacesListResponse> listResponseCall;
        apiMethods = Api.getInstance(this);
        listResponseCall = apiMethods.getAllPlacesList();
        Log.i(TAG, "Getting places list from API");
        listResponseCall.enqueue(new Callback<PlacesListResponse>() {
            @Override
            public void onResponse(Call<PlacesListResponse> call, Response<PlacesListResponse> response) {
                if (response.code() == Api.STATUS_200) {
                    handlePlacesList(response.body());
                } else {
                    Log.e(TAG, "Get places list api failed with code: " + response.code());
                }
            }

            @Override
            public void onFailure(Call<PlacesListResponse> call, Throwable t) {
                Log.e(TAG, "Get places list api failed" + t.getLocalizedMessage());
            }
        });
    }

    private void handlePlacesList(PlacesListResponse placesListResponse) {
        Log.i(TAG, "handlePlacesList: ");
        DatabaseManager databaseManager = new DatabaseManager(this);

        if (placesListResponse != null) {


            Log.i(TAG, "Got places list response");
            places = placesListResponse.getPlaces();
            databaseManager.savePlacesToDatabase(places);
            if (!(places.size() == 0)) {
                initializeGeoFence();
                addGeoFence2();
            }
           /* initializeGeoFence();
            addGeoFence2();*/
        } else {

            Log.e(TAG, "Places list response is null");

        }
    }

    private void initializeGeoFence() {
        // Toast.makeText(this, "initializing geo fence", Toast.LENGTH_LONG).show();
        Log.i(TAG, "initializing geo fence");
        Geofence geofence;
        geofenceList = new ArrayList<>();
        for (Place place : places) {
            Log.i(TAG, "Adding geofence at " + place.getLatitude() + ", " + place.getLongitude());
            // @TODO Fetch and set the Geofence Radius dynamically
            geofence = new Geofence.Builder()
                    // Set the request ID of the geofence. This is a string to identify this
                    // geofence.
                    .setRequestId(place.getPlaceId() + "")
                    .setCircularRegion(
                            place.getLatitude(),
                            place.getLongitude(),
                            500f)
                    .setExpirationDuration(60 * 60 * 1000)
                    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)
                    .build();

            geofenceList.add(geofence);
        }
    }

    private void addGeoFence2() {
        Log.i(TAG, "addGeoFence2: ");
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Make sure permission is granted before starting the service. NOTE: Requesting permission from Service is not allowed.
            return;
        }
        geofencingClient.addGeofences(getGeofencingRequest(), createGeoFencePendingIntent())
                .addOnSuccessListener(new OnSuccessListener<Void>() {
                    @Override
                    public void onSuccess(Void aVoid) {
                        Log.i(TAG, "Geo fence added");
                        // Toast.makeText(HomeActivity.this, "Geo fence added", Toast.LENGTH_LONG).show();
                        GeoFenceMakerService.this.stopSelf();
                    }
                })
                .addOnFailureListener(new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        Log.e(TAG, "Geo fence add failed. ");
                        e.printStackTrace();
                        // Toast.makeText(HomeActivity.this, "Geo fence add failed", Toast.LENGTH_LONG).show();
                        GeoFenceMakerService.this.stopSelf();
                    }
                });
    }

    private GeofencingRequest getGeofencingRequest() {

        // Toast.makeText(this, "getGeofencingRequest", Toast.LENGTH_LONG).show();
        Log.i(TAG, "getGeofencingRequest");
        GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
        builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
        builder.addGeofences(geofenceList);
        return builder.build();
    }

    private PendingIntent createGeoFencePendingIntent() {
        Intent intent;

        Log.d(TAG, "createGeoFencePendingIntent");

        if (geoFencePendingIntent == null) {
            Log.i(TAG, "GeoFence pending intent is null. Creating new instance");

            intent = new Intent(this, GeofenceBroadcastReceiver.class);
            // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
            // calling addGeofences() and removeGeofences().
            geoFencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.
                    FLAG_UPDATE_CURRENT);


        }

        return geoFencePendingIntent;
    }

}

标签: androidandroid-locationgeofencingandroid-geofence

解决方案


从定位服务发送的 Intent 可以触发您的应用程序中的各种操作,但您不应该让它启动活动或片段,因为组件应该只在响应用户操作时才可见。在许多情况下,BroadcastReceiver 是处理地理围栏转换的好方法。BroadcastReceiver 在事件发生时获取更新,例如进入或离开地理围栏的转换,并且可以开始长时间运行的后台工作。

在这里检查

private PendingIntent getGeofencePendingIntent() {
    if (geofencePendingIntent != null) {
        return geofencePendingIntent;
    }
    Intent intent = new Intent(this, GeofenceBroadcastReceiver.class);
        
    geofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    return geofencePendingIntent;
}

推荐阅读