电子围栏未触发(待定意图和广播接收器)

6

我曾经使用了与Geofences相关的Android教程,然而当应用关闭时,教程显然是无法发挥作用的。因此,在搜索了一番之后,我注意到SO上的用户b-ryce使用了BroadcastReceiver,以便在应用程序不活动时触发地理围栏(他的SO问题链接)。

然而,我就是不能强制地理围栏在我移动进入/离开注册位置时触发。以下是我的步骤:

    /**
     * GeoLocation library
     */
    mGeoLocation = new GeoLocation(sInstance);


    /**
     * mReceiver init
     */
    mReceiver = new Receiver();
    sInstance.registerReceiver(mReceiver, mReceiver.createIntentFilter());

在 GeoLocation 类内部:

/*
 * Create a PendingIntent that triggers an IntentService in your
 * app when a geofence transition occurs.
 */
protected PendingIntent getTransitionPendingIntent() {
    if (mGeoPendingIntent != null) {
        return mGeoPendingIntent;
    }

    else {

        // Create an explicit Intent
        //  Intent intent = new Intent(mContext,
        //          ReceiveTransitionsIntentService.class);

        Intent intent = new Intent(getClass().getPackage().getName() + ".GEOFENCE_RECEIVE");

        /**
         * Return the PendingIntent
         */
        return PendingIntent.getBroadcast(
                mContext,
                0,
                intent,
                PendingIntent.FLAG_UPDATE_CURRENT);

    }
}

以下是我创建新地理围栏的步骤:

                        Geofence fence = new Geofence.Builder()
                                .setRequestId(hashCode)
                                        // when entering this geofence
                                .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)
                                .setCircularRegion(
                                        Double.parseDouble(single.getSetting("latitude")),
                                        Double.parseDouble(single.getSetting("longitude")),
                                        Float.parseFloat(single.getSetting("radius")) // radius in meters
                                )
                                .setExpirationDuration(Geofence.NEVER_EXPIRE)
                                .build();

                        mGeofences.add(fence);

数组被填充后,我们在Geofence类中调用AddGeofences方法。

mGeoLocation.AddGeofences(mGeofences);

接收器类的AndroidManifest.xml:

    <!-- RECEIVER -->
    <receiver android:name=".Receiver" >
    </receiver>

接收器类只需在地理围栏触发时记录日志

public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();

    Log.d("sfen", "Broadcast recieved "+ action +" but not needed.");
}

问题在于,地理围栏仅在我打开应用程序内的地图并选择位置时触发。关闭应用程序(在后台运行)不会触发任何事件,地理围栏转换也不会触发任何事件。
有人可以告诉我我做错了什么吗?

@RussWilde - 是的,当我将我的应用程序移至后台并打开Google地图时,我的两个操作都被触发了。 - gregor
听到这个消息真是不幸,我在使用地理围栏方面的效果非常参差不齐,包括在其他应用程序中如 IFTTT 和 Field Trip 中,每次我都在想为什么有些地理围栏不能被可靠地触发,我猜测 Android 的基础位置服务更新频率不够高,无法捕捉到每个位置。遗憾的是,如果情况确实如此,我没有有用的解决方案,但我也很感兴趣听听其他人可以提供的答案。 - 0x00h
@gregor - 你好,你介意分享一下最终是如何让它工作的吗? - Rat-a-tat-a-tat Ratatouille
@Rat-a-tat-a-tatRatatouille 直到今天,我还没有找到合适的解决方案。联系谷歌位置服务开发人员也没有用(因为没有人回复)。 - gregor
我将创建一个新的Github项目,其中只包含一个活动、一个后台服务和一个接收器。加入静态地理位置信息,看看是否能够正常工作。此外,https://github.com/chenjishi/android_location_demo 这个项目根本无法工作。 - gregor
显示剩余3条评论
2个回答

0

在编程方面,应该使用 IntentService 来接收地理围栏事件(而非 receiver)。我在 https://github.com/chenjishi/android_location_demo 上写了一个演示。

1.首先定义 LocationClient。

    private LocationClient locationClient;

2.初始化它

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
    if (resp == ConnectionResult.SUCCESS) {
        locationClient = new LocationClient(this, this, this);
        locationClient.connect();
    }
}

3. 连接成功后,注册地理围栏

@Override
public void onConnected(Bundle bundle) {
    ArrayList<Store> storeList = getStoreList();
    if (null != storeList && storeList.size() > 0) {
        ArrayList<Geofence> geofenceList = new ArrayList<Geofence>();
        for (Store store : storeList) {
            float radius = (float) store.radius;
            Geofence geofence = new Geofence.Builder()
                    .setRequestId(store.id)
                    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)
                    .setCircularRegion(store.latitude, store.longitude, radius)
                    .setExpirationDuration(Geofence.NEVER_EXPIRE)
                    .build();

            geofenceList.add(geofence);
        }

        PendingIntent geoFencePendingIntent = PendingIntent.getService(this, 0,
                new Intent(this, GeofenceIntentService.class), PendingIntent.FLAG_UPDATE_CURRENT);
        locationClient.addGeofences(geofenceList, geoFencePendingIntent, this);
    }
}

4. 最后定义IntentService以接收地理围栏事件

public class GeofenceIntentService extends IntentService {
public static final String TRANSITION_INTENT_SERVICE = "ReceiveTransitionsIntentService";

public GeofenceIntentService() {
    super(TRANSITION_INTENT_SERVICE);
}

@Override
protected void onHandleIntent(Intent intent) {
    if (LocationClient.hasError(intent)) {
        //todo error process
    } else {
        int transitionType = LocationClient.getGeofenceTransition(intent);
        if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER ||
                transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
            List<Geofence> triggerList = LocationClient.getTriggeringGeofences(intent);

            for (Geofence geofence : triggerList) {
                generateNotification(geofence.getRequestId(), "address you defined");
            }
        }
    }
}

private void generateNotification(String locationId, String address) {
    long when = System.currentTimeMillis();
    Intent notifyIntent = new Intent(this, MainActivity.class);
    notifyIntent.putExtra("id", locationId);
    notifyIntent.putExtra("address", address);
    notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    NotificationCompat.Builder builder =
            new NotificationCompat.Builder(this)
                    .setSmallIcon(R.drawable.dac_logo)
                    .setContentTitle(locationId)
                    .setContentText(address)
                    .setContentIntent(pendingIntent)
                    .setAutoCancel(true)
                    .setDefaults(Notification.DEFAULT_SOUND)
                    .setWhen(when);

    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify((int) when, builder.build());
}

}


嗨。正如所述,我也会引用b-ryce的回复:“因此,在玩弄了一段时间后,看起来在示例代码中定义的ReceiveTransitionsIntentService将在应用程序不在附近时停止接收通知。我认为这是示例代码中的一个大问题”。因此,使用地理围栏似乎有非常不同的结果。仍然不确定为什么有些人有时可以工作,而其他人总是可以工作... - gregor
@gregor,你确定你的手机定位服务复选框已经勾选了吗???地理围栏需要启用位置服务,你应该给用户提供一个启用它的指南。 - Jishi Chen
我注意到了。然而,在其中一部手机上,自从我将其从4.4.2升级到4.4.4以后,它的工作效果更好了。我会进行更多测试并发布结果(如果有的话)。 - gregor
@JishiChen,你能看一下这个问题吗? - Skizo-ozᴉʞS ツ

0
我看到两个问题。第一个是你没有正确创建PendingIntent。看起来你是这样做的:
    // Create an explicit Intent
    //  Intent intent = new Intent(mContext,
    //          ReceiveTransitionsIntentService.class);

    Intent intent = new Intent(getClass().getPackage().getName() + ".GEOFENCE_RECEIVE");

首先,这个评论是不正确的。这不是一个“显式Intent”。这只生成一个只包含ACTION的Intent。你需要一个能启动你的BroadcastReceiver的“显式Intent”。
你需要做的是:
    Intent intent = new Intent(mContext, Receiver.class);

第二个问题是您需要允许其他应用程序启动您的BroadcastReceiver。为此,您需要在清单文件中将其声明为“exported”。请将您的清单条目更改为以下内容:
<!-- RECEIVER -->
    <receiver android:name=".Receiver"
              android:exported="true"/>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接