地理围栏 PendingIntent 始终为空

Posted

技术标签:

【中文标题】地理围栏 PendingIntent 始终为空【英文标题】:Geofence PendingIntent is always getting as null 【发布时间】:2020-05-29 22:56:01 【问题描述】:

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

当前实施 - 工作流程:

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

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

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

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

我附上整个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;
    


【问题讨论】:

【参考方案1】:

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

check here

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;

【讨论】:

我遵循了相同的方法,您可以在我附加的代码中看到相同的实现(参见createGeoFencePendingIntent() 函数)。但问题是这个函数总是在创建新的意图,而不是检查是否有任何待处理的意图退出。 geofencePendingIntent -> 签入 null

以上是关于地理围栏 PendingIntent 始终为空的主要内容,如果未能解决你的问题,请参考以下文章

onHandleWork 从未收到来自 pendingintent 地理围栏的意图

地理围栏转换 PendingIntent 被 Android Oreo 上的操作系统阻止

Android 地理围栏是不是保持活动状态直到删除/过期或仅在我的 PendingIntent 启动之前

应用程序死机时的地理围栏

在 Android 12/API 31 中,地理围栏不适用于 IMMUTABLE 未决意图。为啥?

phonegap 安卓地理定位。某些属性始终为空