我已经创建了一个服务来定期获取设备的当前位置.即使从最近打开的应用程序中清除了应用程序,我也希望该服务在后台运行.目前,该服务仅在后台运行,直到应用程序出现在最近打开的应用程序中,但在应用程序被刷掉(或以其他方式杀死)时立即停止.我已经尝试过堆栈溢出中的各种帮助,但我无法解决这个问题.请帮忙.这是我的服务代码.
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. * * 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. * * 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); } }
cgr.. 25
覆盖服务中的onTaskRemoved()并使用警报管理器再次启动服务.以下是我们的应用程序的代码,它做同样的工作正常:
@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); }
因为你可能想要定期发送位置,即使服务在低内存(或任何原因)被杀死的情况下,我建议你处理uncaughtException以在N秒后重新启动它.这就是我们在完美运行的应用程序中所做的工作:
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); } };
注意:我认为我记得我在Kitkat上验证过,START_STICKY不适用于Kitkat和更高的API级别.请自行验证.
更多:
当您定期发送loc时,您可能必须考虑深度睡眠模式.要在深度睡眠中工作,请使用WakefulBroadcastReceiver与AlarmManager结合使用.看一下我的另一篇文章如何在深度睡眠模式下使用http.
更新:
如果用户从"设置"中"强制停止"应用程序,则此解决方案不起作用(实际上不需要工作).事实上这很好,因为如果用户自己想要停止应用程序,重新启动服务并不是一个好方法.所以,没关系.
覆盖服务中的onTaskRemoved()并使用警报管理器再次启动服务.以下是我们的应用程序的代码,它做同样的工作正常:
@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); }
因为你可能想要定期发送位置,即使服务在低内存(或任何原因)被杀死的情况下,我建议你处理uncaughtException以在N秒后重新启动它.这就是我们在完美运行的应用程序中所做的工作:
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); } };
注意:我认为我记得我在Kitkat上验证过,START_STICKY不适用于Kitkat和更高的API级别.请自行验证.
更多:
当您定期发送loc时,您可能必须考虑深度睡眠模式.要在深度睡眠中工作,请使用WakefulBroadcastReceiver与AlarmManager结合使用.看一下我的另一篇文章如何在深度睡眠模式下使用http.
更新:
如果用户从"设置"中"强制停止"应用程序,则此解决方案不起作用(实际上不需要工作).事实上这很好,因为如果用户自己想要停止应用程序,重新启动服务并不是一个好方法.所以,没关系.