如何在 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 中创建地理围栏?的主要内容,如果未能解决你的问题,请参考以下文章