地理围栏事件并不总是被称为

ros*_*lin 2 android location broadcastreceiver geofencing android-geofence

这是我添加地理围栏的方式:

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的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;
            }
        }
    }
}
}
Run Code Online (Sandbox Code Playgroud)

现在通常它起作用,但并非总是如此.我会说只有大约75%的时间它应该工作,地理围栏事件实际上被称为.我觉得自从我设置地理围栏以来的时间越多,被调用的可能性就越小.为什么会这样?当垃圾收集器清理应用程序时,触发事件是否也被解除?我怎么能做到这一点,以便我的地理围栏总是被调用,当案件?

编辑:

这是我的defaultConfig:

 defaultConfig {
    minSdkVersion 15
    targetSdkVersion 23

    ndk {
        moduleName "ndkVidyoSample"
    }
}
Run Code Online (Sandbox Code Playgroud)

我从广播接收器更改为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();
}
Run Code Online (Sandbox Code Playgroud)

}

我在其中也有删除和添加地理围栏的代码,并且监听器总是进入onSuccess添加它们.

Mar*_*cak 7

对于初学者,我不会将此代码放在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
} 
Run Code Online (Sandbox Code Playgroud)

2)CPU未激活

Geofences处于活动状态这一事实并不一定意味着您的手机处于唤醒状态并且计算位置检查.

要解决此问题,您可以从BroacastReceiver启动ForegroundService.该服务也应该持有部分WakeLock.这保证了:

  1. 操作系统不会杀死服务(或更好:杀死的机会更少......)
  2. 用户知道该服务,并且可以在必要时将其解雇
  3. CPU正在运行.因此,您可以确保检索该位置的代码正在运行(请记住在服务停止时释放WakeLock).

请注意,Android可能仍会在必要时终止您的服务.

您可以在网上找到大量关于如何从BroadcastReceiver启动ForegroundService,如何保存WakeLock等的示例......

另外,请查看新的Android O API,它会对ForegroundService和其他组件进行一些小的更改.

PS:我开发和应用程序使用上面提到的所有组件(FusedLocation除外),我非常满意.

编辑:回答OP的问题

哦,我们试着在这里做一些订单,否则未来的读者可能会很容易混淆.我将首先回答原始问题和"赏金旗帜"中的内容,然后是OP编辑,最后是OP在评论中提出的问题.

1)原始问题

当垃圾收集器清理应用程序时,触发事件是否也被解除?

很可能是的.请参阅此答案,其中OP实施了一个在单独进程中运行的服务,以便即使应用程序被终止也可以触发地理围栏.

如果时间已经过去,我需要了解导致地理围栏不被调用的原因

很多原因.看我原来的答案.

我看到地理围栏逻辑的实现与服务而不是广播接收器,这将更好地工作?

接收器和服务是两回事.请阅读Android的文档.您可以从BroadcastReceiver启动服务,这通常是"接收"PendingIntents并使用它们执行某些操作的首选方式.

2)编辑

  • 请注意,我没有告诉您用服务替换BroadcastReceiver,但是从接收器启动服务并在那里处理所有逻辑可能是个好主意.
  • 使IntentService成为Singleton类是不必要的(来自IntentService文档)

所有请求都在一个工作线程上处理 - 它们可能需要多长时间(并且不会阻止应用程序的主循环),但一次只能处理一个请求.

  • 不要将Context存储到Singleton类或某些静态引用中.我印象深刻的Android Studio没有警告你.

3)评论

我需要这个全天候工作因此我无法一直使用该位置,导致明显的电池问题.

请阅读Android Oreo后台执行限制.这可能是一个问题.

此外,我现在改为intentService,这是否足以确保它应该保持清醒?

不,正如我所说,你可能需要一个部分WakeLock来打开CPU.

我是否需要以另一种方式启动它,以便将其保留在前台?

是.要启动Foreground Service,需要调用startForeground(int,Notification)

请注意:IntentServices的生命周期仅限于onHandleIntent()函数的结尾.通常,它们不应该活超过几秒钟.如果要启动Foreground,请使用Service类.

此外,正如原始答案中所述,Android Oreo提供了一个新的Foreground API.

不是问题,只是一个通知:我需要在这里使用Geofencing.(如有必要,Geofencing将启动gps

好的完美.看看什么最适合你.

  • 经过2天的测试,似乎工作正常,所以我给了你赏金. (2认同)