You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 14下ForegroundServiceDidNotStartInTimeException崩溃求助

前台定位服务Android 14崩溃排查与解决

问题背景

开发了带通知的前台定位服务用于追踪用户位置,部分Android 14用户出现崩溃,无法复现该问题,Crashlytics捕获到ForegroundServiceDidNotStartInTimeException异常。

崩溃日志

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)

相关启动逻辑代码

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);
}
}

前台服务代码(ServiceTechnicianTrackingService)

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");
  }
}
}

AndroidManifest配置

<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" />

崩溃原因分析

  1. Android 14超时限制收紧:Android 14要求调用startForegroundService()后必须在5秒内调用startForeground(),超时就会触发该异常,相比旧版本时间要求更严格。
  2. 服务启动流程延迟:onCreate()中执行依赖注入、初始化FusedLocationProviderClient等操作,若这些步骤耗时,会挤占onStartCommand()中调用startForeground()的时间窗口。
  3. 通知创建潜在问题:createNotification()可能存在未捕获的异常或耗时操作(如创建通知渠道、加载资源慢),导致startForeground()未被成功执行;即使有try-catch,异常后服务仍处于未启动前台的非法状态。
  4. 重复启动隐患:若服务已启动,再次调用startForegroundService()会触发onStartCommand(),但此时isStarted为true,不会调用startForeground(),Android 14系统仍可能判定未及时启动前台而抛出异常。

解决方案

1. 前置前台启动,减少初始化延迟

将startForeground()调用移到onCreate()最开头,优先完成前台启动,再执行其他初始化操作:

@Override public void onCreate() {
super.onCreate();
// 优先启动前台,抢占时间窗口
startForegroundAndDisplayNotification();
isStarted = true;

// 后续初始化操作
injector().inject(this);
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
locationCallback = new LocationCallback() {
  // 原有逻辑
};
}

@Override public int onStartCommand(Intent intent, int flags, int startId) {
// 移除isStarted判断,前台已在onCreate中启动
if(intent != null) {
  String action = intent.getAction();
  if(ACTION_STOP_SERVICE.equalsIgnoreCase(action)) {
    stopForeground(true);
    stopSelf();
  } else {
    requestLocationUpdates();
  }
}
return START_STICKY;
}

2. 优化通知创建逻辑

  • 在Application类中提前创建通知渠道,避免服务启动时创建渠道的延迟。
  • 简化通知布局,避免加载大量资源;增加通知非空判断,创建失败时主动停止服务:
private void startForegroundAndDisplayNotification() {
if (Build.VERSION.SDK_INT >= 26) {
  Notification notification = createNotification();
  if(notification == null) {
    stopSelf();
    return;
  }
  try {
    startForeground(1, notification);
  } catch (Exception e) {
    Timber.e(e, "Failed to start foreground");
    stopSelf();
  }
}
}

3. 避免重复启动服务

增加服务运行状态检查,防止重复调用startForegroundService():

private boolean isServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
    if (ServiceTechnicianTrackingService.class.getName().equals(service.service.getClassName())) {
        return true;
    }
}
return false;
}

private void startForegroundService() {
if(isServiceRunning()) {
  return;
}
Intent i = new Intent(this, ServiceTechnicianTrackingService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  startForegroundService(i);
} else {
  startService(i);
}
}

4. Android 14权限适配

确保POST_NOTIFICATIONS权限申请流程无漏洞,Android 14中无该权限时startForeground()会直接失败,进而触发超时异常。

内容的提问来源于stack exchange,提问作者Zookey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 11:17:15