我的用户报告了数百次崩溃,但仍然无法找到解决方法 . 这些崩溃来自Android 8(三星,华为,谷歌) .

我得到这两个崩溃:

Fatal Exception: android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1881)
       at android.os.Handler.dispatchMessage(Handler.java:105)
       at android.os.Looper.loop(Looper.java:164)
       at android.app.ActivityThread.main(ActivityThread.java:6938)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

另一个:

Fatal Exception: android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2104)
       at android.os.Handler.dispatchMessage(Handler.java:108)
       at android.os.Looper.loop(Looper.java:166)
       at android.app.ActivityThread.main(ActivityThread.java:7428)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)

我假设这些崩溃是相同的,但正如您所见,堆栈跟踪显示不同的代码行 .

问题是我无法重现它,我的设备和模拟器上的一切正常 . 但是,我(不知何故)通过创建服务而不在 Service 类中调用 startForeground() 来复制 .

我无法“捕获”异常,因为它在创建服务5秒后立即来自系统级别 .

我做了什么,我创建了一个创建粘性通知并调用 startForeground 方法(我的Service类)的方法:

private void startWithNotification() {
     Resources res = getResources();
     String title = res.getString(R.string.application_name);

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
         createChannels();
     }

     NotificationCompat.Builder builder =  new NotificationCompat.Builder(context, ANDROID_CHANNEL_ID)
             .setContentTitle(title)
             .setChannelId(ANDROID_CHANNEL_ID)
             .setCategory(NotificationCompat.CATEGORY_SERVICE)
             .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
             .setPriority(NotificationCompat.PRIORITY_DEFAULT)
             .setOngoing(true)
             .setAutoCancel(false)
             .setSmallIcon(R.drawable.ic_siluette)
             .setColor(ContextCompat.getColor(this, R.color.colorPrimary))
             .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.application_icon));

     startForeground(NOTIFICATION_APP, builder.build());
 }

 private void createChannels() {
     // create android channel
     NotificationChannel androidChannel = new NotificationChannel(ANDROID_CHANNEL_ID, ANDROID_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
     // Sets whether notifications posted to this channel should display notification lights
     androidChannel.enableLights(true);
     // Sets whether notification posted to this channel should vibrate.
     androidChannel.enableVibration(true);
     // Sets the notification light color for notifications posted to this channel
     androidChannel.setLightColor(Color.GREEN);
     // Sets whether notifications posted to this channel appear on the lockscreen or not
        androidChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);

     NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

     nm.createNotificationChannel(androidChannel);
 }

从不同的服务生命周期事件中调用此方法:

onCreate()
onStartCommand()
stopService()
onDestroy()

我在这些事件中调用方法,因为人们说服务可能没有被创建并且它会被自动销毁 .

当通过 BroadcastReceiver 进行传入或传出呼叫时,服务开始:

public class IncomingOutgoingCallReceiver extends BroadcastReceiver {
   private void callAppService(Context context, int callType) {
        Intent intent = new Intent(context, MyService.class);
        Bundle bundle = new Bundle();
        bundle.putInt(CALL_TYPE, callType);
        intent.putExtras(bundle);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(intent);
        }
        else {
            context.startService(intent);
        }
    }

    private void onCallEnd(Context context) {
       context.stopService(new Intent(context, MyService.class));
    }
}

服务类:

public class MyService extends Service {

    private void handleIntent(Intent intent) {
        // Use intent data and do work

        if (canStartService(intent)) {
            return;
        }
    }

    private boolean canStartService(Intent intent) {
        // multiple checks
        // if (intent bundle contains ... ) return false;
        // if (phone number contains .... ) return false;

        return true;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        startWithNotification();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        handleIntent(intent);
        startWithNotification();

        return START_NOT_STICKY;
    }

    private void startWithNotification() {
        // Contains the code from above (didn't put here because of space)
    }

    @Override
    public boolean stopService(Intent name) {
        startWithNotification();

        return super.stopService(name);
    }

    // Can be called from different Views which are attached to the WindowManager (user interacting with the UI)
    public void stopService() {
        startWithNotification();

        stopForeground(true);
        stopSelf();
    }

    @Override
    public void onDestroy() {
        startWithNotification();

        super.onDestroy();
    }

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