如何在 Android 中创建地理围栏?

Posted

技术标签:

【中文标题】如何在 Android 中创建地理围栏?【英文标题】:How to Create Geofence in an Android? 【发布时间】:2016-09-01 00:27:30 【问题描述】:

如何在当前纬度、经度上创建地理围栏(创建和监控地理围栏)。 我正在尝试多个示例但不创建。 使用此代码:

public Geofence geofence(float radius, double latitude, double longitude) 
    String id = UUID.randomUUID().toString();
    return new Geofence.Builder()
            .setRequestId(id)
            .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
            .setCircularRegion(latitude, longitude, radius)
            .setExpirationDuration(Geofence.NEVER_EXPIRE)
            .build();

【问题讨论】:

【参考方案1】:

Google 提供了这个教程,非常容易理解:

http://io2015codelabs.appspot.com/codelabs/geofences

Udacity 还有一门教授位置服务的课程,包括地理围栏:

https://www.udacity.com/course/google-location-services-on-android--ud876-1

将 Google Play 服务添加到 Gradle 文件:

dependencies 
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.0.0'
    compile 'com.google.android.gms:play-services:7.3.0'

添加到清单文件:

<meta-data
    android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

添加到 Activity 的 XML 布局文件中:

<Button
        android:id="@+id/add_geofences_button"
        android:layout_
        android:layout_
        android:layout_alignParentLeft="true"
        android:onClick="addGeofencesButtonHandler"
        android:text="Add GeoFences" />

添加到 Activity 的 Java 文件中:

public class MainActivity extends Activity
implements
GoogleApiClient.ConnectionCallbacks, 
GoogleApiClient.OnConnectionFailedListener, 
ResultCallback<Status> 

 @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mAddGeofencesButton = (Button) findViewById(R.id.add_geofences_button);
        // Empty list for storing geofences.
        mGeofenceList = new ArrayList<Geofence>();

        // Get the geofences used. Geofence data is hard coded in this sample.
        populateGeofenceList();

        // Kick off the request to build GoogleApiClient.
        buildGoogleApiClient();
    

protected synchronized void buildGoogleApiClient() 
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    

public void populateGeofenceList() 
for (Map.Entry<String, LatLng> entry : Constants.LANDMARKS.entrySet()) 
mGeofenceList.add(new Geofence.Builder()
   .setRequestId(entry.getKey())
   .setCircularRegion(
   entry.getValue().latitude,
   entry.getValue().longitude,
   Constants.GEOFENCE_RADIUS_IN_METERS
   )
   .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
   .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
   Geofence.GEOFENCE_TRANSITION_EXIT)
   .build());
   


@Override
protected void onStart() 
    super.onStart();
    if (!mGoogleApiClient.isConnecting() || !mGoogleApiClient.isConnected()) 
        mGoogleApiClient.connect();
    


@Override
protected void onStop() 
    super.onStop();
    if (mGoogleApiClient.isConnecting() || mGoogleApiClient.isConnected()) 
        mGoogleApiClient.disconnect();
    


@Override
public void onConnected(Bundle connectionHint) 



@Override
public void onConnectionFailed(ConnectionResult result) 
    // Do something with result.getErrorCode());


@Override
public void onConnectionSuspended(int cause) 
    mGoogleApiClient.connect();


    public void addGeofencesButtonHandler(View view) 
            if (!mGoogleApiClient.isConnected()) 
                Toast.makeText(this, "Google API Client not connected!", Toast.LENGTH_SHORT).show();
                return;
            

            try 
                LocationServices.GeofencingApi.addGeofences(
                        mGoogleApiClient,
                        getGeofencingRequest(),
                        getGeofencePendingIntent()
                ).setResultCallback(this); // Result processed in onResult().
             catch (SecurityException securityException) 
                // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
            
    

   private GeofencingRequest getGeofencingRequest() 
        GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
        builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
        builder.addGeofences(mGeofenceList);
        return builder.build();
    

    private PendingIntent getGeofencePendingIntent() 
            Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
            // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling addgeoFences()
            return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        

public void onResult(Status status) 
        if (status.isSuccess()) 
            Toast.makeText(
                    this,
                    "Geofences Added",
                    Toast.LENGTH_SHORT
            ).show();
         else 
            // Get the status code for the error and log it using a user-friendly message.
            String errorMessage = GeofenceErrorMessages.getErrorString(this,
                    status.getStatusCode());
        
    

创建一个名为 Constans 的 java 文件:

public class Constants 

    public static final long GEOFENCE_EXPIRATION_IN_MILLISECONDS = 12 * 60 * 60 * 1000;
    public static final float GEOFENCE_RADIUS_IN_METERS = 20;

    public static final HashMap<String, LatLng> LANDMARKS = new     HashMap<String, LatLng>();
    static 
        // San Francisco International Airport.
        LANDMARKS.put("Moscone South", new LatLng(37.783888,-122.4009012));

        // Googleplex.
        LANDMARKS.put("Japantown", new LatLng(37.785281,-122.4296384));

        // Test
        LANDMARKS.put("SFO", new LatLng(37.621313,-122.378955));
    

创建一个名为 GeofenceTransitionsIntentService 的 java 文件:

public class GeofenceTransitionsIntentService extends IntentService 
  protected static final String TAG = "GeofenceTransitionsIS";

  public GeofenceTransitionsIntentService() 
    super(TAG);  // use TAG to name the IntentService worker thread
  

  @Override
  protected void onHandleIntent(Intent intent) 
    GeofencingEvent event = GeofencingEvent.fromIntent(intent);
    if (event.hasError()) 
      Log.e(TAG, "GeofencingEvent Error: " + event.getErrorCode());
      return;
    
  

 String description = getGeofenceTransitionDetails(event);
   sendNotification(description);
 

   private static String getGeofenceTransitionDetails(GeofencingEvent event) 
String transitionString =
    GeofenceStatusCodes.getStatusCodeString(event.getGeofenceTransition());
List triggeringIDs = new ArrayList();
for (Geofence geofence : event.getTriggeringGeofences()) 
  triggeringIDs.add(geofence.getRequestId());

return String.format("%s: %s", transitionString, TextUtils.join(", ", triggeringIDs));


  private void sendNotification(String notificationDetails) 
    // Create an explicit content Intent that starts MainActivity.
    Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);

    // Get a PendingIntent containing the entire back stack.
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    stackBuilder.addParentStack(MainActivity.class).addNextIntent(notificationIntent);
    PendingIntent notificationPendingIntent =
        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

    // Get a notification builder that's compatible with platform versions >= 4
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);

    // Define the notification settings.
    builder.setColor(Color.RED)
        .setContentTitle(notificationDetails)
        .setContentText("Click notification to return to App")
        .setContentIntent(notificationPendingIntent)
        .setAutoCancel(true);

    // Fire and notify the built Notification.
    NotificationManager notificationManager =
        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(0, builder.build());
  

您可能应该坚持他们的教程,而不是将代码复制并粘贴到您的项目中:D

Source

【讨论】:

在onResult中,是否应该在顶部有@Override?我在 GeofenceErrorMessages.getErrorString(this, status.getStatusCode()); 中的 GeofenceErrorMessages 上显示红色文本,我找不到 GeoFenceErrorMessages 类 您好,@Override 是必需的,因为它将覆盖 onResult 的默认方法实现。 GeoFenceErrorMessages 类是一个自定义类,用于获取地理围栏的错误 ID 并返回错误消息,这部分不是地理围栏工作所必需的,但这是一个很好的做法,更多信息可以在 Udacity 的本课程中找到:udacity.com/course/google-location-services-on-android--ud876-1 @MarcolaCarr 我们需要在 manifest 文件中的 application 标签下声明 GeofenceTransitionsIntentService 吗?【参考方案2】:

我在服务中这样做

地理位置服务

    public class GeolocationService extends Service implements LocationListener,
        GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks 

    private Context mContext;
    private GoogleApiClient mGoogleApiClient;
    private LocationRequest mLocationRequest;
//    private Location mLastLocation;
    private PendingIntent mGeofencePendingIntent;
    private String mLastUpdateTime;
    public static boolean isGeoFenceAdded = false;
    private boolean mUpdateGeoFence = false;
    private boolean mRemoveAllGeoFence = false;

    private static final long TIME_OUT = 100;

    @Override
    public void onCreate() 
        super.onCreate();

        mContext = GeolocationService.this;
        buildGoogleApiClient();
        createLocationRequest();

    

    protected synchronized void buildGoogleApiClient() 
        mGoogleApiClient = new GoogleApiClient.Builder(mContext)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        /// FIXME: 2/15/2017 connect should be handled through onStart and onStop of Activity
        mGoogleApiClient.connect();
    

    protected void createLocationRequest() 
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(5000);//set the interval in which you want to get locations
        mLocationRequest.setFastestInterval(2500);//if a location is available sooner you can get it (i.e. another app is using the location services)
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
        if (intent != null) 
            if (intent.getAction() != null) 
                if (intent.getAction().equals(Constants.ACTION_UPDATE_GEOFENCE)) 
                    //// FIXME: 3/21/2017 you can also receive triggered location here..
                    mUpdateGeoFence = true;
                    isGeoFenceAdded = false;
                    mRemoveAllGeoFence = false;
                 else if (intent.getAction().equals(Constants.ACTION_ADD_GEOFENCE)) 
                    mUpdateGeoFence = false;
                    isGeoFenceAdded = false;
                    mRemoveAllGeoFence = false;
                 else if (intent.getAction().equals(Constants.ACTION_REMOVE_ALL_GEOFENCE)) 
                    mRemoveAllGeoFence = true;
                    isGeoFenceAdded = true;
                    mUpdateGeoFence = false;
                
            
        
        //try this for null as http://***.com/a/25096022/3496570
        ///return START_REDELIVER_INTENT;
        return super.onStartCommand(intent, flags, startId);
    

    @Nullable
    @Override
    public IBinder onBind(Intent intent) 
        return null;
    

    @Override
    public void onConnected(@Nullable Bundle bundle) 
        startLocationUpdates(mContext);
    

    @Override
    public void onConnectionSuspended(int i) 
        switch (i) 
            case CAUSE_SERVICE_DISCONNECTED:
                /*if (onLocationUpdateListener != null)
                    onLocationUpdateListener.onError(
                            Constants.ErrorType.SERVICE_DISCONNECTED);*/
                break;
            case CAUSE_NETWORK_LOST:
                /*if (onLocationUpdateListener != null)
                    onLocationUpdateListener.onError(
                            Constants.ErrorType.NETWORK_LOST);*/
                break;
        

        //// FIXME: 3/2/2017 check is it right to check for re Connecting..
        //---  http://***.com/a/27350444/3496570
        ///mGoogleApiClient.connect();
    

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) 
        /*if (onLocationUpdateListener != null)
            onLocationUpdateListener.onError(
                    Constants.ErrorType.CONNECTION_FAIL);*/

        //// FIXME: 3/3/2017 call a transparent activity and call startResolutionForresult from their and return result to service using action
        if (connectionResult.hasResolution()) 
            /*try 
                // !!!
                connectionResult.startResolutionForResult(this, REQUEST_CODE_RESOLVE_ERR);
             catch (IntentSender.SendIntentException e) 
                e.printStackTrace();
            */
         else 
            /*GoogleApiAvailability.getInstance().getErrorDialog(mContext, connectionResult.getErrorCode(), 0).show();
            return;*/
        
    

    @Override
    public void onLocationChanged(final Location currentLocation) 
        setupGeoFencePoints(currentLocation);
        /*if (onLocationUpdateListener != null && mLocation != null) 
            onLocationUpdateListener.onLocationChange(mLocation);
        */
    

    private void setupGeoFencePoints(final Location currentLocation) 

        mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
//        mLastLocation = currentLocation;

        if (currentLocation != null && isGeoFenceAdded == false)
        
            if (mUpdateGeoFence) 
                if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) 
                    LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient
                            , getGeofencePendingIntent()).setResultCallback(new ResultCallback<Status>() 
                        @Override
                        public void onResult(@NonNull Status status) 
                            if (status.isSuccess()) 
                                //if old geoFence's remove successfully then add new ones.
                                addGeoFences(currentLocation);
                            
                        
                    );
                
             else 
                addGeoFences(currentLocation);
            
        
        else if(isGeoFenceAdded && mRemoveAllGeoFence )
            if (mGoogleApiClient != null && mGoogleApiClient.isConnected())
            
                LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient
                        , mGeofencePendingIntent).setResultCallback(new ResultCallback<Status>() 
                    @Override
                    public void onResult(@NonNull Status status) 
                        if (status.isSuccess()) 

                            mRemoveAllGeoFence = false;
                            isGeoFenceAdded = false;
                            //if old geoFence's remove successfully then do nothing.
                            stopLocationUpdate();

                            if (mGoogleApiClient != null && mGoogleApiClient.isConnected())
                                mGoogleApiClient.disconnect();
                        
                    
                );
            
        
    

    @Override
    public void onDestroy() 
        super.onDestroy();
        stopLocationUpdate();

        if (mGoogleApiClient != null && mGoogleApiClient.isConnected())
            mGoogleApiClient.disconnect();
    

    private void startLocationUpdates(final Context mContext) 

        if (ActivityCompat.checkSelfPermission(mContext,
                Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(mContext,
                Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
            return;
        

//        mLastLocation = FusedLocationApi.getLastLocation(mGoogleApiClient);

        PendingResult<Status> pendingResult = FusedLocationApi.requestLocationUpdates(
                mGoogleApiClient, mLocationRequest, this);
        pendingResult.setResultCallback(new ResultCallback<Status>() 
            @Override
            public void onResult(@NonNull Status status) 
                //update it's code too.
                if (status.isSuccess()) 
                    Toast.makeText(mContext, "location Update Started",
                            Toast.LENGTH_SHORT).show();
                 else if (status.hasResolution()) 
                    Toast.makeText(mContext, "Open intent to resolve",
                            Toast.LENGTH_SHORT).show();
                
            
        );
    

    private void stopLocationUpdate() 
        //three types of constructor ..
        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) 
            FusedLocationApi.removeLocationUpdates(
                    mGoogleApiClient, this);
        
    

    public void addGeoFences(Location currentLocation) 
        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) 
            try 

                if (getGeofencingRequest(currentLocation) != null) 
                    LocationServices.GeofencingApi.addGeofences(
                            mGoogleApiClient,
                            // The GeofenceRequest object.
                            getGeofencingRequest(currentLocation),
                            // A pending intent that that is reused when calling removeGeofences(). This
                            // pending intent is used to generate an intent when a matched geofence
                            // transition is observed.
                            getGeofencePendingIntent()
                            //).await(TimeOut,TimeUnit.Miilisecponds);
                    ).setResultCallback(new ResultCallback<Status>() 
                        @Override
                        public void onResult(@NonNull Status status) 
                            if (status.isSuccess()) 
                                Toast.makeText(mContext, "Geo Fence Added", Toast.LENGTH_SHORT).show();

                                isGeoFenceAdded = true;
                                mRemoveAllGeoFence = false;
                                mUpdateGeoFence = false;

                                /// FIXME: 3/2/2017 I didn't have to draw it.
                                ///broadcastDrawGeoFenceOnMap();

                             else 
                                String errorMessage = getErrorString(mContext, status.getStatusCode());
                                Toast.makeText(mContext, "Status Failed", Toast.LENGTH_SHORT).show();
                            
                        
                    ); // Result processed in onResult().
                

             catch (SecurityException securityException) 
                securityException.printStackTrace();
                // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
                //logSecurityException(securityException);
            
        
    

    private PendingIntent getGeofencePendingIntent() 
        if (mGeofencePendingIntent != null) 
            return mGeofencePendingIntent;
        
        // Reuse the PendingIntent if we already have it.
        /// FIXME: 2/9/2017 Update the below class with a receiever..
        Intent intent = new Intent(mContext, GeofenceReceiver.class);///GeofenceTransitionsIntentService.class);
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
        // addGeofences() and removeGeofences().
        /// FIXME: 3/1/2017 It must be reciever not IntentService
        mGeofencePendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        return mGeofencePendingIntent;
    

    private GeofencingRequest getGeofencingRequest(Location mCurrentLocation) 

        /// FIXME: 2/13/2017 mLastLocation can be null because it will take time for the first time.
        /// this request should be called after first mLastLocation has been fetched..
        GeofencingRequest geofencingRequest = null;
        if (mCurrentLocation != null) 
            List<SimpleGeofence> simpleFenceList = SimpleGeofenceStore
                    .getInstance().getLatestGeoFences(mCurrentLocation);

            simpleFenceList.add(new SimpleGeofence("currentLocation",
                    mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(),
                    100f, GEOFENCE_EXPIRATION_IN_MILLISECONDS,
                    Geofence.GEOFENCE_TRANSITION_EXIT));

            ListSharedPref.saveAnyTypeOfList(ListSharedPref.GEO_FENCE_LIST_KEY, simpleFenceList);

            GeofencingRequest.Builder geofencingRequestBuilder = new GeofencingRequest.Builder();
            geofencingRequestBuilder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);

            for (SimpleGeofence simpleGeofence : simpleFenceList)
                geofencingRequestBuilder.addGeofence(simpleGeofence.toGeofence());

            geofencingRequest = geofencingRequestBuilder.build();
        
        // Return a GeofencingRequest.
        return geofencingRequest;
    

地理围栏接收器

public class GeofenceReceiver extends IntentService 
    public static final int NOTIFICATION_ID = 1;

    public GeofenceReceiver() 
        super("GeofenceReceiver");
    

    @Override
    protected void onHandleIntent(Intent intent) 
        GeofencingEvent geoEvent = GeofencingEvent.fromIntent(intent);

        Location triggredLocation = geoEvent.getTriggeringLocation();

        if (geoEvent.hasError()) 
            Log.d(HomeActivity.TAG, "Error GeofenceReceiver.onHandleIntent");
         else 
            Log.d(HomeActivity.TAG, "GeofenceReceiver : Transition -> "
                    + geoEvent.getGeofenceTransition());

            int transitionType = geoEvent.getGeofenceTransition();

            if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER
                    || transitionType == Geofence.GEOFENCE_TRANSITION_DWELL
                    || transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) 
                List<Geofence> triggerList = geoEvent.getTriggeringGeofences();

                //if(triggerList.g)


                Type listType = new TypeToken<ArrayList<SimpleGeofence>>().getType();
                List<SimpleGeofence> geoFenceList = GenericPref.readAnyTypeOfList(GenericPref.GEO_FENCE_LIST_KEY,listType);

                for (Geofence geofence : triggerList)
                
                    /*SimpleGeofence sg = SimpleGeofenceStore.getInstance()
                            .getSimpleGeofences().get(geofence.getRequestId());*/

                    SimpleGeofence sg = null;
                    for(SimpleGeofence simpleGeofence : geoFenceList)
                        if(simpleGeofence.getId().equalsIgnoreCase(geofence.getRequestId()))
                            sg = simpleGeofence;
                            break;
                        
                    

                    String transitionName = "";
                    switch (transitionType) 
                        case Geofence.GEOFENCE_TRANSITION_DWELL:
                            transitionName = "dwell";
                            break;

                        case Geofence.GEOFENCE_TRANSITION_ENTER:
                            transitionName = "enter";

                            String date = DateFormat.format("yyyy-MM-dd hh:mm:ss",
                                    new Date()).toString();
                            EventDataSource eds = new EventDataSource(
                                    getApplicationContext());
                            eds.create(transitionName, date, geofence.getRequestId());
                            eds.close();

                            GeofenceNotification geofenceNotification = new GeofenceNotification(
                                    this);
                            if(sg != null)
                                geofenceNotification
                                        .displayNotification(sg, transitionType);
                            
                            break;

                        case Geofence.GEOFENCE_TRANSITION_EXIT:
                            transitionName = "exit";
                            broadcastUpdateGeoFences();
                            //update your List
                            // Unregister all geoFences and reRegister it again
                            break;
                    
                
            
        
    

    public void broadcastUpdateGeoFences() 
        //// FIXME: 3/2/2017 what if app is closed
        HomeActivity.geofencesAlreadyRegistered = false;
        MainActivity.isGeoFenceAdded = false;
        Intent intent = new Intent(Constants.RECEIVER_GEOFENCE);
        intent.putExtra("done", 1);
        sendBroadcast(intent);
    

【讨论】:

【参考方案3】:

我的要求是当我点击地图时,然后在那个位置,我正在创建地理围栏圈。并在我们当前的位置进入该栅栏以及退出该栅栏时显示通知。

MapsActivity.java

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.location.Location;

import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.LocationListener;

import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.MenuItem;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener,
        LocationListener,
        GoogleMap.OnMapClickListener, ResultCallback<Status> 

    private GoogleMap mMap;
    SupportMapFragment mapFragment;
    LocationRequest mLocationRequest;
    GoogleApiClient mGoogleApiClient;
    Location mLastLocation;
    Marker mCurrLocationMarker;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    


    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    @Override
    public void onMapReady(GoogleMap googleMap) 
        mMap = googleMap;

        mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);

        mMap.setOnMapClickListener(this);

        //Initialize Google Play Services
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) 
                //Location Permission already granted
                buildGoogleApiClient();
                mMap.setMyLocationEnabled(true);
             else 
                //Request Location Permission
                checkLocationPermission();
            
         else 
            buildGoogleApiClient();
            mMap.setMyLocationEnabled(true);
        
    

    protected synchronized void buildGoogleApiClient() 
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        mGoogleApiClient.connect();
    

    @Override
    public void onConnected(Bundle bundle) 
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(1000);
        mLocationRequest.setFastestInterval(1000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) 
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        
    

    @Override
    public void onConnectionSuspended(int i) 
    


    @Override
    public void onLocationChanged(Location location) 
        mLastLocation = location;
        if (mCurrLocationMarker != null) 
            mCurrLocationMarker.remove();
        

        //Place current location marker
        LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.position(latLng);
        markerOptions.title("Current Position");
        markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA));
        mCurrLocationMarker = mMap.addMarker(markerOptions);

        //move map camera
        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15));

        Location targetLocation = new Location("");//provider name is unnecessary
        targetLocation.setLatitude(18.569557d);//your coords of course
        targetLocation.setLongitude(73.879369d);

       // This is also working

       /* if (location.distanceTo(targetLocation) < 100) 
            // bingo!
            Toast.makeText(this, "You are at MplusSoft Technology", Toast.LENGTH_SHORT).show();
         else 
            Toast.makeText(this, "You are not at your place.", Toast.LENGTH_SHORT).show();

        */

    


    public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;

    private void checkLocationPermission() 
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) 

            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.ACCESS_FINE_LOCATION)) 

                // Show an explanation to the user *asynchronously* -- don't block
                // this thread waiting for the user's response! After the user
                // sees the explanation, try again to request the permission.
                new AlertDialog.Builder(this)
                        .setTitle("Location Permission Needed")
                        .setMessage("This app needs the Location permission, please accept to use location functionality")
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() 
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) 
                                //Prompt the user once explanation has been shown
                                ActivityCompat.requestPermissions(MapsActivity.this,
                                        new String[]Manifest.permission.ACCESS_FINE_LOCATION,
                                        MY_PERMISSIONS_REQUEST_LOCATION);
                            
                        )
                        .create()
                        .show();


             else 
                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(this,
                        new String[]Manifest.permission.ACCESS_FINE_LOCATION,
                        MY_PERMISSIONS_REQUEST_LOCATION);
            
        
    

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) 
        switch (requestCode) 
            case MY_PERMISSIONS_REQUEST_LOCATION: 
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) 

                    // permission was granted, yay! Do the
                    // location-related task you need to do.
                    if (ContextCompat.checkSelfPermission(this,
                            Manifest.permission.ACCESS_FINE_LOCATION)
                            == PackageManager.PERMISSION_GRANTED) 

                        if (mGoogleApiClient == null) 
                            buildGoogleApiClient();
                        
                        mMap.setMyLocationEnabled(true);
                    

                 else 

                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                    Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show();
                
                return;
            

            // other 'case' lines to check for other
            // permissions this app might request
        
    

    @Override
    public void onPause() 
        super.onPause();

        //stop location updates when Activity is no longer active
        if (mGoogleApiClient != null) 
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, (com.google.android.gms.location.LocationListener) this);
        
    


    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) 

    


    //------------------------------------- Geo fencing -------------------------------------


    @Override
    protected void onResume() 
        super.onResume();

    

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


    @Override
    public void onMapClick(LatLng latLng) 
        markerForGeofence(latLng);
    

    private Marker geoFenceMarker;

    // Create a marker for the geofence creation
    private void markerForGeofence(LatLng latLng) 
        Log.e("markerForGeofence", "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 (mMap != null) 
            // Remove last geoFenceMarker
            if (geoFenceMarker != null)
                geoFenceMarker.remove();

            geoFenceMarker = mMap.addMarker(markerOptions);

            startGeofence();
        
    


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

    // Create a Geofence
    private Geofence createGeofence(LatLng latLng, float radius) 
        Log.e("createGeofence", "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();
    

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

    private PendingIntent geoFencePendingIntent;
    private final int GEOFENCE_REQ_CODE = 0;

    @SuppressLint("LongLogTag")
    private PendingIntent createGeofencePendingIntent() 
        Log.e("createGeofencePendingIntent", "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.e("addGeofence", "addGeofence");
        //if (checkPermission())
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) 
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        
        LocationServices.GeofencingApi.addGeofences(
                mGoogleApiClient,
                request,
                createGeofencePendingIntent()
        ).setResultCallback(this);
    



    // Draw Geofence circle on GoogleMap
    private Circle geoFenceLimits;
    private void drawGeofence() 
        Log.e("drawGeofence()", "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 = mMap.addCircle( circleOptions );
    



    // Start Geofence creation process
    private void startGeofence() 
        Log.e("startGeofence", "startGeofence()");
        if( geoFenceMarker != null ) 
            Geofence geofence = createGeofence( geoFenceMarker.getPosition(), GEOFENCE_RADIUS );
            GeofencingRequest geofenceRequest = createGeofenceRequest( geofence );
            addGeofence( geofenceRequest );
         else 
            Log.e("Geofence marker is null", "Geofence marker is null");
        
    

    static Intent makeNotificationIntent(Context geofenceService, String msg)
    
        Log.e("makeNotificationIntent ",msg);
        return new Intent(geofenceService,MainActivity.class);
    


GeofenceTrasitionService.java

import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;

import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofenceStatusCodes;
import com.google.android.gms.location.GeofencingEvent;

import java.util.ArrayList;
import java.util.List;

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 = MapsActivity.makeNotificationIntent(
                getApplicationContext(), msg
        );

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(MapsActivity.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_launcher_background)
                .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.";
        
    

activity_maps.xml

    <?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:map="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_
    android:layout_
    tools:context=".MapsActivity" />

清单

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


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

        <activity
            android:name=".MapsActivity"
            android:label="@string/title_activity_maps"> <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter></activity>

        <service android:name=".GeofenceTrasitionService"/>

build.gradle

implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
implementation 'com.google.android.gms:play-services-maps:15.0.1'
implementation 'com.google.android.gms:play-services-location:15.0.1'

【讨论】:

以上是关于如何在 Android 中创建地理围栏?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 android 地理围栏中实现“取消驻留”?

Android:如何在用户关闭位置设置时获取地理围栏事件?

如何使用 Android 地理围栏 API?

如何提高 Android 地理围栏进入/退出通知的一致性?

Android 地理围栏(多边形)

如何在奥利奥后台监控地理围栏?