[英]Geofence events not always called
這是我添加地理圍欄的方式:
public void setGeofenceRequest(Location location) {
if (geofences == null) {
geofences = new ArrayList<Geofence>();
}
geofences.add(new Geofence.Builder()
.setRequestId("3")
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_EXIT)
.setCircularRegion(
location.getLatitude(), location.getLongitude(), PSLocationService.getInstance(context).kPSGeofencingDistanceMedium)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.build());
Intent intent = new Intent(context, ReceiveTransitionsBroadcastReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
if (geofences.size() > 0) {
LocationServices.GeofencingApi.addGeofences(mLocationClient, geofences, pi);
Log.i("", "geof autopilot2 will set geofence for autopilot-3");
}
}
這是我的BroadcastReceiver。 我應該在哪里收到它們:
public class ReceiveTransitionsBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
Log.i("","autopilot valid geof on receive transisionts broadcast receiver");
PSMotionService.getInstance(ctx).buildGoogleApiClient();
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
int transitionType = geofencingEvent.getGeofenceTransition();
Location geofenceCenter = PSApplicationClass.getInstance().pref.getGeoCenter(ctx);
if(geofencingEvent.getTriggeringLocation() != null) {
if (geofenceCenter != null) {
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver TRIGGERING LOCATION: " + geofencingEvent.getTriggeringLocation().toString() + " / GEOFENCE CENTER: " + geofenceCenter.getLatitude() + ", " + geofenceCenter.getLongitude(), "D", Constants.TRACKER);
} else
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver TRIGGERING LOCATION: " + geofencingEvent.getTriggeringLocation().toString(), "D", Constants.TRACKER);
}else Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver ERROR => TRIGGERING LOCATION NULL", "D", Constants.TRACKER);
if(transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
List<Geofence> triggerList = geofencingEvent.getTriggeringGeofences();
for (Geofence geofence : triggerList) {
Log.i("", "geof is s receive transition broadcast receiver " + transitionType + " GPS zone " + geofence.getRequestId());
if(geofence.getRequestId().contentEquals("3")) {
Log.i("", "geof autopilot2 ENTERED GEOFENCE will start pilot with first location");
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver check to see if should start pilot", "T", Constants.TRACKER);
PSLocationService.getInstance(ctx).fastGPS = -1;
PSLocationService.getInstance(ctx).RequestLocationUpdates();
if(PSTrip.getActiveTrip() != null) {
PSLocationService.getInstance(ctx).removeAutoPilotGeofence();
}else PSMotionService.getInstance(ctx).checkinTime = System.currentTimeMillis() / 1000;
}
}
}
}
}
現在通常它起作用,但並非總是如此。 我會說只有大約75%的時間它應該工作,地理圍欄事件實際上被稱為。 我覺得自從我設置地理圍欄以來的時間越多,被調用的可能性就越小。 為什么會這樣? 當垃圾收集器清理應用程序時,觸發事件是否也被解除? 我怎么能做到這一點,以便我的地理圍欄總是被調用,當案件?
這是我的defaultConfig:
defaultConfig {
minSdkVersion 15
targetSdkVersion 23
ndk {
moduleName "ndkVidyoSample"
}
}
我從廣播接收器更改為IntentService:
public class PSGeofenceTransitionsIntentService extends IntentService {
private static ActivityManager manager;
private static PSGeofenceTransitionsIntentService instance;
private GeofencingClient mGeofencingClient;
Context context;
private PendingIntent mGeofencePendingIntent;
public static boolean isMyServiceRunning(Class<?> serviceClass) {
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
public static PSGeofenceTransitionsIntentService getInstance(Context context) {
if (instance == null) {
// Create the instance
instance = new PSGeofenceTransitionsIntentService(context);
}
if (!isMyServiceRunning(PSGeofenceTransitionsIntentService.class)) {
Intent bindIntent = new Intent(context, PSGeofenceTransitionsIntentService.class);
context.startService(bindIntent);
}
// Return the instance
return instance;
}
public PSGeofenceTransitionsIntentService() {
super("GeofenceTransitionsIntentService");
}
public PSGeofenceTransitionsIntentService(Context context) {
super("GeofenceTransitionsIntentService");
mGeofencingClient = LocationServices.getGeofencingClient(context);
manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
instance = this;
this.context = context;
}
protected void onHandleIntent(Intent intent) {
Log.i("", "autopilot valid geof on receive transisionts broadcast receiver");
PSMotionService.getInstance(context).buildGoogleApiClient();
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
int transitionType = geofencingEvent.getGeofenceTransition();
Location geofenceCenter = PSApplicationClass.getInstance().pref.getGeoCenter(context);
if (geofencingEvent.getTriggeringLocation() != null) {
if (geofenceCenter != null) {
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver TRIGGERING LOCATION: " + geofencingEvent.getTriggeringLocation().toString() + " / GEOFENCE CENTER: " + geofenceCenter.getLatitude() + ", " + geofenceCenter.getLongitude(), "D", Constants.TRACKER);
} else
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver TRIGGERING LOCATION: " + geofencingEvent.getTriggeringLocation().toString(), "D", Constants.TRACKER);
} else
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver ERROR => TRIGGERING LOCATION NULL", "D", Constants.TRACKER);
if (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
List<Geofence> triggerList = geofencingEvent.getTriggeringGeofences();
for (Geofence geofence : triggerList) {
Log.i("", "geof is s receive transition broadcast receiver " + transitionType + " GPS zone " + geofence.getRequestId());
if (geofence.getRequestId().contentEquals("3")) {
Log.i("", "geof autopilot2 ENTERED GEOFENCE will start pilot with first location");
Utils.appendLog("GEOFENCE ENTERED ReceiveTransitionsBroadcastReceiver check to see if should start pilot", "T", Constants.TRACKER);
PSLocationService.getInstance(context).isLocationRequestsOn = -1;
PSLocationService.getInstance(context).RequestLocationUpdates();
if (PSTrip.getActiveTrip() != null) {
removeAutoPilotGeofence();
} else
PSMotionService.getInstance(context).checkinTime = System.currentTimeMillis() / 1000;
}
}
}
}
public void removeAutoPilotGeofence() {
try {
Log.i("", "autopilot remove autopilot geofence");
List<String> list = new ArrayList<String>();
list.add("3");
if(mGeofencingClient == null)
mGeofencingClient = LocationServices.getGeofencingClient(context);
mGeofencingClient.removeGeofences(list).addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Utils.appendLog("GEOFENCE removeAutoPilotGeofence Success removing geofences!", "I", Constants.TRACKER);
Log.i("", "GEOFENCE removeAutoPilotGeofence Success removing geofences!");
PSApplicationClass.getInstance().pref.setGeoCenterString(context, "-1");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Utils.appendLog("GEOFENCE removeAutoPilotGeofence FAILURE removing geofences!" + e.getMessage(), "I", Constants.TRACKER);
Log.i("", "GEOFENCE removeAutoPilotGeofence FAILURE removing geofences!" + e.getMessage());
}
});
Utils.appendLog("GEOFENCE: Disabling geofence done removeAutoPilotGeofence", "E", Constants.TRACKER);
} catch (final Exception e) {
if (e.getMessage().contains("GoogleApiClient") && e.getMessage().contains("not connected")) {
PSLocationService.getInstance(context).startLocationClient();
Handler han = new Handler();
han.postDelayed(new Runnable() {
@Override
public void run() {
Utils.appendLog("autopilot2 error will try again", "E", Constants.TRACKER);
removeAutoPilotGeofence();
}
}, 1000);
}
Log.i("", "autopilot2 error replaceFragment autopilot geofence:" + e.getMessage());
Utils.appendLog("autopilot2 error replaceFragment autopilot geofence:" + e.getMessage(), "E", Constants.TRACKER);
}
}
public void setGeofenceRequest(final Location location) {
ArrayList geofences = new ArrayList<>();
geofences.add(new Geofence.Builder()
.setRequestId("3")
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_EXIT)
.setCircularRegion(
location.getLatitude(), location.getLongitude(), PSLocationService.kPSGeofencingDistanceMedium)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.build());
//ADDING GEOFENCES
if (geofences.size() > 0) {
if(mGeofencingClient == null)
mGeofencingClient = LocationServices.getGeofencingClient(context);
mGeofencingClient.addGeofences(getGeofencingRequest(location, geofences), getGeofencePendingIntent()).addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
RealmLocation realmLocation = new RealmLocation(location.getLatitude(), location.getLongitude(), location.getTime() / 1000, null, true);
realmLocation.setAccuracy(location.getAccuracy());
realmLocation.setSpeed(location.getSpeed());
PSApplicationClass.getInstance().pref.setGeoCenter(realmLocation, context);
Utils.appendLog("GEOFENCE setGeofenceRequest Success adding geofences!" + location.getLatitude() + " / " + location.getLongitude(), "I", Constants.TRACKER);
Log.i("", "GEOFENCE setGeofenceRequest Success adding geofences! " + location.getLatitude() + " / " + location.getLongitude());
PSLocationService.getInstance(context).stopLocationClient();
PSMotionService.getInstance(context).buildGoogleApiClient();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Utils.appendLog("GEOFENCE setGeofenceRequest FAILURE adding geofences!" + e.getMessage(), "I", Constants.TRACKER);
Log.i("", "GEOFENCE setGeofenceRequest FAILURE adding geofences!" + e.getMessage());
}
});
Log.i("", "geof autopilot2 will set geofence for autopilot-3");
}
}
/**
* Gets a PendingIntent to send with the request to add or remove Geofences. Location Services
* issues the Intent inside this PendingIntent whenever a geofence transition occurs for the
* current list of geofences.
*
* @return A PendingIntent for the IntentService that handles geofence transitions.
*/
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(context, PSGeofenceTransitionsIntentService.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
// addGeofences() and removeGeofences().
return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
/**
* Builds and returns a GeofencingRequest. Specifies the list of geofences to be monitored.
* Also specifies how the geofence notifications are initially triggered.
*/
private GeofencingRequest getGeofencingRequest(Location location, ArrayList<Geofence> geofences) {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
// The INITIAL_TRIGGER_ENTER flag indicates that geofencing service should trigger a
// GEOFENCE_TRANSITION_ENTER notification when the geofence is added and if the device
// is already inside that geofence.
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_EXIT);
// Add the geofences to be monitored by geofencing service.
builder.addGeofences(geofences);
// Return a GeofencingRequest.
return builder.build();
}
}
我在其中也有刪除和添加地理圍欄的代碼,並且監聽器總是進入onSuccess添加它們。
對於初學者,我不會將此代碼放在BroadcastReceiver中。
除了不好的做法之外,組件可能會在代碼執行完之前關閉。
如果您需要運行可能需要一些時間的代碼,請考慮從Receiver啟動服務 。 否則,執行時間較短,您可以使用IntentService 。
通過查看您的代碼,我知道您的Geofences未按預期工作的兩個原因:
1)Geofences的性質
Geofences API主要通過WiFi /蜂窩數據檢索您的位置,這通常是不可用的。
我嘗試過使用Geofences一次,發現它們非常不准確。 我切換到LocationManager使其使用純GPS位置,它符合我的期望。
請看這個答案 ,建議
在一段時間內輪詢GPS硬件而不對結果做任何事情,您將開始獲得更准確的地理圍欄。
我從來沒有嘗試過谷歌的FusedLocation API ,但我聽過有人說它對他們來說效果很好。
如果您使用LocationManager,則必須自己實施“地理圍欄邏輯”; 您可以使用Location.distanceTo(位置)輕松完成。
例:
final float distanceFromCenter = currentLocation.distanceTo(this.destination);
if (distanceFromCenter <= YOUR_RADIUS_IN_METERS) {
// you are inside your geofence
}
2)CPU未激活
Geofences處於活動狀態這一事實並不一定意味着您的手機處於喚醒狀態並且計算位置檢查。
要解決此問題,您可以從BroacastReceiver啟動ForegroundService。 該服務也應該持有部分WakeLock 。 這保證了:
請注意,Android可能仍會在必要時終止您的服務。
您可以在網上找到大量關於如何從BroadcastReceiver啟動ForegroundService,如何保存WakeLock等的示例......
另外,請查看新的Android O API ,它會對ForegroundService和其他組件進行一些小的更改。
PS:我開發和應用程序使用上面提到的所有組件(FusedLocation除外),我非常滿意。
編輯:回答OP的問題
哦,我們試着在這里做一些訂單,否則未來的讀者可能會很容易混淆。 我將首先回答原始問題和“賞金旗幟”中的內容,然后是OP編輯,最后是OP在評論中提出的問題。
1)原始問題
當垃圾收集器清理應用程序時,觸發事件是否也被解除?
很可能是的。 請參閱此答案 ,其中OP實施了一個在單獨進程中運行的服務,以便即使應用程序被終止也可以觸發地理圍欄。
如果時間已經過去,我需要了解導致地理圍欄不被調用的原因
很多原因。 看我原來的答案。
我看到地理圍欄邏輯的實現與服務而不是廣播接收器,這將更好地工作?
接收器和服務是兩回事。 請閱讀Android的文檔。 您可以從BroadcastReceiver啟動服務,這通常是“接收”PendingIntents並使用它們執行某些操作的首選方式。
2)編輯
所有請求都在一個工作線程上處理 - 它們可能需要多長時間(並且不會阻止應用程序的主循環),但一次只能處理一個請求。
3)評論
我需要這個全天候工作因此我不能一直使用該位置,導致明顯的電池問題。
請閱讀Android Oreo后台執行限制 。 這可能是一個問題。
此外,我現在改為intentService,這是否足以確保它應該保持清醒?
不,正如我所說,你可能需要一個部分WakeLock來打開CPU。
我是否需要以另一種方式啟動它,以便將其保留在前台?
是。 要啟動Foreground Service,需要調用startForeground(int,Notification)
請注意:IntentServices的生命周期僅限於onHandleIntent()函數的結尾。 通常,它們不應該活超過幾秒鍾。 如果要啟動Foreground,請使用Service類。
此外,正如原始答案所述,Android Oreo提供了一個新的Foreground API。
不是問題,只是一個通知:我需要在這里使用Geofencing。 (如有必要,Geofencing將啟動gps
好的完美。 看看什么最適合你。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.