8

Utworzyłem usługę, aby pobierać bieżące położenie urządzenia w okresowych odstępach czasu. Chcę, aby usługa działała w tle, nawet jeśli aplikacja została wyczyszczona z ostatnio otwieranych aplikacji. Obecnie usługa działa tylko w tle, dopóki aplikacja nie pojawi się w ostatnio otwieranych aplikacjach, ale zatrzyma się natychmiast po usunięciu aplikacji (lub zabiciu jej w inny sposób). Próbowałem wszelkiej pomocy dostępnej w przepełnieniu stosu, ale nie jestem w stanie tego rozwiązać. Proszę pomóż. Oto mój kod do usługi.Jak ponownie uruchomić usługę po zabiciu aplikacji z ostatnich zadań

package com.packr.services; 

import android.app.AlarmManager; 
import android.app.PendingIntent; 
import android.app.Service; 
import android.content.Context; 
import android.content.Intent; 
import android.content.pm.ServiceInfo; 
import android.location.Location; 
import android.os.Bundle; 
import android.os.IBinder; 
import android.os.SystemClock; 
import android.support.annotation.Nullable; 
import android.util.Log; 
import android.widget.Toast; 

import com.google.android.gms.common.ConnectionResult; 
import com.google.android.gms.common.api.GoogleApiClient; 
import com.google.android.gms.location.LocationListener; 
import com.google.android.gms.location.LocationRequest; 
import com.google.android.gms.location.LocationServices; 

import java.text.DateFormat; 
import java.util.Date; 

/** 
* Created by Arindam on 11-Dec-15. 
*/ 
public class LocationService extends Service implements 
     GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { 
    protected static final String TAG = "packrMATE"; 
    /** 
    * The desired interval for location updates. Inexact. Updates may be more or less frequent. 
    */ 
    public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000; 

    /** 
    * The fastest rate for active location updates. Exact. Updates will never be more frequent 
    * than this value. 
    */ 
    public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = 
      UPDATE_INTERVAL_IN_MILLISECONDS/2; 

    // Keys for storing activity state in the Bundle. 
    protected final static String REQUESTING_LOCATION_UPDATES_KEY = "requesting-location-updates-key"; 
    protected final static String LOCATION_KEY = "location-key"; 
    protected final static String LAST_UPDATED_TIME_STRING_KEY = "last-updated-time-string-key"; 

    /** 
    * Provides the entry point to Google Play services. 
    */ 
    protected GoogleApiClient mGoogleApiClient; 

    /** 
    * Stores parameters for requests to the FusedLocationProviderApi. 
    */ 
    protected LocationRequest mLocationRequest; 

    /** 
    * Represents a geographical location. 
    */ 
    protected Location mCurrentLocation; 

    /** 
    * Tracks the status of the location updates request. Value changes when the user presses the 
    * Start Updates and Stop Updates buttons. 
    */ 
    protected Boolean mRequestingLocationUpdates; 

    /** 
    * Time when the location was updated represented as a String. 
    */ 
    protected String mLastUpdateTime; 

    @Override 
    public void onCreate() { 
     Log.d(TAG,"Service started"); 
     super.onCreate(); 
     mRequestingLocationUpdates = false; 
     mLastUpdateTime = ""; 

     // Kick off the process of building a GoogleApiClient and requesting the LocationServices 
     // API. 
     buildGoogleApiClient(); 
    } 

    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
     Log.d(TAG,"Service fucking started"); 
     mGoogleApiClient.connect(); 
     if (mGoogleApiClient.isConnected()) { 
      startLocationUpdates(); 
     } 
     return Service.START_STICKY; 

    } 

    @Override 
    public void onDestroy() { 
     mGoogleApiClient.disconnect(); 
     super.onDestroy(); 
    } 

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

    @Override 
    public void onConnected(Bundle bundle) { 
     Log.i(TAG, "Connected to GoogleApiClient"); 

     // If the initial location was never previously requested, we use 
     // FusedLocationApi.getLastLocation() to get it. If it was previously requested, we store 
     // its value in the Bundle and check for it in onCreate(). We 
     // do not request it again unless the user specifically requests location updates by pressing 
     // the Start Updates button. 
     // 
     // Because we cache the value of the initial location in the Bundle, it means that if the 
     // user launches the activity, 
     // moves to a new location, and then changes the device orientation, the original location 
     // is displayed as the activity is re-created. 
     if (mCurrentLocation == null) { 
      mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); 
      mLastUpdateTime = DateFormat.getTimeInstance().format(new Date()); 
      Toast.makeText(getApplicationContext(),"Hello Babe",Toast.LENGTH_SHORT).show(); 
     } 

     // If the user presses the Start Updates button before GoogleApiClient connects, we set 
     // mRequestingLocationUpdates to true (see startUpdatesButtonHandler()). Here, we check 
     // the value of mRequestingLocationUpdates and if it is true, we start location updates. 
      startLocationUpdates(); 
    } 

    @Override 
    public void onConnectionSuspended(int i) { 
     // The connection to Google Play services was lost for some reason. We call connect() to 
     // attempt to re-establish the connection. 
     Log.i(TAG, "Connection suspended"); 
     mGoogleApiClient.connect(); 
    } 

    @Override 
    public void onLocationChanged(Location location) { 
     mCurrentLocation = location; 
     mLastUpdateTime = DateFormat.getTimeInstance().format(new Date()); 
     Toast.makeText(this, String.valueOf(location.getLatitude() + " "+ String.valueOf(location.getLongitude())), 
       Toast.LENGTH_SHORT).show(); 
     Log.e(TAG,"fuck man location found"); 
    } 

    @Override 
    public void onConnectionFailed(ConnectionResult connectionResult) { 
     // Refer to the javadoc for ConnectionResult to see what error codes might be returned in 
     // onConnectionFailed. 
     Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + connectionResult.getErrorCode()); 
    } 

    protected synchronized void buildGoogleApiClient() { 
     Log.i(TAG, "Building GoogleApiClient"); 
     mGoogleApiClient = new GoogleApiClient.Builder(this) 
       .addConnectionCallbacks(this) 
       .addOnConnectionFailedListener(this) 
       .addApi(LocationServices.API) 
       .build(); 
     createLocationRequest(); 

    } 
    /** 
    * Sets up the location request. Android has two location request settings: 
    * {@code ACCESS_COARSE_LOCATION} and {@code ACCESS_FINE_LOCATION}. These settings control 
    * the accuracy of the current location. This sample uses ACCESS_FINE_LOCATION, as defined in 
    * the AndroidManifest.xml. 
    * <p/> 
    * When the ACCESS_FINE_LOCATION setting is specified, combined with a fast update 
    * interval (5 seconds), the Fused Location Provider API returns location updates that are 
    * accurate to within a few feet. 
    * <p/> 
    * These settings are appropriate for mapping applications that show real-time location 
    * updates. 
    */ 
    protected void createLocationRequest() { 
     mLocationRequest = new LocationRequest(); 

     // Sets the desired interval for active location updates. This interval is 
     // inexact. You may not receive updates at all if no location sources are available, or 
     // you may receive them slower than requested. You may also receive updates faster than 
     // requested if other applications are requesting location at a faster interval. 
     mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS); 

     // Sets the fastest rate for active location updates. This interval is exact, and your 
     // application will never receive updates faster than this value. 
     mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS); 

     mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); 
    } 

    /** 
    * Requests location updates from the FusedLocationApi. 
    */ 
    protected void startLocationUpdates() { 
     // The final argument to {@code requestLocationUpdates()} is a LocationListener 
     // (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html). 
     LocationServices.FusedLocationApi.requestLocationUpdates(
       mGoogleApiClient, mLocationRequest, this); 
    } 
    /** 
    * Removes location updates from the FusedLocationApi. 
    */ 
    protected void stopLocationUpdates() { 
     // It is a good practice to remove location requests when the activity is in a paused or 
     // stopped state. Doing so helps battery performance and is especially 
     // recommended in applications that request frequent location updates. 

     // The final argument to {@code requestLocationUpdates()} is a LocationListener 
     // (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html). 
     LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this); 
    } 
    @Override 
    public void onTaskRemoved(Intent rootIntent) { 
     Log.e("FLAGX : ", ServiceInfo.FLAG_STOP_WITH_TASK + ""); 
     Intent restartServiceIntent = new Intent(getApplicationContext(), 
       this.getClass()); 
     restartServiceIntent.setPackage(getPackageName()); 

     PendingIntent restartServicePendingIntent = PendingIntent.getService(
       getApplicationContext(), 1, restartServiceIntent, 
       PendingIntent.FLAG_ONE_SHOT); 
     AlarmManager alarmService = (AlarmManager) getApplicationContext() 
       .getSystemService(Context.ALARM_SERVICE); 
     alarmService.set(AlarmManager.ELAPSED_REALTIME, 
       SystemClock.elapsedRealtime() + 1000, 
       restartServicePendingIntent); 

     super.onTaskRemoved(rootIntent); 
    } 
} 

Odpowiedz

12

Override onTaskRemoved() w menedżerze usług i wykorzystanie alarmu aby ponownie uruchomić usługę. Poniżej jest kod z naszej aplikacji, która robi to samo i działa prawidłowo:

@Override 
public void onTaskRemoved(Intent rootIntent) { 
    super.onTaskRemoved(rootIntent); 

    Log.d(TAG, "TASK REMOVED"); 

    PendingIntent service = PendingIntent.getService(
      getApplicationContext(), 
      1001, 
      new Intent(getApplicationContext(), MyService.class), 
      PendingIntent.FLAG_ONE_SHOT); 

    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 
    alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service); 
} 

Jak chcesz wysłać lokalizację okresowo nawet w przypadku, gdy usługa zostanie zabity na małej ilości pamięci (lub z jakiegokolwiek powodu), sugeruję aby obsłużyć wyjątek uncaughtException, aby ponownie uruchomić go po N sekundach. W ten sposób zrobiliśmy w naszej aplikacji, która działa perfekcyjnie:

private Thread.UncaughtExceptionHandler defaultUEH; 
private Thread.UncaughtExceptionHandler uncaughtExceptionHandler = new Thread.UncaughtExceptionHandler() { 

    @Override 
    public void uncaughtException(Thread thread, Throwable ex) { 
     Log.d(TAG, "Uncaught exception start!"); 
     ex.printStackTrace(); 

     //Same as done in onTaskRemoved() 
     PendingIntent service = PendingIntent.getService(
       getApplicationContext(), 
       1001, 
       new Intent(getApplicationContext(), MyService.class), 
       PendingIntent.FLAG_ONE_SHOT); 

     AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 
     alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service); 
     System.exit(2); 
     } 
    }; 

Uwaga: myślę i pamiętam, że zweryfikowano go na KitKat że START_STICKY nie działa na KitKat i wyższych poziomów API. Sprawdź to dla siebie.

WIĘCEJ:
Jak zrobić loc okresowe wysyłanie, może trzeba rozważyć tryb głębokiego uśpienia. Aby wszystko działało w głębokim śnie, użyj WakefulBroadcastReceiver w połączeniu z AlarmManager. Spójrz na mój drugi wpis How to use http in deep sleep mode.

UPDATE:
To rozwiązanie nie działa (w rzeczywistości nie musi pracować) jeżeli użytkownik „stop force” w ustawieniach aplikacji. Jest to dobre, ponieważ ponowne uruchomienie usługi nie jest dobrym rozwiązaniem, jeśli użytkownik sam chce zatrzymać aplikację. Jest więc w porządku.

+0

Awesome man. Wielkie dzięki. –

+0

Dzięki Dawn, masz ładne kodowanie;) – cgr

+1

Dodałem WIĘCEJ do odpowiedzi, która dotyczy trybu głębokiego snu. Sprawdź, czy musisz się tym zająć. – cgr

0

zastąpić return Service.START_NOT_STICKY; z return START_STICKY;

+0

to była literówka fro m mój koniec. Ale to też nie działa. Zasadniczo wypróbowałem tylko START_STICKY. –

0

Jeśli TYLKO chce ponownie uruchomić usługę po jej zabić od ostatniego zadania, prostego stosowania

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

Jeśli używasz START_STICKY, po zabiciu aplikacji z ostatniego zadania, usługa zostanie zabity (onTaskRemoved zwolniony , onDestroynIE zwolniony) WTEDY rozpocznie auto ponownie (onCreate zwolniony, onStartComand zwolniony)

Powiązane problemy