如何在应用程序从最近的任务中被杀死后重新启动服务
How to restart service after the app is killed from recent tasks
我创建了一个服务来定期获取设备的当前位置。即使该应用程序已从最近打开的应用程序中清除,我也希望该服务在后台 运行。目前,服务 运行 仅在应用程序出现在最近打开的应用程序中之前处于后台,但当应用程序被刷掉(或以其他方式终止)时立即停止。
我已经尝试了堆栈溢出中可用的各种帮助,但我无法解决这个问题。请帮忙。这是我的服务代码。
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);
}
}
将return Service.START_NOT_STICKY;
替换为return START_STICKY;
覆盖您服务中的 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 combined with AlarmManager. Take a look at my other post 。
更新:
如果用户 "FORCE STOP" 来自“设置”的应用程序,则此解决方案不起作用(实际上不需要起作用)。这实际上很好,因为如果用户自己想停止应用程序,重新启动服务并不是一个好方法。所以,没关系。
如果您只想在服务从最近的任务中终止后重新启动服务,简单使用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
如果您使用 START_STICKY
,当您从最近的任务中终止应用程序时,您的服务将被终止(onTaskRemoved
被触发,onDestroy
NOT 已解雇)然后 它将再次自动启动(onCreate
已解雇,onStartComand
已解雇)
我使用 android 9 并且该解决方案部分适用于我。
我有一个前台服务(24/7 工作)的案例,我想在应用程序崩溃后重新启动它。
当捕获事件 uncaughtExceptionHandler
时,应用程序被冻结,而且 public void onTaskRemoved(Intent rootIntent) {
事件在最新的 Android 版本中不再工作(我想是 O+)。
如果您需要针对更多活动的解决方案,我的应用程序只有一个 activity 片段,只需使用 this link。
为了解决这个问题,我添加了一个函数来检查 activity 是否正常工作(以终止它)和一些终止进程的说明:
class MyApplication : Application() {
private var currentActivity: Activity? = null
override fun onCreate() {
super.onCreate()
StorageManager.init()
this.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStarted(activity: Activity) {
currentActivity = activity
}
override fun onActivityDestroyed(activity: Activity) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityStopped(activity: Activity) {
currentActivity = null
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
}
override fun onActivityResumed(activity: Activity) {
}
})
Thread.setDefaultUncaughtExceptionHandler { _, e ->
// Close current activity
currentActivity?.finish()
val service : PendingIntent? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Start service in Oreo and latest
PendingIntent.getForegroundService(
applicationContext,
8888,
Intent(applicationContext, SensorService::class.java),
PendingIntent.FLAG_ONE_SHOT)
} else {
// Start service in Nougat and older
PendingIntent.getService(
applicationContext,
8888,
Intent(applicationContext, MyService::class.java),
PendingIntent.FLAG_ONE_SHOT)
}
// The great solution introduced by @cgr
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service)
// Kill the current application process to avoid freezing activity
android.os.Process.killProcess(android.os.Process.myPid())
exitProcess(10)
}
}
}
添加到清单:
<application
android:name="com.example.MyApplication"
...
我创建了一个服务来定期获取设备的当前位置。即使该应用程序已从最近打开的应用程序中清除,我也希望该服务在后台 运行。目前,服务 运行 仅在应用程序出现在最近打开的应用程序中之前处于后台,但当应用程序被刷掉(或以其他方式终止)时立即停止。 我已经尝试了堆栈溢出中可用的各种帮助,但我无法解决这个问题。请帮忙。这是我的服务代码。
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);
}
}
将return Service.START_NOT_STICKY;
替换为return START_STICKY;
覆盖您服务中的 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 combined with AlarmManager. Take a look at my other post
更新:
如果用户 "FORCE STOP" 来自“设置”的应用程序,则此解决方案不起作用(实际上不需要起作用)。这实际上很好,因为如果用户自己想停止应用程序,重新启动服务并不是一个好方法。所以,没关系。
如果您只想在服务从最近的任务中终止后重新启动服务,简单使用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
如果您使用 START_STICKY
,当您从最近的任务中终止应用程序时,您的服务将被终止(onTaskRemoved
被触发,onDestroy
NOT 已解雇)然后 它将再次自动启动(onCreate
已解雇,onStartComand
已解雇)
我使用 android 9 并且该解决方案部分适用于我。
我有一个前台服务(24/7 工作)的案例,我想在应用程序崩溃后重新启动它。
当捕获事件 uncaughtExceptionHandler
时,应用程序被冻结,而且 public void onTaskRemoved(Intent rootIntent) {
事件在最新的 Android 版本中不再工作(我想是 O+)。
如果您需要针对更多活动的解决方案,我的应用程序只有一个 activity 片段,只需使用 this link。
为了解决这个问题,我添加了一个函数来检查 activity 是否正常工作(以终止它)和一些终止进程的说明:
class MyApplication : Application() {
private var currentActivity: Activity? = null
override fun onCreate() {
super.onCreate()
StorageManager.init()
this.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStarted(activity: Activity) {
currentActivity = activity
}
override fun onActivityDestroyed(activity: Activity) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityStopped(activity: Activity) {
currentActivity = null
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
}
override fun onActivityResumed(activity: Activity) {
}
})
Thread.setDefaultUncaughtExceptionHandler { _, e ->
// Close current activity
currentActivity?.finish()
val service : PendingIntent? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Start service in Oreo and latest
PendingIntent.getForegroundService(
applicationContext,
8888,
Intent(applicationContext, SensorService::class.java),
PendingIntent.FLAG_ONE_SHOT)
} else {
// Start service in Nougat and older
PendingIntent.getService(
applicationContext,
8888,
Intent(applicationContext, MyService::class.java),
PendingIntent.FLAG_ONE_SHOT)
}
// The great solution introduced by @cgr
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service)
// Kill the current application process to avoid freezing activity
android.os.Process.killProcess(android.os.Process.myPid())
exitProcess(10)
}
}
}
添加到清单:
<application
android:name="com.example.MyApplication"
...