地理围栏 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 启动之前