Utrzymanie usługi lokalizacji przy życiu, gdy aplikacja jest zamknięta

Mam usługę, która wysyła powiadomienie, gdy użytkownik zmieni swoją lokalizację. Ta usługa działa dobrze, ale problem pojawia się, gdy użytkownik zamyka aplikację, gdy usługa również się zamyka.

W Jaki Sposób Mogę sprawić, że usługa będzie nadal żywa, mimo że aplikacja została zamknięta?

Moje usługi to:

public class LocationService extends Service implements LocationListener {
    public final static int MINUTE = 1000 * 60;


    boolean isGPSEnabled = false;
    boolean isNetworkEnabled = false;
    boolean canGetLocation = false;

    Location location; // location
    double latitude = 0; // latitude
    double longitude = 0; // longitude
    String provider;

    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10;

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1 * MINUTE;

    // Declaring a Location Manager
    protected LocationManager locationManager;



    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();

    /**
     * Class used for the client Binder. Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        public LocationService getService() {
            // Return this instance of LocalService so clients can call public
            // methods
            return LocationService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    public Location getLocation() {
        try {
            locationManager = (LocationManager) getBaseContext().getSystemService(LOCATION_SERVICE);

            // getting GPS status
            isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

            // getting network status
            isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            if (!isGPSEnabled && !isNetworkEnabled) {
                // no network provider is enabled. DEFAULT COORDINATES


            } else {
                this.canGetLocation = true;
                if (isNetworkEnabled) {
                    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES,
                            this);
                    Log.d("Network", "Network Enabled");
                    if (locationManager != null) {
                        location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        if (location != null) {
                            latitude = location.getLatitude();
                            longitude = location.getLongitude();
                        }
                    }
                }
                // if GPS Enabled get lat/long using GPS Services
                if (isGPSEnabled) {
                    if (location == null) {
                        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES,
                                this);
                        Log.d("GPS", "GPS Enabled");
                        if (locationManager != null) {
                            location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                            if (location != null) {
                                latitude = location.getLatitude();
                                longitude = location.getLongitude();
                            }
                        }
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.i("LOCATION", "Latitude: " + latitude + "- Longitude: " + longitude);


        return location;
    }

    @Override
    public void onLocationChanged(Location arg0) {

        NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
        Intent intent = null;

        intent = new Intent(this, CompleteSurveyActivity.class);

        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_launcher).setAutoCancel(true)
                .setContentIntent(contentIntent).setContentTitle(this.getString(R.string.app_name)).setContentText("text");

        // mBuilder.setContentIntent(contentIntent);
        mNotificationManager.notify((int) System.currentTimeMillis() % Integer.MAX_VALUE, mBuilder.build());

        double longitude = location.getLongitude();
        double latitude = location.getLatitude();

        Log.i("LOCATION", "Latitude: " + latitude + "- Longitude: " + longitude);

    }

    @Override
    public void onProviderDisabled(String arg0) {
    }

    @Override
    public void onProviderEnabled(String arg0) {
    }

    @Override
    public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
    }

}

Zadzwoniłem stąd:

public class MyActivity extends Activity {

    LocationService mService;
    boolean mBound = false;


    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            // We've bound to LocalService, cast the IBinder and get
            // LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;

        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_activity);

        exampleButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                initService();
            }
        });

    }

    public void initService() {
        if (mBound)
            mService.getLocation();
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, LocationService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

Manifest.xml

  <service android:name=".LocationService" android:enabled="true"></service>
Author: SolArabehety, 2014-01-29

2 answers

W przeciwieństwie do tego, co powiedział @ sven-menschner, myślę, że unbound Service jest dokładnie tym, czego potrzebujesz, ponieważ usługi związane podlegają mechanizmom bind/unbind, które zabiłyby twoją usługę. Ja bym tak zrobił:

W pliku manifestu zdefiniuj swoją usługę:

<service
  android:name=".YourService"
  android:enabled="true"
  android:exported="true"
  android:description="@string/my_service_desc"
  android:label="@string/my_infinite_service">
  <intent-filter>
    <action android:name="com.yourproject.name.LONGRUNSERVICE" />
  </intent-filter>
</service>

Uwaga: Istnieje lista już zaimplementowanych akcji, ale możesz zdefiniować własne akcje dla zamiaru uruchomienia usługi. Wystarczy utworzyć klasę singleton i zdefiniować ciągi przypisujące im To powinno być wyjątkowe. Ustawienie" enabled " na true służy tylko do tworzenia instancji usługi, a wyeksportowane ustawienie na true jest tylko w przypadku, gdy potrzebujesz innych aplikacji wysyłających intencje do Service. Jeśli nie, możesz bezpiecznie ustawić to ostatnie na wartość false.

Następnym krokiem będzie uruchomienie usługi od aktywności. Można to łatwo zrobić przez:

public class MainActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Intent servIntent = new Intent("com.yourproject.name.LONGRUNSERVICE");
    startService(servIntent);

    ...
  }
}

Ostatnim krokiem jest zdefiniowanie inicjalizacji Service. Miej oko na metodę onBind(). Skoro nie chcesz, żeby była związana, po prostu return null. To byłoby coś takiego:

public class MyService extends Service {
  @Override
  public IBinder onBind(Intent intent) {
    // This won't be a bound service, so simply return null
    return null;
  }

  @Override
  public void onCreate() {
    // This will be called when your Service is created for the first time
    // Just do any operations you need in this method.
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    return super.onStartCommand(intent, flags, startId);
  }
}

Teraz Twoja usługa będzie działać, nawet jeśli zamkniesz swoją główną Activity. Został tylko jeden krok: aby pomóc Service nie być skończonym, uruchom go jako usługę pierwszoplanową (zrób to w swoim serwisie). Spowoduje to utworzenie ikony powiadomienia na pasku stanu. Nie oznacza to, że Twoja główna aktywność również działa (dlatego nie chcesz powiązanej usługi), ponieważ aktywności i usługi mają różne cykle życia. W celu pomóż tej usłudze działać tak długo, spróbuj utrzymać stertę tak nisko, jak to możliwe, aby uniknąć Androida, który go zabija.

Jeszcze jedno uznanie: nie można sprawdzić, czy usługa nadal działa, zabijając DVM. Jeśli zabijesz DVM, zabijesz wszystko, a więc i serwis.

 8
Author: nKn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-10 21:40:03

Istnieją dwa rodzaje usług Android: uruchomiony i związany. Szukasz tego drugiego. Dokumentacja pokazuje, jak z niej korzystać, Poniżej znajduje się ładny schemat cyklu życia.

Zamiast uruchamiać i wiązać usługę w jednym kroku za pomocą bindService() musisz najpierw wywołać startService(). Następnie działa, dopóki go nie zatrzymasz, nawet jeśli aplikacja jest zamknięta. Ale nie zapomnij zatrzymać usługi w odpowiednim czasie, aby uniknąć problemów z pamięcią itp.

HTH

 2
Author: Sven Menschner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-03 12:19:18