如何在 Geofence android 中添加后台服务
Posted
技术标签:
【中文标题】如何在 Geofence android 中添加后台服务【英文标题】:How to add background service in Geofence android 【发布时间】:2018-10-22 17:22:51 【问题描述】:我正在尝试创建一个带有后台服务的地理围栏以进行监控。地理围栏创建成功并在应用程序活动打开但关闭应用程序地理围栏时工作。我现在该怎么办。我的代码是:
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener
@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.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
ref = FirebaseDatabase.getInstance().getReference("MyLocation");
geoFire = new GeoFire(ref);
mVerticalSeekBar = (VerticalSeekBar)findViewById(R.id.verticalSeekBar);
setUpdateLocation();
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
switch (requestCode)
case MY_PERMISSION_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
if (checkPlayService())
buildGoogleApiClient();
createLocationRequest();
displayLocation();
break;
private void setUpdateLocation()
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(this, new String[]
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
, MY_PERMISSION_REQUEST_CODE);
else
if (checkPlayService())
buildGoogleApiClient();
createLocationRequest();
displayLocation();
private void displayLocation()
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
return;
mLastLocaiton = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mLastLocaiton != null)
final double latitude = mLastLocaiton.getLatitude();
final double longitude = mLastLocaiton.getLongitude();
geoFire.setLocation("You", new GeoLocation(latitude, longitude), new GeoFire.CompletionListener()
@Override
public void onComplete(String key, DatabaseError error)
if (mCurrent != null)
mCurrent.remove();
mCurrent = mMap.addMarker(new MarkerOptions()
.position(new LatLng(latitude, longitude))
.title("You"));
LatLng coordinate = new LatLng(latitude, longitude);
CameraUpdate yourLocation = CameraUpdateFactory.newLatLngZoom(coordinate, 12);
mMap.animateCamera(yourLocation);
);
Log.d("MRF", String.format("Your last location was chaged: %f / %f", latitude, longitude));
else
Log.d("MRF", "Can not get your location.");
private void createLocationRequest()
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FATEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
private void buildGoogleApiClient()
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API).build();
mGoogleApiClient.connect();
private boolean checkPlayService()
GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance();
int result = googleAPI.isGooglePlayServicesAvailable(this);
if (result != ConnectionResult.SUCCESS)
if (googleAPI.isUserResolvableError(result))
googleAPI.getErrorDialog(this, result, PLAY_SERVICE_RESULATION_REQUEST).show();
else
Toast.makeText(this, "This Device is not supported.", Toast.LENGTH_SHORT).show();
return false;
return true;
@Override
public void onMapReady(GoogleMap googleMap)
mMap = googleMap;
LatLng dangerous_area = new LatLng(23.7424236, 90.3942189);
mMap.addCircle(new CircleOptions()
.center(dangerous_area)
.radius(100)
.strokeColor(Color.BLUE)
.fillColor(0x220000FF)
.strokeWidth(5.0f));
//add GeoQuery here
GeoQuery geoQuery = geoFire.queryAtLocation(new GeoLocation(dangerous_area.latitude, dangerous_area.longitude), 0.1f);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener()
@Override
public void onKeyEntered(String key, GeoLocation location)
sendNotification("MRF", String.format("%s entered the dangerous area",key));
@Override
public void onKeyExited(String key)
sendNotification("MRF", String.format("%s exit the dangerous area",key));
@Override
public void onKeyMoved(String key, GeoLocation location)
Log.d("MOVE", String.format("%s move within the dangerous area [%f/%f]", key, location.latitude, location.longitude));
@Override
public void onGeoQueryReady()
@Override
public void onGeoQueryError(DatabaseError error)
Log.d("ERROR", ""+error);
);
private void sendNotification(String title, String content)
Notification.Builder builder = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(content);
NotificationManager manager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = new Intent(this, MapsActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
builder.setContentIntent(contentIntent);
Notification notification = builder.build();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
manager.notify(new Random().nextInt(), notification);
@Override
public void onConnected(@Nullable Bundle bundle)
displayLocation();
startLocationUpdate();
private void startLocationUpdate()
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
return;
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
@Override
public void onConnectionSuspended(int i)
mGoogleApiClient.connect();
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult)
@Override
public void onLocationChanged(Location location)
mLastLocaiton = location;
displayLocation();
github链接为:https://github.com/Farhad2015/Geofence-GeoFire/blob/master/app/src/main/java/com/mahmud/geotesting/MapsActivity.java
请帮我创建一个后台服务,用于从后台监控地理围栏。
【问题讨论】:
只需创建一个后台服务并使用作业调度程序管理后台服务即可在后台撤消android服务。可能是它对你的工作,请点击此链接developer.android.com/reference/android/app/job/JobScheduler。 【参考方案1】:在 Android Oreo 中,您无法将长时间运行的服务创建为后台服务。所以你必须创建一个前台服务,该服务必须绑定到当前的Activity
另一个问题是位置更新。当您在服务中使用geoQuery
时,您还必须在服务中更新位置。当位置更新触发时,您必须将此更新传递给活动,然后只有 UI 可以更新。在我的解决方案中,我使用一个界面来更新 UI。您还可以使用广播侦听器。
当您创建服务并将其绑定到活动时,您可以在服务中使用地理围栏
public void startService(LatLng latLng, double radius)
if (!isServiceRunning)
isServiceRunning = true;
else
Log.e(TAG, "startTimer request for an already running timer");
if (geoQuery!=null)
geoQuery.removeAllListeners();
geoQuery = geoFire.queryAtLocation(new GeoLocation(latLng.latitude, latLng.longitude), 2f);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener()
@Override
public void onKeyEntered(String key, GeoLocation location)
sendNotification("MRF", String.format("%s entered the dangerous area", key));
@Override
public void onKeyExited(String key)
sendNotification("MRF", String.format("%s exit the dangerous area", key));
@Override
public void onKeyMoved(String key, GeoLocation location)
Log.d("MOVE", String.format("%s move within the dangerous area [%f/%f]", key, location.latitude, location.longitude));
@Override
public void onGeoQueryReady()
@Override
public void onGeoQueryError(DatabaseError error)
Log.d("ERROR", "" + error);
);
您可以从您的活动中调用此方法
geoService.startService(dangerous_area,2000);
即使您的应用程序被销毁,这也将起作用。
完整代码如下
public class MapsActivity extends FragmentActivity implements OnMapReadyCallback
private GoogleMap mMap;
//Play Service Location
private static final int MY_PERMISSION_REQUEST_CODE = 7192;
private static final int PLAY_SERVICE_RESULATION_REQUEST = 300193;
private Location mLastLocaiton;
private static int UPDATE_INTERVAL = 5000;
private static int FATEST_INTERVAL = 3000;
private static int DISPLACEMENT = 10;
Marker mCurrent;
VerticalSeekBar mVerticalSeekBar;
private static final String TAG = MapsActivity.class.getSimpleName();
private GeoService geoService;
private boolean serviceBound;
@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.
mVerticalSeekBar = (VerticalSeekBar) findViewById(R.id.verticalSeekBar);
mVerticalSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
mMap.animateCamera(CameraUpdateFactory.zoomTo(progress), 1500, null);
@Override
public void onStartTrackingTouch(SeekBar seekBar)
@Override
public void onStopTrackingTouch(SeekBar seekBar)
);
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
switch (requestCode)
case MY_PERMISSION_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
if (checkPlayService())
geoService.buildGoogleApiClient();
geoService.createLocationRequest();
geoService.displayLocation();
geoService.setLocationChangeListener(new GeoService.LocationChangeListener()
@Override
public void onLocationChange(Location location)
if (mCurrent != null)
mCurrent.remove();
mCurrent = mMap.addMarker(new MarkerOptions()
.position(new LatLng(location.getLatitude(), location.getLongitude()))
.title("You"));
LatLng coordinate = new LatLng(location.getLatitude(), location.getLongitude());
CameraUpdate yourLocation = CameraUpdateFactory.newLatLngZoom(coordinate, 12);
mMap.animateCamera(yourLocation);
);
break;
private void setUpdateLocation()
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(this, new String[]
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
, MY_PERMISSION_REQUEST_CODE);
else
if (checkPlayService())
geoService.buildGoogleApiClient();
geoService.createLocationRequest();
geoService.displayLocation();
geoService.setLocationChangeListener(new GeoService.LocationChangeListener()
@Override
public void onLocationChange(Location location)
if (mCurrent != null)
mCurrent.remove();
mCurrent = mMap.addMarker(new MarkerOptions()
.position(new LatLng(location.getLatitude(), location.getLongitude()))
.title("You"));
LatLng coordinate = new LatLng(location.getLatitude(), location.getLongitude());
CameraUpdate yourLocation = CameraUpdateFactory.newLatLngZoom(coordinate, 12);
mMap.animateCamera(yourLocation);
);
private boolean checkPlayService()
GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance();
int result = googleAPI.isGooglePlayServicesAvailable(this);
if (result != ConnectionResult.SUCCESS)
if (googleAPI.isUserResolvableError(result))
googleAPI.getErrorDialog(this, result, PLAY_SERVICE_RESULATION_REQUEST).show();
else
Toast.makeText(this, "This Device is not supported.", Toast.LENGTH_SHORT).show();
return false;
return true;
@Override
public void onMapReady(GoogleMap googleMap)
mMap = googleMap;
LatLng dangerous_area = new LatLng(8.5324236, 76.8842189);
mMap.addCircle(new CircleOptions()
.center(dangerous_area)
.radius(2000)
.strokeColor(Color.BLUE)
.fillColor(0x220000FF)
.strokeWidth(5.0f));
geoService.startService(dangerous_area,2000);
@Override
protected void onStart()
super.onStart();
if (Log.isLoggable(TAG, Log.VERBOSE))
Log.v(TAG, "Starting and binding service");
Intent i = new Intent(this, GeoService.class);
startService(i);
bindService(i, mConnection, 0);
@Override
protected void onStop()
super.onStop();
if (serviceBound)
// If a timer is active, foreground the service, otherwise kill the service
if (geoService.isServiceRunning())
geoService.foreground();
else
stopService(new Intent(this, GeoService.class));
// Unbind the service
unbindService(mConnection);
serviceBound = false;
/**
* Callback for service binding, passed to bindService()
*/
private ServiceConnection mConnection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName className, IBinder service)
if (Log.isLoggable(TAG, Log.VERBOSE))
Log.v(TAG, "Service bound");
GeoService.RunServiceBinder binder = (GeoService.RunServiceBinder) service;
geoService = binder.getService();
serviceBound = true;
// Ensure the service is not in the foreground when bound
geoService.background();
setUpdateLocation();
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(MapsActivity.this);
@Override
public void onServiceDisconnected(ComponentName name)
if (Log.isLoggable(TAG, Log.VERBOSE))
Log.v(TAG, "Service disconnect");
serviceBound = false;
;
public static class GeoService extends Service implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener
private LocationRequest mLocationRequest;
private GoogleApiClient mGoogleApiClient;
private Location mLastLocation;
private DatabaseReference ref;
private GeoFire geoFire;
private LocationChangeListener mLocationChangeListener;
private static final String TAG = GeoService.class.getSimpleName();
// Is the service tracking time?
private boolean isServiceRunning;
// Foreground notification id
private static final int NOTIFICATION_ID = 1;
// Service binder
private final IBinder serviceBinder = new RunServiceBinder();
private GeoQuery geoQuery;
public class RunServiceBinder extends Binder
GeoService getService()
return GeoService.this;
@Override
public void onCreate()
if (Log.isLoggable(TAG, Log.VERBOSE))
Log.v(TAG, "Creating service");
ref = FirebaseDatabase.getInstance().getReference("MyLocation");
geoFire = new GeoFire(ref);
isServiceRunning = false;
@Override
public int onStartCommand(Intent intent, int flags, int startId)
if (Log.isLoggable(TAG, Log.VERBOSE))
Log.v(TAG, "Starting service");
return Service.START_STICKY;
@Override
public IBinder onBind(Intent intent)
if (Log.isLoggable(TAG, Log.VERBOSE))
Log.v(TAG, "Binding service");
return serviceBinder;
@Override
public void onDestroy()
super.onDestroy();
if (Log.isLoggable(TAG, Log.VERBOSE))
Log.v(TAG, "Destroying service");
/**
* Starts the timer
*/
public void startService(LatLng latLng, double radius)
if (!isServiceRunning)
isServiceRunning = true;
else
Log.e(TAG, "startService request for an already running Service");
if (geoQuery!=null)
geoQuery.removeAllListeners();
geoQuery = geoFire.queryAtLocation(new GeoLocation(latLng.latitude, latLng.longitude), 2f);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener()
@Override
public void onKeyEntered(String key, GeoLocation location)
sendNotification("MRF", String.format("%s entered the dangerous area", key));
@Override
public void onKeyExited(String key)
sendNotification("MRF", String.format("%s exit the dangerous area", key));
@Override
public void onKeyMoved(String key, GeoLocation location)
Log.d("MOVE", String.format("%s move within the dangerous area [%f/%f]", key, location.latitude, location.longitude));
@Override
public void onGeoQueryReady()
@Override
public void onGeoQueryError(DatabaseError error)
Log.d("ERROR", "" + error);
);
/**
* Stops the timer
*/
public void stopService()
if (isServiceRunning)
isServiceRunning = false;
geoQuery.removeAllListeners();
else
Log.e(TAG, "stopTimer request for a timer that isn't running");
/**
* @return whether the service is running
*/
public boolean isServiceRunning()
return isServiceRunning;
/**
* Place the service into the foreground
*/
public void foreground()
startForeground(NOTIFICATION_ID, createNotification());
/**
* Return the service to the background
*/
public void background()
stopForeground(true);
/**
* Creates a notification for placing the service into the foreground
*
* @return a notification for interacting with the service when in the foreground
*/
private Notification createNotification()
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle("Service is Active")
.setContentText("Tap to return to the Map")
.setSmallIcon(R.mipmap.ic_launcher);
Intent resultIntent = new Intent(this, MapsActivity.class);
PendingIntent resultPendingIntent =
PendingIntent.getActivity(this, 0, resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(resultPendingIntent);
return builder.build();
private void sendNotification(String title, String content)
Notification.Builder builder = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(title)
.setContentText(content);
NotificationManager manager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = new Intent(this, MapsActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
builder.setContentIntent(contentIntent);
Notification notification = builder.build();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_SOUND;
manager.notify(new Random().nextInt(), notification);
@Override
public void onConnected(@Nullable Bundle bundle)
displayLocation();
startLocationUpdate();
private void startLocationUpdate()
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
return;
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
@Override
public void onConnectionSuspended(int i)
mGoogleApiClient.connect();
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult)
@Override
public void onLocationChanged(Location location)
mLastLocation = location;
displayLocation();
interface LocationChangeListener
void onLocationChange(Location location);
private void createLocationRequest()
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FATEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
private void buildGoogleApiClient()
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API).build();
mGoogleApiClient.connect();
private void displayLocation()
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
return;
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mLastLocation != null)
final double latitude = mLastLocation.getLatitude();
final double longitude = mLastLocation.getLongitude();
geoFire.setLocation("You", new GeoLocation(latitude, longitude), new GeoFire.CompletionListener()
@Override
public void onComplete(String key, DatabaseError error)
if (mLocationChangeListener!=null)
mLocationChangeListener.onLocationChange(mLastLocation);
);
Log.d("MRF", String.format("Your last location was chaged: %f / %f", latitude, longitude));
else
Log.d("MRF", "Can not get your location.");
public void setLocationChangeListener(LocationChangeListener mLocationChangeListener)
this.mLocationChangeListener = mLocationChangeListener;
不要忘记将服务添加到清单中
<service android:name=".MapsActivity$GeoService" />
完整源码github.com/vinayakb73/GeoFence-GeoFire
【讨论】:
你能把完整的代码发给我吗......我对你的回答有点困惑。感谢您的回复。 我已经添加了完整的代码。只需将整个 MapsActivity 复制并替换到您项目的 MapsActivity 中即可。 默认 FirebaseApp 未在此过程 com.mahmud.geotesting 中初始化。确保首先调用 FirebaseApp.initializeApp(Context)。 -- 运行代码时出现此错误。 我不知道那个错误是什么。请从我的仓库下载完整的源代码然后运行它。 github.com/vinayakb73/GeoFence-GeoFire 当我去其他活动并返回 MapActivity - 它会给出另一个通知。如果在应用程序处于活动状态时进入或退出地理围栏,我想要一个通知。希望你能理解。感谢您的回复。以上是关于如何在 Geofence android 中添加后台服务的主要内容,如果未能解决你的问题,请参考以下文章
如果设备已经在Geofence android内部,则停止初始触发
Android 地理围栏在进入/退出时触发,没有错误但没有 GEOFENCE_TRANSITION_ 标志
当手机靠近受监控的位置时,Android ProximityAlert 或 Geofence 是不是会消耗更多电量?