Android Oreo JobIntentService在Android 7及以下版本中可以保持后台运行,但在Android 8及以上版本中经常崩溃。

9
我最近将所有服务替换为前台服务和JobIntentService,因为在Oreo及以上版本中存在一些后台执行限制(https://developer.android.com/about/versions/oreo/background)。根据文档,JobIntentService在Android 7及以下版本中的行为类似于Intent Service,在Android 8及以上版本中的行为类似于JobScheduler。我注意到Google提供的新JobIntentService存在问题。
Android 8及以上版本: 在Android 8及以上版本中不断发生崩溃。这里有一个提出相同问题的票据https://issuetracker.google.com/issues/63622293,我已经添加了一些极客建议的临时修复方法。
Android 7及以下版本: JobIntentService的行为类似于Intent Service,但在完成工作后无法停止。
我已在服务中实现了JobIntentService,每当用户执行某些操作时就会触发。
代码
public class SampleJobIntentService extends FixedJobIntentService {

public static void postData(Context context, String data) {
    Intent intent = new Intent(context, SampleJobIntentService.class);
            intent.setAction(INITIAL_ACTION);
            intent.putExtra(SAMPLE_ID, data);
            SampleJobIntentService.enqueueWork(context,intent);
}

public static void enqueueWork(Context context, Intent work) {
    SampleJobIntentService.enqueueWork(context, SampleJobIntentService.class, JOB_ID, work);

 @Override
    protected void onHandleWork(@NonNull Intent intent) {
        if (intent != null) {
            SampleRequest sampleRequest = requests.get(intent.getAction());
            if (sampleRequest != null) {
                try {
                   // perform some networking operations
                } catch (Exception ex) {
                    Log.d("Error for intent ");
                }
                Log.i("send action ");
            } else
                Log.e("action not found for ");
        }
    }
    }

为避免与JobIntentService崩溃,我参考了https://issuetracker.google.com/issues/63622293的一些内容。
public abstract class FixedJobIntentService extends JobIntentService {

    @Override
    GenericWorkItem dequeueWork() {
        try {
            return new FixedGenericWorkItem(super.dequeueWork());
        } catch (SecurityException ignored) {
            doStopCurrentWork();
        }
        return null;
    }

    private class FixedGenericWorkItem implements GenericWorkItem {
        final GenericWorkItem mGenericWorkItem;

        FixedGenericWorkItem(GenericWorkItem genericWorkItem) {
            mGenericWorkItem = genericWorkItem;
        }

        @Override
        public Intent getIntent() {
            if (mGenericWorkItem != null) {
                return mGenericWorkItem.getIntent();
            }
            return null;
        }

        @Override
        public void complete() {
            try {
                if (mGenericWorkItem != null) {
                    mGenericWorkItem.complete();
                }
            } catch (IllegalArgumentException ignored) {
                doStopCurrentWork();
            }
        }
    }
}

为什么要触发一个服务来触发JobIntentService?用户操作时可以直接触发JobIntentService吗? - Ankur
即使我直接触发JobIntentService,它在工作完成后也没有被杀死。我在我的项目中提到了一个使用它的场景。我在我的项目中使用了几个JobIntentServices。 - Kalai.G
你能提供一下你正在使用的JobIntentService的示例代码和触发代码吗? - Ankur
我已经更新了我的问题。 - Kalai.G
1
@Kalai.G,好的...我明白了...我会逐步解释作为答案...因为我在这里看不到太多空间...而且我也不想让整个线程充满闲聊。 - sandhya sasane
谁给这个问题投了反对票,请给我一个有效的理由。你认为在这里报告的开发人员https://issuetracker.google.com/issues/63622293 是垃圾吗? - Kalai.G
3个回答

19

好的,这是一个庞大的理论,无法在这里全部阐述。我会尽力让一些概念更加清晰易懂。


我已经花了两年时间阅读谷歌文档...它们是没有用的...没有适当的文档和适当的示例代码供开发人员使用..!! 所以我在每篇stack-overflow的帖子中都提到这一点,因为这可以帮助节省其他人的时间..!!


看起来你是一位优秀的程序员;只需要一些提示来回答你的问题 :

提示1:

YOU :- 我最近将所有服务替换为前台服务和 JobIntentService

前台服务:

如果你需要始终运行的进程,此进程将永远不会结束...一旦启动就会一直运行,则使用服务,该服务从其OnStartCommand返回START_STICKY。但再次建议不要使用它,因为如果你想不惜一切代价实现它...那么你就必须使用setOngoing(true)的通知,这样结束用户就无法滑动通知,它将永远存在。

使用前台服务:

在 Android 系统 Oreo 及以上版本中,对接收器的使用也有一些限制,声明所有接收器和意图操作将无法在清单文件中进行,并且只能通过声明 BootComplete 权限来使用单个接收器,该接收器接收 boot_completed 意图并在 Oreo 以下调用服务,在 Oreo 及以上则调用前台服务。现在,您可以从前台服务中实现所有运行时接收器,并在 Ondestroy 方法中取消注册。我从未找到过官方的运行时接收器示例代码,最终是经过数月辛勤工作成功实现了... 不得不说,这并不是一个聪明的解决方案,因为 Google 对此采取了限制。

何时使用前台服务:

仅当您需要实现广播接收器时才需要使用前台服务... 如果您不需要实现任何广播接收器,请保持距离......

提示-2:

你:我最近已经将所有服务替换为前台服务和 JobIntentService

** 服务具有以下特点:**

只需要完成一个非常微小的工作...然后退出...必须通过StopSelf()退出...但是,如果多次调用,服务可能会导致数据丢失...由于同一服务线程可以运行多次...如果您想使服务做很多工作...请使用START_STICKY...但我已经建议了什么时候使用它,在提示1中。

** Intentservice的质量:**

执行相对较长的任务,并具有仅串行执行的特性。如果再次调用相同的intentService,则所有调用将被保留在队列中,并将在一个接一个地完成一个接一个。这与上面描述的服务不同。它会自动结束...无需开发人员结束它..!!

** 所有的独特之处 :**

一旦它们崩溃,Android就可以停止在未来调用它们而不通知您,因为它会使应用程序崩溃。需要使用try-catch-exception避免崩溃。但是...如果在服务内实现线程,那么try-catch-exception无法防止应用程序崩溃...

** 那么该怎么办 & 如何实现呢 :**

使用FireBaseJobScedular :-

  1. 易于使用
  2. 使用简单的JobService
  3. 能够运行更长或更短的任务... 即使是一直在运行的任务
  4. 甚至被非标准公司支持,如vivo、mi、oppo、one+3等,它们对原版安卓(stock-android)进行更改并赋予名称,如FunTouchOs、ColorOs、OxygenOs。
  5. 只需要将电池设置更改为“不优化此应用程序”
  6. 是的,谷歌官方支持它,并建议使用它
  7. 它创建了GooglePlyService的实例并在其中运行,显然非标准公司也不会限制谷歌应用程序执行其任务。
  8. 适用于Oreo..,我甚至已经在Android P上测试过,并且可以在Android 5.0以下的版本中作为AlarmManager任务运行。
  9. 仍然建议使用minsdk大于16target sdk 26,因为如果您想上传应用到Google Play,现在必须这样做,您可能已经听说过这个消息。以及compile sdk 26
  10. 只需在清单中绑定您的JobService,并使用单行权限receive_boot_complete
  11. 只需安排它...它将在市场上的每个制造商的设备上启动...即使在cold boothot boot上也是如此
  12. 它最小化了大量代码,并且您可以专注于实际任务
  13. 任务完成后,您可以使用return false表示任务已完成,并将结束JobService。

我建议这样做是因为我是一家知名公司的CTO,并且曾经经历过在许多类型的安卓手机制造商中引起的前台服务问题...这不是苹果和iOS,所以我们必须经历。我是过去18年来一直担任开发者,今天也大多数时间都在编写代码...所有的开发项目和开发策略都是由我自己思考的。

请纠正我...因为您没有提到您的任务项目与何相关... 以及您确切想在前台服务和意图服务中完成什么... 让我知道... 我很乐意帮助您。 这是一个一般性的理论回答,而不是您想要的实际答案... 但是要给您提供确切的答案,我需要了解您项目范围的详细信息。


我想感谢您解决我的问题。根据我的项目要求(我不能公开披露),我必须使用前台服务,这个服务将触发新的Jobintentservice,发送度量数据,并且这不会自动被杀死(Intentservice以前可以)。根据您的建议,如果我使用firebasejobscheduler,这个问题就会消失了?让我试一试。再次感谢您。 - Kalai.G
问题将一直存在,直到出现编程和依赖错误。仅仅实现“firebasejobscheduler”并不能解决你的问题。这些是范例... - sandhya sasane
如果我们从后台启动意图服务,将会抛出Illegal state exception异常,因为存在后台执行限制。 - Kalai.G
1
后台执行限制可以通过前台服务或者如果您不想要一个粘性通知,那么应该使用AlarmManager来解决。同样,您可以运行长时间运行的任务,但不能是永久任务。再次,在每次重新启动时,系统不会记住闹钟设置,因此需要使用数据库进行处理,并在启动时重新设置闹钟。 - sandhya sasane
1
忘了提到 JobIntentService 仍处于 alpha 版本。 - Kalai.G
显示剩余2条评论

1

JobIntentService类似于Intent Service,但一旦工作完成就不会停止

问题出在您扩展的FixedJobIntentService类中的dequeueWork方法。

尝试将其更改为以下内容:

GenericWorkItem superValue = super.dequeueWork();
if (superValue != null) {
    return new FixedGenericWorkItem(superValue);
}
return null;

查看JobIntentService代码,工作项处理逻辑如下,即直到队列中没有剩余的工作项为止,所有项目都会被处理(即对于每个项目调用onHandleWork)

    while ((work = dequeueWork()) != null) {
        if (DEBUG) Log.d(TAG, "Processing next work: " + work);
        onHandleWork(work.getIntent());
        if (DEBUG) Log.d(TAG, "Completing work: " + work);
        work.complete();
    }

您的实现中存在问题,即在处理完第一个工作项后,super.dequeueWork()返回null,而您没有对此进行处理,只是发送了一个新的FixedGenericWorkItem对象并传递了null值。您可能会注意到,在后续调用中将向onHandleWork传递null值。
希望这可以解决您的问题。

0

我认为你只需要这么多的代码。创建一个新的类MyJobIntentService并编写这么多的代码,然后调用postData()来启动你的服务。

public class MyJobIntentService extends JobIntentService {

public static void postData(Context context, String data) {
    final Intent intent = new Intent(context, MyJobIntentService.class);
    intent.setAction(INITIAL_ACTION);
    intent.putExtra(SAMPLE_ID, data);
    enqueueWork(context, MyJobIntentService.class, 1000, intent);
}

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

@Override
public void onDestroy() {
    Ln.d("Cancelled service");
    super.onDestroy();
}

@Override
    protected void onHandleWork(@NonNull Intent intent) {
        if (intent != null) {
            final SampleRequest sampleRequest = requests.get(intent.getAction());
            if (sampleRequest != null) {
                try {
                   // perform some networking operations
                } catch (Exception ex) {
                    Log.d("Error for intent ");
                }
                Log.i("send action ");
            } else {                
               Log.e("action not found for ");
            }
        }
    }
}

同时确保在清单文件中添加您的服务

<service
            android:name="service.MyJobIntentService"
            android:exported="false"
            android:permission="android.permission.BIND_JOB_SERVICE" />

你有查看过这个问题跟踪器吗?https://issuetracker.google.com/issues/63622293 - Kalai.G
1
是的,我已经全文阅读了。但是我的用户没有遇到任何崩溃问题。我面临的唯一问题是,如果对其进行多次调用,则服务不会启动,并且这在OREO设备上发生。 - Ankur
没错。我的应用程序过去会多次调用发送度量指标的功能,这个崩溃经常发生。你测试过在Android 7及以下版本中,JobIntentService在工作完成后是否已停止吗? - Kalai.G
根据评论,这似乎不是一个有效的答案,或者它还没有被测试过?如果是这种情况,应该将其删除。 - StarWind0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接