android - 地理围栏 PendingIntent 始终为空
问题描述
我已经在应用程序中实现了地理围栏。但问题是每当我打开应用程序时,都会再次添加地理围栏。因此,如果我们在地理围栏区域内,它会立即触发进入警报。
当前实施 - 工作流程:
- 用户打开应用
- 一个被调用的服务
GeoFenceMakerService
是从主活动开始的。 GeoFenceMakerService
调用 API 并从服务器获取地点的详细信息(纬度、经度、地点 ID、地点名称、半径等)。可以有一个或多个地方。- 地点详细信息(API 结果)将保存到本地数据库。
- 如果有 1 个或多个地点,则它为每个地点构建地理围栏对象 (initializeGeoFence)。
- 构建地理围栏请求并创建待处理的意图。这将传递给地理围栏客户端以添加地理围栏。(
geofencingClient.addGeofences(getGeofencingRequest(), createGeoFencePendingIntent())
) 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;
}
}
解决方案
从定位服务发送的 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;
}
推荐阅读
- time-series - How to transform multiple unaligned time series which arrived in different interval and depends on each other
- javafx - Is there an event when for example to detect when a text field loses focus?
- javascript - 如何在 webpack 中使用别名来覆盖 node_modules 中已经存在的依赖项?
- ionic-framework - Ionic 3. NavController 的问题。当我注销时它停止正常工作
- linux - Transform table into 2 columns
- android - 外部数据库的Android SQLite“没有这样的表”异常
- python-3.x - Instance Counting Properly with Sub Classes
- c - 向进程组中除自身以外的所有进程发出信号
- python - sqlalchemy 无法识别我的 mysql 数据库
- gnuradio - osmocom 源和 rtl-sdr 源的区别?