I have foreground service with notification that tracks user location, but in the Crashlytics I see that the app crashes for some users with Android 14. I can not reproduce it.
Fatal Exception: android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{2170719 u0 com.anstar.fieldworkhq/.coordinates.ServiceTechnicianTrackingService}
at android.app.ActivityThread.generateForegroundServiceDidNotStartInTimeException(ActivityThread.java:2315)
at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:2286)
Caused by android.app.StackTrace: Last startServiceCommon() call for this service was made here
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:2023)
at android.app.ContextImpl.startForegroundService(ContextImpl.java:1967)
at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:847)
at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:847)
at com.zoran.app.main.MainActivity$$ExternalSyntheticApiModelOutline0.m(D8$$SyntheticClass)
at com.zoran.app.main.MainActivity.startForegroundService(MainActivity.java:326)
at com.zoran.app.main.MainActivity.tryToStartForegroundLocationService(MainActivity.java:313)
at com.zoran.app.main.MainActivity.trackLocationIfNeeded(MainActivity.java:220)
at com.zoran.app.main.MainActivity.onCreate(MainActivity.java:95)
at android.app.Activity.performCreate(Activity.java:8975)
at android.app.Activity.performCreate(Activity.java:8944)
As you can see the crash happens in the MainActivity, when I tried to star foreground service with this code:
private void trackLocationIfNeeded() {
if (presenter.isTrackable()) {
if (ViewUtil.isPlayServiceAvailable(this)) {
if (isForegroundLocationPermissionGranted() && presenter.isTrackTechnicianRouteAllowed()) {
tryToStartForegroundLocationService();
presenter.saveThatUserAskedForPermission();
} else {
if (!presenter.isUserAskedForLocationPermission()) {
askForLocationPermission();
presenter.saveThatUserAskedForPermission();
}
}
}
} else {
stopServiceTechnicianLocationService();
}
}
private void tryToStartForegroundLocationService() {
if(Utils.isPlayServiceAvailable(this)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if(isPostNotificationsPermissionGranted()) {
startForegroundService();
} else {
askForPostNotificationsPermission();
}
} else {
startForegroundService();
}
}
}
private void startForegroundService() {
Intent i = new Intent(this, ServiceTechnicianTrackingService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(i);
} else {
startService(i);
}
}
startForegroundService(i); is the line 326.
This is my foreground location tracking service with startCommand where I try at the beginning to start service with a notification.
public class ServiceTechnicianTrackingService extends Service {
private FusedLocationProviderClient fusedLocationProviderClient;
private LocationCallback locationCallback;
@Inject ServiceTechnicianLocationManager serviceTechnicianCoordinateManager;
@Inject NetworkManager NetworkManager;
public static final String ACTION_STOP_SERVICE = "action_stop_service";
private boolean isStarted = false;
@Nullable @Override public IBinder onBind(Intent intent) {
return null;
}
@Override public void onCreate() {
super.onCreate();
injector().inject(this);
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
locationCallback = new LocationCallback() {
@Override public void onLocationResult(@NotNull LocationResult locationResult) {
if(locationResult != null && locationResult.getLastLocation() != null) {
super.onLocationResult(locationResult);
String lat = String.valueOf(locationResult.getLastLocation().getLatitude());
String lng = String.valueOf(locationResult.getLastLocation().getLongitude());
Timber.d("Lat is: " + lat + " lng is: " + lng);
if(networkManager.isNetworkAvailable()) {
serviceTechnicianCoordinateManager.updateCurrentLocation(new Coordinate(lat, lng));
}
}
}
};
}
@Override public int onStartCommand(Intent intent, int flags, int startId) {
// Call startForeground early to prevent the exception
if(!isStarted) {
startForegroundAndDisplayNotification();
isStarted = true;
}
if(intent != null) {
String action = intent.getAction();
if(ACTION_STOP_SERVICE.equalsIgnoreCase(action)) {
stopForeground(true);
stopSelf();
} else {
requestLocationUpdates();
}
}
return START_STICKY;
}
@Override public void onDestroy() {
super.onDestroy();
stopLocationUpdates();
isStarted = false;
}
private void startForegroundAndDisplayNotification() {
if (Build.VERSION.SDK_INT >= 26) {
try {
Notification notification = createNotification();
startForeground(1, notification);
} catch (Exception e) {
Timber.e(e, "Exception in startForegroundAndDisplayNotification");
}
}
}
In AndroidManifest file I have this:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<service
android:name=".coordinates.ServiceTechnicianTrackingService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="location" />