Search code examples
javaandroidandroid-serviceandroid-locationandroid-service-binding

Android: Is there any Android service that can execute when the app is closed and also acts like a bound service(constant interaction)


I want to have a location updater service and I need to query it from the MainActivity. I need a service cause I need it to run on the background when the app is closed in order to send the location to a server and send notifications based on that. Also I've read that bound services act like a server and the activity like a client, and ther's no need to start the service which is ideal for the task this service has to do. Which type of service could fulfil my needs?. Thank you very much


Solution

  • START_STICKY tells the OS to recreate the service after it has enough memory and call onStartCommand() again with a null intent. START_NOT_STICKY tells the OS to not bother recreating the service again.

    you can use a service to fired when app is closed or kill from the task:

    public class App_killed extends Service {
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("ClearFromRecentService", "Service Started");
        return START_STICKY;
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("ClearFromRecentService", "Service Destroyed");
    }
    
    public void onTaskRemoved(Intent rootIntent) {
        Log.e("ClearFromRecentService", "END");
        //here you can call a background network request to post you location to server when app is killed
        Toast.makeText(getApplicationContext(), "Warning: App killed", Toast.LENGTH_LONG).show();
        stopSelf(); //call this method to stop the service
    }
    }
    

    delcare your service on menifest

     <service
            android:name="com.empiregroup.amarridebiker.App_killed"
            android:stopWithTask="false" />
    

    this service will keep executing even if the app gets closed...also you can use a Binder class to get instance of the service class in any activity..

    Full source code: works in my case

    public class GPSTracker extends Service {
    
    private static Context mContext;
    // flag for GPS status
    boolean isGPSEnabled = false;
    // flag for network status
    boolean isNetworkEnabled = false;
    // flag for GPS status
    boolean canGetLocation = false;
    protected LocationManager locationManager;
    static String latitude_s, longitude_s;
    Thread triggerService;
    private final IBinder mBinder = new MyBinder();
    
    public GPSTracker() {
        super();
    }
    
    
    public GPSTracker(Context context) {
        mContext = context;
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
        Log.i(LOG, "Service started");
        if (intent != null) {
            addLocationListener();
         }
        return START_STICKY;
    }
    
    private void addLocationListener() {
        triggerService = new Thread(new Runnable() {
            public void run() {
                try {
                    Looper.prepare();//Initialise the current thread as a looper.
                    locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
    
                    //Criteria c = new Criteria();
                    //c.setAccuracy(Criteria.ACCURACY_COARSE);
    
                    //final String PROVIDER = locationManager.getBestProvider(c, true);
    
                    MyLocationListener myLocationListener = new MyLocationListener();
                    if (checkLocationPermission()) {
                        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000, 0, myLocationListener);
                    }
                    Log.d("LOC_SERVICE", "Service RUNNING!");
                    Looper.loop();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }, "LocationThread");
        triggerService.start();
    }
    
    public static void updateLocation(Location location) {
        Context appCtx = Global_variable.getAppContext();
    
        double latitude, longitude;
    
        latitude = location.getLatitude();
        longitude = location.getLongitude();
        try {
            latitude_s = Double.toString(latitude);
            longitude_s = Double.toString(longitude);
        } catch (Exception e) {
    
        }
        Intent filterRes = new Intent();
        filterRes.setAction("mypackage.action.LOCATION");
        filterRes.putExtra("latitude", latitude);
        filterRes.putExtra("longitude", longitude);
        filterRes.putExtra("id", id);
        appCtx.sendBroadcast(filterRes);
    }
    
    
    class MyLocationListener implements LocationListener {
    
        @Override
        public void onLocationChanged(Location location) {
            updateLocation(location);
        }
    
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
    
        }
    
        @Override
        public void onProviderEnabled(String provider) {
    
        }
    
        @Override
        public void onProviderDisabled(String provider) {
    
        }
    }
    
    
    public boolean checkLocationPermission() {
        String permission = "android.permission.ACCESS_FINE_LOCATION";
        int res = mContext.checkCallingOrSelfPermission(permission);
        return (res == PackageManager.PERMISSION_GRANTED);
    }
    
    
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(LOG, "Service created");
    }
    
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(LOG, "Service destroyed");
    }
    
    
    /**
     * Function to check GPS/wifi enabled
     *
     * @return boolean
     */
    public boolean canGetLocation() {
        locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
        // getting GPS status
        isGPSEnabled = locationManager
                .isProviderEnabled(LocationManager.GPS_PROVIDER);
    
        // getting network status
        isNetworkEnabled = locationManager
                .isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    
        if (!isGPSEnabled && !isNetworkEnabled) {
            // location service disabled
            canGetLocation = false;
        } else {
            canGetLocation = true;
        }
        return canGetLocation;
    }
    
    /**
     * Function to show settings alert dialog
     * On pressing Settings button will lauch Settings Options
     */
    public void showSettingsAlert() {
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
        // Setting Dialog Title
        alertDialog.setTitle("GPS is settings");
        // Setting Dialog Message
        alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");
        // On pressing Settings button
        alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                mContext.startActivity(intent);
            }
        });
    
        // on pressing cancel button
        alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });
    
        // Showing Alert Message
        alertDialog.show();
    }
    
    @Override
    public IBinder onBind(Intent arg0) {
        return mBinder;
    }
    
    public class MyBinder extends Binder {
        GPSTracker getService() {
            return GPSTracker.this;
        }
    }
    
    public String getLatitude_s() {
        return latitude_s;
    }
    
    public String getLongitude_s() {
        return longitude_s;
    }
    

    }

    The broadcast receiver class for getting location from service and send it to server.

    public class LocationReceiver extends BroadcastReceiver {
    
    double latitude, longitude;
    
    @Override
    public void onReceive(final Context context, final Intent calledIntent)
    {
        Log.d("LOC_RECEIVER", "Location RECEIVED!");
    
        latitude = calledIntent.getDoubleExtra("latitude", -1);
        longitude = calledIntent.getDoubleExtra("longitude", -1);
    
        updateRemote(latitude, longitude,biker_id);
    
    }
    
    private void updateRemote(final double latitude, final double longitude ,final String id)
    {
        //HERE YOU CAN PUT YOUR ASYNCTASK TO UPDATE THE LOCATION ON YOUR SERVER
        String latitude_s=Double.toString(latitude);
        String longitude_s=Double.toString(longitude);
        new SendToServer().execute(longitude_s, latitude_s,id);
    }
    

    in Manifest file add the services and receiver

        <service
            android:name="com.yourpackage.GPSTracker"
            android:enabled="true"
            android:label="GPS Data"></service>
    
        <service
            android:name="com.yourpackage.App_killed"
            android:stopWithTask="false" />
    
        <receiver
            android:name="com.yourpackage.LocationReceiver"
            android:enabled="true">
            <intent-filter>
                <action android:name="mypackage.action.LOCATION" />
            </intent-filter>
        </receiver>
    

    start the service in an activity:

      GPSTracker gps;
      gps = new GPSTracker(this);
            // check if GPS enabled
            if (gps.canGetLocation()) {
                startService(new Intent(getApplicationContext(), GPSTracker.class));
                } else {
                gps.showSettingsAlert();
            }