How to add background service in Geofence android

后端 未结 1 1233
挽巷
挽巷 2021-02-05 17:21

I am trying to create a geofence with background service for monitor. The geofence create successfully and work when apps Activity is open but when close the app geofence not wo

1条回答
  •  星月不相逢
    2021-02-05 17:40

    From Android Oreo you can't create a long running service as background service. So you have to create a foreground service which the service must bind to the current Activity

    Another problem is the location update. When you using geoQuery inside a service you have to update location also in the service. When a location update trigger you must pass this update to activity then only the UI can update. In my solution I am using an interface to update the UI. You can also use broadcast listeners.

    When you create service and bind it to activity, then you can use Geofence inside service

     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);
                }
            });
        }
    

    You can call this method from your activity as

      geoService.startService(dangerous_area,2000);
    

    This will work even if your app is destroyed.

    The complete code is as follows

    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;
            }
        }
    
    }
    

    Don't forget to add service into manifest

      
    

    Full source code github.com/vinayakb73/GeoFence-GeoFire

    0 讨论(0)
提交回复
热议问题