在后台服务中创建地理围栏
Posted
技术标签:
【中文标题】在后台服务中创建地理围栏【英文标题】:creating geofences in background service 【发布时间】:2016-11-10 20:31:30 【问题描述】:我正在制作一个 android 应用程序,希望在我的服务器上指定的 LatLong 坐标处创建地理围栏。
坐标会在后台从服务中定期更新。我正在阅读这些坐标,并希望在没有用户干预的情况下在后台为其创建地理围栏。我尝试在服务中这样做,但它不起作用。我可以定期从服务器获取坐标,但我无法为它创建地理围栏,得到一个 nullPointerException。
但是,我可以通过单击按钮(硬编码 LatLong 坐标)在 Activity 中创建地理围栏,但它在服务中不起作用。
我的服务:
public class GPSPollingService extends Service implements ResultCallback<Status>, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener
private GoogleApiClient mGoogleApiClient;
private PowerManager.WakeLock mWakeLock;
private LocationRequest mLocationRequest;
// Flag that indicates if a request is underway.
private boolean mInProgress;
private final String TAG = "GPSPollingService";
private PendingIntent mGeofencePendingIntent;
String url = "http://shielded.coolpage.biz/status_read.php";
private static final int REQUEST_CODE_LOCATION = 2;
private Boolean servicesAvailable = false;
private Activity mActivity;
ArrayList<Geofence> mCurrentGeofences = new ArrayList<Geofence>();;
RequestQueue requestQueue;
Location mLocation;
public GPSPollingService()
public GPSPollingService(Activity mActivity)
super();
this.mActivity = mActivity;
@Nullable
@Override
public IBinder onBind(Intent intent)
return null;
@Override
public void onCreate()
super.onCreate();
mInProgress = false;
requestQueue = Volley.newRequestQueue(this);
// Instantiate the current List of geofences
mCurrentGeofences = new ArrayList<Geofence>();
mGeofencePendingIntent = null;
setUpLocationClientIfNeeded();
@Override
public int onStartCommand(Intent intent, int flags, int startId)
super.onStartCommand(intent, flags, startId);
mGoogleApiClient.connect();
Log.d(TAG,"onStartCommand: ");
PowerManager mgr = (PowerManager)getSystemService(Context.POWER_SERVICE);
/*
WakeLock is reference counted so we don't want to create multiple WakeLocks. So do a check before initializing and acquiring.
This will fix the "java.lang.Exception: WakeLock finalized while still held: MyWakeLock" error that you may find.
*/
if (this.mWakeLock == null) //**Added this
this.mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
if (!this.mWakeLock.isHeld()) //**Added this
this.mWakeLock.acquire();
if(!servicesAvailable || mGoogleApiClient.isConnected() || mInProgress)
return START_STICKY;
setUpLocationClientIfNeeded();
if(!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress)
//appendLog(DateFormat.getDateTimeInstance().format(new Date()) + ": Started", Constants.LOG_FILE);
mInProgress = true;
mGoogleApiClient.connect();
return START_STICKY;
private void setUpLocationClientIfNeeded()
if(mGoogleApiClient == null)
googleApiClientBuild();
protected synchronized void googleApiClientBuild()
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
@Override
public void onConnected(@Nullable Bundle bundle)
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(120000); // Update location every 60 seconds
Log.d(TAG,"in onConnected: ");
if ( ContextCompat.checkSelfPermission( this, android.Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED )
ActivityCompat.requestPermissions( mActivity, new String[] android.Manifest.permission.ACCESS_COARSE_LOCATION ,
REQUEST_CODE_LOCATION );
else
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
//Location mLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
//txtOutput.setText(mLocation.toString());
Log.d(TAG, "calling fusedLocationApi");
continueAddGeofences();
@Override
public void onConnectionSuspended(int i)
// Turn off the request flag
mInProgress = false;
// Destroy the current location client
mGoogleApiClient = null;
Log.d(TAG, "onConnectionSuspended ");
@Override
public void onLocationChanged(Location location)
mLocation = location;
Log.d(TAG,"in onLocationChanged: ");
Log.d(TAG, mLocation.toString());
// save new location to db
connectToDb(); // checking for status 1
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult)
mInProgress = false;
Log.d(TAG, "onConnectionFailed");
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution())
Log.d(TAG, "google play services has solution");
// If no resolution is available, display an error dialog
else
Log.d(TAG, "no solution for connection failure");
@Override
public void onDestroy()
// Turn off the request flag
this.mInProgress = false;
if (this.servicesAvailable && this.mGoogleApiClient != null)
this.mGoogleApiClient.unregisterConnectionCallbacks(this);
this.mGoogleApiClient.unregisterConnectionFailedListener(this);
this.mGoogleApiClient.disconnect();
// Destroy the current location client
this.mGoogleApiClient = null;
// Display the connection status
// Toast.makeText(this, DateFormat.getDateTimeInstance().format(new Date()) + ":
// Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();
if (this.mWakeLock != null)
this.mWakeLock.release();
this.mWakeLock = null;
super.onDestroy();
public void connectToDb()
StringRequest stringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>()
@Override
public void onResponse(String s)
Toast.makeText(getApplicationContext() , ""+s,Toast.LENGTH_LONG).show();
Log.d(TAG, ""+s);
try
JSONObject jsonObject = new JSONObject(s);
JSONArray jsonArray = jsonObject.getJSONArray("result");
if(jsonArray.length()!=0)
Log.d(TAG,"array length not 0");
JSONObject personInDanger = jsonArray.getJSONObject(0);
setGeofence(Double.valueOf(personInDanger.getString("latitude")), Double.valueOf(personInDanger.getString("longitude")));
catch (JSONException je)
// do something with it
, new Response.ErrorListener()
@Override
public void onErrorResponse(VolleyError volleyError)
Toast.makeText(getApplicationContext(), ""+volleyError, Toast.LENGTH_LONG).show();
);
requestQueue.add(stringRequest);
/**
* Verify that Google Play services is available before making a request.
*
* @return true if Google Play services is available, otherwise false
*/
private boolean servicesConnected()
// Check that Google Play services is available
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode)
// In debug mode, log the status
Log.d(GeofenceUtils.APPTAG, getString(R.string.play_services_available));
// Continue
return true;
// Google Play services was not available for some reason
else
// Display an error dialog
Toast.makeText(getApplicationContext(), "Google play services not available", Toast.LENGTH_SHORT).show();
//Dialog dialog = GooglePlayServicesUtil.getErrorDialog(resultCode, (Activity) getApplicationContext(), 0);
//if (dialog != null)
// ErrorDialogFragment errorFragment = new ErrorDialogFragment();
// errorFragment.setDialog(dialog);
// errorFragment.show(getSupportFragmentManager(), GeofenceUtils.APPTAG);
return false;
public void setGeofence(Double latitude , Double longitude)
Log.d(TAG, "in setGeofence");
if (!servicesConnected())
return;
SimpleGeofence newGeofence = new SimpleGeofence(
"1",
// Get latitude, longitude, and radius from the db
latitude,
longitude,
Float.valueOf("50.0"),
// Set the expiration time
Geofence.NEVER_EXPIRE,
// Detect both entry and exit transitions
Geofence.GEOFENCE_TRANSITION_ENTER
);
mCurrentGeofences.add(newGeofence.toGeofence());
// Start the request. Fail if there's already a request in progress
try
// Try to add geofences
addGeofences(mCurrentGeofences);
catch (UnsupportedOperationException e)
// Notify user that previous request hasn't finished.
Toast.makeText(this, R.string.add_geofences_already_requested_error,
Toast.LENGTH_LONG).show();
public void addGeofences(ArrayList<Geofence> geofences) throws UnsupportedOperationException
if (!mInProgress)
// Toggle the flag and continue
mInProgress = true;
// Request a connection to Location Services
requestConnection();
// If a request is in progress
else
// Throw an exception and stop the request
throw new UnsupportedOperationException();
private void requestConnection()
getLocationClient().connect();
private GoogleApiClient getLocationClient()
if (mGoogleApiClient == null)
mGoogleApiClient = new GoogleApiClient.Builder(mActivity)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
return mGoogleApiClient;
private void continueAddGeofences()
// Get a PendingIntent that Location Services issues when a geofence transition occurs
mGeofencePendingIntent = createRequestPendingIntent();
if (!mGoogleApiClient.isConnected())
Toast.makeText(mActivity, "not connected", Toast.LENGTH_SHORT).show();
return;
try
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
// The GeofenceRequest object.
mCurrentGeofences,
// 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.
mGeofencePendingIntent
).setResultCallback(this); // Result processed in onResult().
catch (SecurityException securityException)
// Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
logSecurityException(securityException);
private void logSecurityException(SecurityException securityException)
Log.e(GeofenceUtils.APPTAG , "Invalid location permission. " +
"You need to use ACCESS_FINE_LOCATION with geofences", securityException);
public void onResult(Status status)
Intent broadcastIntent = new Intent();
// Temp storage for messages
String msg;
if (status.isSuccess())
Toast.makeText(
mActivity, "Geofences added",
Toast.LENGTH_SHORT
).show();
msg = mActivity.getString(R.string.add_geofences_result_success, status);
// In debug mode, log the result
Log.d(GeofenceUtils.APPTAG, msg);
// Create an Intent to broadcast to the app
broadcastIntent.setAction(GeofenceUtils.ACTION_GEOFENCES_ADDED)
.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES)
.putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, msg);
// If adding the geofences failed
else
/*
* Create a message containing the error code and the list
* of geofence IDs you tried to add
*/
msg = mActivity.getString(
R.string.add_geofences_result_failure,
status.getStatusCode(), status.getStatusMessage());
// Log an error
Log.e(GeofenceUtils.APPTAG, msg);
private PendingIntent createRequestPendingIntent()
// If the PendingIntent already exists
if (null != mGeofencePendingIntent)
// Return the existing intent
return mGeofencePendingIntent;
// If no PendingIntent exists
else
Intent intent = new Intent("com.example.android.geofence.ACTION_RECEIVE_GEOFENCE");
return PendingIntent.getBroadcast(
mActivity,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
我的日志:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
at android.app.PendingIntent.getBroadcastAsUser(PendingIntent.java:506)
at android.app.PendingIntent.getBroadcast(PendingIntent.java:495)
at com.example.android.safetyalert.GPSPollingService.createRequestPendingIntent(GPSPollingService.java:449)
at com.example.android.safetyalert.GPSPollingService.continueAddGeofences(GPSPollingService.java:369)
at com.example.android.safetyalert.GPSPollingService.onConnected(GPSPollingService.java:163)
at com.google.android.gms.common.internal.zzl.zzm(Unknown Source)
at com.google.android.gms.internal.zzof.zzk(Unknown Source)
at com.google.android.gms.internal.zzod.zzsb(Unknown Source)
at com.google.android.gms.internal.zzod.onConnected(Unknown Source)
at com.google.android.gms.internal.zzoh.onConnected(Unknown Source)
at com.google.android.gms.internal.zznw.onConnected(Unknown Source)
at com.google.android.gms.common.internal.zzk$1.onConnected(Unknown Source)
at com.google.android.gms.common.internal.zzd$zzj.zztp(Unknown Source)
at com.google.android.gms.common.internal.zzd$zza.zzc(Unknown Source)
at com.google.android.gms.common.internal.zzd$zza.zzw(Unknown Source)
at com.google.android.gms.common.internal.zzd$zze.zztr(Unknown Source)
at com.google.android.gms.common.internal.zzd$zzd.handleMessage(Unknown Source)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5312)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696)
我已经尝试了我能想到的一切。 我正在轮询 GPS,以便为操作系统提供准确的位置,以提高地理围栏的准确性。我知道它会耗尽我的电池,但现在这不是问题。
【问题讨论】:
您的代码工作正常吗?如果是,请上传代码。我在地理围栏中使用后台服务。 【参考方案1】:Service
已经有Context
。您不应该使用来自Activity
的Context
,因为如果您的Activity
被杀死,mActivity
将为空,从而导致各种问题。用这个替换 createRequestPendingIntent
中的 else 块
else
Intent intent = new Intent("com.example.android.geofence.ACTION_RECEIVE_GEOFENCE");
return PendingIntent.getBroadcast(
this,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
【讨论】:
以上是关于在后台服务中创建地理围栏的主要内容,如果未能解决你的问题,请参考以下文章