如何在Android上使用Geofences

位置感知资源使您的应用程序可以与现实世界进行交互,它们是增加用户参与度的理想选择。 尽管许多移动应用程序都使用它们,但本教程的主题是地理围栏 ,它经常被忽略。

地理围栏是在真实地理区域上设置的虚拟边界。 将用户位置与地理围栏范围相结合,可以知道用户是在地理围栏之内还是之外,或者即使他正在离开或进入该区域。

地理围栏知道位置在其划定区域之内还是之外

想象一下一个大学应用程序,它可以告诉您当前校园里有哪些同事和教授。 或用于奖励老顾客的购物中心应用。 您还可以探索许多其他有趣的可能性。

在本教程中,您将通过创建一个在用户进入或退出地理围栏时向用户显示通知的应用程序,学习如何在Android上使用地理围栏。 如果您以前对Google Play服务Google Maps Android APIIntentService有所了解,那么它会有所帮助。 如果没有,那么您仍然可以继续学习,但是在阅读了本教程之后,您可能需要对这些主题进行一些研究。

1. Android上的地理围栏

在Android上,有几种使用地理围栏的方法。 您甚至可以创建自己的实现来与地理栅栏配合使用,但是使用Google的GeofencingApi更容易。

该API是Google 位置 API的一部分。 它包括GeofenceGeofencingRequestGeofenceApiGeofencingEventGeofenceStatusCodes 。 在本教程中,我们使用这些类来创建和使用地理栅栏。

地理围栏界面

Geofence是代表应该监视的地理区域的接口。 它是通过使用Geofence.Builder创建的。 在创建期间,您可以设置监视区域,地理围栏的到期日期,响应性,标识符以及它应寻找的过渡类型。

为了将功耗降至最低,建议在大多数情况下使用半径至少为100米的地理围栏。 如果地理围栏位于乡村,则应将半径增加到500米或更高,以确保地理围栏有效。

Geofence geofence = new Geofence.Builder()
    .setRequestId(GEOFENCE_REQ_ID) // Geofence ID
    .setCircularRegion( LATITUDE, LONGITUDE, RADIUS) // defining fence region
    .setExpirationDuration( DURANTION ) // expiring date
    // Transition types that it should look for
    .setTransitionTypes( Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT )
    .build();

地理围栏过渡

  • GEOFENCE_TRANSITION_DWELL指示用户进入该区域并花了一些时间。 当用户进入和离开该区域的速度过快时,避免出现多个警报很有用。 您可以使用setLoiteringDelay参数配置setLoiteringDelay时间。
  • GEOFENCE_TRANSITION_ENTER指示用户何时进入监视区域。
  • GEOFENCE_TRANSITION_EXIT指示用户何时退出区域。

GeofenceRequest

GeofencingRequest类接收应监视的地理围栏。 您可以通过使用Builder ,传递GeofenceList<Geofence>以及创建地理围栏时触发的通知类型来创建实例。

GeofencingRequest request = new GeofencingRequest.Builder()
    // Notification to trigger when the Geofence is created
    .setInitialTrigger( GeofencingRequest.INITIAL_TRIGGER_ENTER )
    .addGeofence( geofence ) // add a Geofence
    .build();

GeofencingApi

GeofencingApi类是与Google的geofencing API进行所有交互的入口点。 它是Location API的一部分,并且依赖于GoogleApiClient来工作。 您将使用GeofencingApi添加和删​​除地理围栏。

要添加地理围栏,请调用addGeofence()方法。 它使用传递给GeofencingRequest的设置监视给定区域,并在进行地理围栏转换(进入或退出该区域)时拍摄PendingIntent

PendingResult<Status> addGeofences (GoogleApiClient client, 
    GeofencingRequest geofencingRequest, 
    PendingIntent pendingIntent)

要删除地理围栏,请调用removeGeofences() 。 您可以使用其请求标识符或未决意图删除地理围栏。

PendingResult<Status> removeGeofences(GoogleApiClient client, 
    List<String> geofenceRequestIds)
PendingResult<Status> removeGeofences (GoogleApiClient client, 
    PendingIntent pendingIntent)

2.创建地理围栏应用

在本教程中,我们创建一个简单的应用程序,该应用程序监视用户位置并在用户进入或退出地理围栏区域时发布通知。 该应用程序仅包含一个Activity和一个IntentService 。 我们还快速浏览了GoogleMapGoogleApiClientFusedLocationProviderApi ,并探讨了地理围栏API的一些注意事项。

应用截图

步骤1:专案设定

GeofencingApiGoogle Play服务的一部分。 要访问它,您需要正确设置您的开发环境并创建一个GoogleApiClient实例。 使用空白的Activity创建一个新项目,如下所示编辑项目的build.gradle文件,并同步您的项目。

步骤2:权限

我们需要设置正确的权限才能创建和使用地理围栏。 将以下权限添加到项目清单中:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

从Android 6.0开始,该应用会在运行时而非安装过程中请求权限 。 我们将在本教程的后面部分解决这个问题。

步骤3:建立版面

该项目包含一个布局,即MainActity布局。 它包含设备的当前纬度和经度,以及显示地理围栏和用户位置的GoogleMap片段。

由于activity_main.xml非常简单,我只想专注于MapFragment元素。 您可以在本教程的源文件中查看完整的布局。

<!--GoogleMap fragment-->
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:name="com.google.android.gms.maps.MapFragment"
    android:id="@+id/map"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

第4步:Google Maps API密钥

由于我们使用的是MapFragment ,因此我们需要设置和初始化GoogleMap实例。 首先,您需要获取一个API密钥 。 获得API密钥后,将其添加到项目清单中。

<meta-data
   android:name="com.google.android.geo.API_KEY"
   android:value="YOUR_API_KEY"/>

让我们从GoogleMap实例开始。 在Activity类中实现GoogleMap.OnMapReadyCallbackGoogleMap.OnMapClickListenerGoogleMap.OnMarkerClickListener并初始化地图。

public class MainActivity extends AppCompatActivity
    implements 
			OnMapReadyCallback, 
			GoogleMap.OnMapClickListener,
            GoogleMap.OnMarkerClickListener
    {
    
    private static final String TAG = MainActivity.class.getSimpleName();
        
    private TextView textLat, textLong;
    private MapFragment mapFragment;
    private GoogleMap map;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textLat = (TextView) findViewById(R.id.lat);
        textLong = (TextView) findViewById(R.id.lon);
        
        // initialize GoogleMaps
        initGMaps();
    }
    
    // Initialize GoogleMaps
    private void initGMaps(){
        mapFragment = (MapFragment) getFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    } 
    
    // Callback called when Map is ready
    @Override
    public void onMapReady(GoogleMap googleMap) {
        Log.d(TAG, "onMapReady()");
        map = googleMap;
        map.setOnMapClickListener(this);
        map.setOnMarkerClickListener(this);
    }
    
	// Callback called when Map is touched
    @Override
    public void onMapClick(LatLng latLng) {
        Log.d(TAG, "onMapClick("+latLng +")");
    }

	// Callback called when Marker is touched
    @Override
    public boolean onMarkerClick(Marker marker) {
        Log.d(TAG, "onMarkerClickListener: " + marker.getPosition() );
        return false;
    } 
}

步骤5: GoogleApiClient

要使用GeofencingApi界面,我们需要一个GoogleApiClient入口点。 让我们在Activity实现一个GoogleApiClient.ConnectionCallbacks和一个GoogleApiClient.OnConnectionFailedListener ,如下所示。

public class MainActivity extends AppCompatActivity
        implements
            GoogleApiClient.ConnectionCallbacks,
            GoogleApiClient.OnConnectionFailedListener,
            OnMapReadyCallback,
            GoogleMap.OnMapClickListener,
            GoogleMap.OnMarkerClickListener {
            
    // ...
    private GoogleApiClient googleApiClient;
            
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        
        // ...

        // create GoogleApiClient
        createGoogleApi();
    }
    
    // Create GoogleApiClient instance
    private void createGoogleApi() {
        Log.d(TAG, "createGoogleApi()");
        if ( googleApiClient == null ) {
            googleApiClient = new GoogleApiClient.Builder( this )
                    .addConnectionCallbacks( this )
                    .addOnConnectionFailedListener( this )
                    .addApi( LocationServices.API )
                    .build();
        }
    }
    
    @Override
    protected void onStart() {
        super.onStart();

        // Call GoogleApiClient connection when starting the Activity
        googleApiClient.connect();
    }

    @Override
    protected void onStop() {
        super.onStop();

        // Disconnect GoogleApiClient when stopping Activity
        googleApiClient.disconnect();
    }
    
    // GoogleApiClient.ConnectionCallbacks connected
    @Override
    public void onConnected(@Nullable Bundle bundle) {
        Log.i(TAG, "onConnected()");
    }

    // GoogleApiClient.ConnectionCallbacks suspended
    @Override
    public void onConnectionSuspended(int i) {
        Log.w(TAG, "onConnectionSuspended()");
    }

    // GoogleApiClient.OnConnectionFailedListener fail
    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Log.w(TAG, "onConnectionFailed()");
    }
}

步骤6: FusedLocationProviderApi

我们还需要访问用户的当前位置。 FusedLocationProviderApi接口为我们提供了此信息,并允许对位置请求进行高度控制。 考虑到位置请求对设备的电池消耗有直接影响,这非常重要。

现在,让我们实现一个LocationListener 。 通过创建Location请求,检查用户是否为应用程序授予了适当的权限,并在屏幕上显示其当前位置。

public class MainActivity extends AppCompatActivity
        implements
            // ....
            LocationListener
            {
            
    private Location lastLocation;
    //...
    
    // GoogleApiClient.ConnectionCallbacks connected
    @Override
    public void onConnected(@Nullable Bundle bundle) {
        Log.i(TAG, "onConnected()");
        getLastKnownLocation();
    }
    
    // Get last known location
    private void getLastKnownLocation() {
        Log.d(TAG, "getLastKnownLocation()");
        if ( checkPermission() ) {
            lastLocation = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
            if ( lastLocation != null ) {
                Log.i(TAG, "LasKnown location. " +
                        "Long: " + lastLocation.getLongitude() +
                        " | Lat: " + lastLocation.getLatitude());
                writeLastLocation();
                startLocationUpdates();
            } else {
                Log.w(TAG, "No location retrieved yet");
                startLocationUpdates();
            }
        }
        else askPermission();
    }
    
    private LocationRequest locationRequest;
    // Defined in mili seconds. 
    // This number in extremely low, and should be used only for debug
    private final int UPDATE_INTERVAL =  1000;
    private final int FASTEST_INTERVAL = 900;

    // Start location Updates
    private void startLocationUpdates(){
        Log.i(TAG, "startLocationUpdates()");
        locationRequest = LocationRequest.create()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(UPDATE_INTERVAL)
                .setFastestInterval(FASTEST_INTERVAL);

        if ( checkPermission() )
            LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
    }
    
    @Override
    public void onLocationChanged(Location location) {
        Log.d(TAG, "onLocationChanged ["+location+"]");
        lastLocation = location;
        writeActualLocation(location);
    }
    
    // Write location coordinates on UI
    private void writeActualLocation(Location location) {
        textLat.setText( "Lat: " + location.getLatitude() );
        textLong.setText( "Long: " + location.getLongitude() );
    }

    private void writeLastLocation() {
        writeActualLocation(lastLocation);
    }
    
    // Check for permission to access Location
    private boolean checkPermission() {
        Log.d(TAG, "checkPermission()");
        // Ask for permission if it wasn't granted yet
        return (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED );
    }
    
    // Asks for permission
    private void askPermission() {
        Log.d(TAG, "askPermission()");
        ActivityCompat.requestPermissions(
                this,
                new String[] { Manifest.permission.ACCESS_FINE_LOCATION },
                REQ_PERMISSION
        );
    }

    // Verify user's response of the permission requested
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        Log.d(TAG, "onRequestPermissionsResult()");
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch ( requestCode ) {
            case REQ_PERMISSION: {
                if ( grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED ){
                    // Permission granted
                    getLastKnownLocation();

                } else {
                    // Permission denied
                    permissionsDenied();
                }
                break;
            }
        }
    }

    // App cannot work without the permissions
    private void permissionsDenied() {
        Log.w(TAG, "permissionsDenied()");
    }
}

必须解决的是,上面创建的LocationRequest没有针对生产环境进行优化。 UPDATE_INTERVAL太短,会消耗过多的电池电量。 更实际的生产配置可能是:

private final int UPDATE_INTERVAL =  3 * 60 * 1000; // 3 minutes
private final int FASTEST_INTERVAL = 30 * 1000;  // 30 secs
private void startLocationUpdates(){
        Log.i(TAG, "startLocationUpdates()");
        locationRequest = LocationRequest.create()
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setInterval(UPDATE_INTERVAL)
                .setFastestInterval(FASTEST_INTERVAL);

        if ( checkPermission() )
            LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
}

步骤7:GoogleMap标记

我们的Activity需要两个不同的标记locationMarker使用FusedLocationProviderApi给出的纬度和经度来通知设备的当前位置。 geoFenceMarker是地理围栏创建的目标,因为它使用在地图上给出的最后触摸来检索其位置。

@Override
public void onMapClick(LatLng latLng) {
    Log.d(TAG, "onMapClick("+latLng +")");
    markerForGeofence(latLng);
}

private void writeActualLocation(Location location) {
    // ...
    markerLocation(new LatLng(location.getLatitude(), location.getLongitude()));
}

private Marker locationMarker;
// Create a Location Marker 
private void markerLocation(LatLng latLng) {
    Log.i(TAG, "markerLocation("+latLng+")");
    String title = latLng.latitude + ", " + latLng.longitude;
    MarkerOptions markerOptions = new MarkerOptions()
            .position(latLng)
            .title(title);
    if ( map!=null ) {
        // Remove the anterior marker
        if ( locationMarker != null )
            locationMarker.remove();
        locationMarker = map.addMarker(markerOptions);
        float zoom = 14f;
        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, zoom);
        map.animateCamera(cameraUpdate);
    }
}

private Marker geoFenceMarker;
// Create a marker for the geofence creation
private void markerForGeofence(LatLng latLng) {
    Log.i(TAG, "markerForGeofence("+latLng+")");
    String title = latLng.latitude + ", " + latLng.longitude;
    // Define marker options
    MarkerOptions markerOptions = new MarkerOptions()
            .position(latLng)
            .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE))
            .title(title);
    if ( map!=null ) {
        // Remove last geoFenceMarker
        if (geoFenceMarker != null)
            geoFenceMarker.remove();

        geoFenceMarker = map.addMarker(markerOptions);
    }
}

第8步:创建地理围栏

最后,是时候创建地理围栏了。 我们使用geoFenceMarker作为地理围栏的中心。

private static final long GEO_DURATION = 60 * 60 * 1000;
private static final String GEOFENCE_REQ_ID = "My Geofence";
private static final float GEOFENCE_RADIUS = 500.0f; // in meters

// Create a Geofence
private Geofence createGeofence( LatLng latLng, float radius ) {
    Log.d(TAG, "createGeofence");
    return new Geofence.Builder()
            .setRequestId(GEOFENCE_REQ_ID)
            .setCircularRegion( latLng.latitude, latLng.longitude, radius)
            .setExpirationDuration( GEO_DURATION )
            .setTransitionTypes( Geofence.GEOFENCE_TRANSITION_ENTER
                    | Geofence.GEOFENCE_TRANSITION_EXIT )
            .build();
}

接下来,我们创建GeofencingRequest对象。

// Create a Geofence Request
private GeofencingRequest createGeofenceRequest( Geofence geofence ) {
    Log.d(TAG, "createGeofenceRequest");
    return new GeofencingRequest.Builder()
            .setInitialTrigger( GeofencingRequest.INITIAL_TRIGGER_ENTER )
            .addGeofence( geofence )
            .build();
}

我们使用PendingIntent对象调用将处理GeofenceEventIntentService 。 我们稍后会创建GeofenceTrasitionService.class

private PendingIntent geoFencePendingIntent;
private final int GEOFENCE_REQ_CODE = 0;
private PendingIntent createGeofencePendingIntent() {
    Log.d(TAG, "createGeofencePendingIntent");
    if ( geoFencePendingIntent != null )
        return geoFencePendingIntent;

    Intent intent = new Intent( this, GeofenceTrasitionService.class);
    return PendingIntent.getService(
            this, GEOFENCE_REQ_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT );
}

// Add the created GeofenceRequest to the device's monitoring list
private void addGeofence(GeofencingRequest request) {
    Log.d(TAG, "addGeofence");
    if (checkPermission())
        LocationServices.GeofencingApi.addGeofences(
                googleApiClient,
                request,
                createGeofencePendingIntent()
        ).setResultCallback(this);
}

我们还将在地图上绘制地理围栏作为可视参考。

@Override
public void onResult(@NonNull Status status) {
    Log.i(TAG, "onResult: " + status);
    if ( status.isSuccess() ) {
        drawGeofence();
    } else {
        // inform about fail
    }
}

// Draw Geofence circle on GoogleMap
private Circle geoFenceLimits;
private void drawGeofence() {
    Log.d(TAG, "drawGeofence()");

    if ( geoFenceLimits != null )
        geoFenceLimits.remove();

    CircleOptions circleOptions = new CircleOptions()
            .center( geoFenceMarker.getPosition())
            .strokeColor(Color.argb(50, 70,70,70))
            .fillColor( Color.argb(100, 150,150,150) )
            .radius( GEOFENCE_RADIUS );
    geoFenceLimits = map.addCircle( circleOptions );
}

startGeofence()方法负责启动MainActivity类中的地理围栏过程。

@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch ( item.getItemId() ) {
            case R.id.geofence: {
                startGeofence();
                return true;
            }
        }
        return super.onOptionsItemSelected(item);
    }
    
	// Start Geofence creation process
    private void startGeofence() {
        Log.i(TAG, "startGeofence()");
        if( geoFenceMarker != null ) {
            Geofence geofence = createGeofence( geoFenceMarker.getPosition(), GEOFENCE_RADIUS );
            GeofencingRequest geofenceRequest = createGeofenceRequest( geofence );
            addGeofence( geofenceRequest );
        } else {
            Log.e(TAG, "Geofence marker is null");
        }
    }

步骤9:地理围栏转换服务

现在,我们终于可以创建前面提到的GeofenceTrasitionService.class 。 此类扩展了IntentService ,并负责处理GeofencingEvent 。 首先,我们从收到的意图中获取此事件。

GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);

然后,我们检查发生的地理围栏过渡是否对我们感兴趣。 如果是这样,我们将检索已触发地理围栏的列表,并创建具有适当操作的通知。

// Retrieve GeofenceTrasition
int geoFenceTransition = geofencingEvent.getGeofenceTransition();
// Check if the transition type
if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
        geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT ) {
    // Get the geofence that were triggered
    List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
    // Create a detail message with Geofences received
    String geofenceTransitionDetails = getGeofenceTrasitionDetails(geoFenceTransition, triggeringGeofences );
    // Send notification details as a String
    sendNotification( geofenceTransitionDetails );
}

我还实现了一些辅助方法,以使类的实现更容易理解。

public class GeofenceTrasitionService extends IntentService {

    private static final String TAG = GeofenceTrasitionService.class.getSimpleName();
    public static final int GEOFENCE_NOTIFICATION_ID = 0;

    public GeofenceTrasitionService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        // Retrieve the Geofencing intent
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);

        // Handling errors
        if ( geofencingEvent.hasError() ) {
            String errorMsg = getErrorString(geofencingEvent.getErrorCode() );
            Log.e( TAG, errorMsg );
            return;
        }

		// Retrieve GeofenceTrasition
        int geoFenceTransition = geofencingEvent.getGeofenceTransition();
        // Check if the transition type
        if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT ) {
            // Get the geofence that were triggered
            List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
			// Create a detail message with Geofences received
            String geofenceTransitionDetails = getGeofenceTrasitionDetails(geoFenceTransition, triggeringGeofences );
            // Send notification details as a String
            sendNotification( geofenceTransitionDetails );
        }
    }
	
	// Create a detail message with Geofences received
    private String getGeofenceTrasitionDetails(int geoFenceTransition, List<Geofence> triggeringGeofences) {
        // get the ID of each geofence triggered
        ArrayList<String> triggeringGeofencesList = new ArrayList<>();
        for ( Geofence geofence : triggeringGeofences ) {
            triggeringGeofencesList.add( geofence.getRequestId() );
        }

        String status = null;
        if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER )
            status = "Entering ";
        else if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT )
            status = "Exiting ";
        return status + TextUtils.join( ", ", triggeringGeofencesList);
    }

	// Send a notification
    private void sendNotification( String msg ) {
        Log.i(TAG, "sendNotification: " + msg );

        // Intent to start the main Activity
        Intent notificationIntent = MainActivity.makeNotificationIntent(
                getApplicationContext(), msg
        );

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(MainActivity.class);
        stackBuilder.addNextIntent(notificationIntent);
        PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

        // Creating and sending Notification
        NotificationManager notificatioMng =
                (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
        notificatioMng.notify(
                GEOFENCE_NOTIFICATION_ID,
                createNotification(msg, notificationPendingIntent));
    }

    // Create a notification
    private Notification createNotification(String msg, PendingIntent notificationPendingIntent) {
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
        notificationBuilder
                .setSmallIcon(R.drawable.ic_action_location)
                .setColor(Color.RED)
                .setContentTitle(msg)
                .setContentText("Geofence Notification!")
                .setContentIntent(notificationPendingIntent)
                .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND)
                .setAutoCancel(true);
        return notificationBuilder.build();
    }

	// Handle errors
    private static String getErrorString(int errorCode) {
        switch (errorCode) {
            case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE:
                return "GeoFence not available";
            case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES:
                return "Too many GeoFences";
            case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS:
                return "Too many pending intents";
            default:
                return "Unknown error.";
        }
    }
}

3.测试

在虚拟设备上测试

在虚拟设备上测试地理围栏要简单得多。 有几种方法可以做到这一点。 在Android Studio中,打开一个虚拟设备,然后单击右下角的更多选项按钮。

点击更多选项

在左侧的“ 位置”标签中,输入位置的坐标。

发送经纬度

我更喜欢使用telnet命令来控制虚拟设备。 要使用此功能,您需要使用以下命令从命令行连接到设备:

telnet localhost [DEVICE_PORT]

设备端口显示在虚拟设备窗口中。 设备端口通常等于5554

您可能需要使用auth_token授权此连接,但命令行会向您显示该连接的位置。 导航到该位置并复制令牌并输入auth [YOUR_AUTH_TOKEN]

现在,您可以通过运行以下命令来设置设备的位置:

geo fix [LATITUDE] [LONGITUDE]

结论

地理围栏可能是您应用程序的重要补充,因为它可以大大提高用户参与度。 有许多探索的可能性,您甚至可以使用室内信标(例如Estimote)来创建精致的体验。 使用室内信标,您可以确切地知道用户经过的地点,例如,购物中心。

将地理围栏添加到项目很简单,但是我们始终需要牢记功耗。 这意味着我们需要仔细选择地理围栏的大小和更新速率,因为两者都会直接影响您应用程序的功耗。

因此,进行测试对于了解应用程序的功耗非常重要。 如果用户不需要或不需要此功能,也可以考虑为其提供完全禁用地理围栏的选项。

翻译自: https://code.tutsplus.com/tutorials/how-to-work-with-geofences-on-android--cms-26639

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值