JobScheduler API 在 Android 8.0 设备上崩溃
JobScheduler API crash on Android 8.0 devices
为了满足我的应用程序的 Android 目标 API 26 要求,我修改了我的应用程序中的后台服务,以使用 JobScheduler 而不是仅作为普通服务启动。这是我更新的代码。
在我的 PhoneUtils class,
public static void scheduleTTSJob(Context context) {
if(isTTSJobServiceOn(context))
{
return;
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
ComponentName serviceComponent = new ComponentName(context, TTSJobScheduledService.class);
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, serviceComponent);
builder.setMinimumLatency(1 * 1000); // wait at least
builder.setOverrideDeadline(3 * 1000); // maximum delay
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
jobScheduler.schedule(builder.build());
}
}
public static boolean isTTSJobServiceOn( Context context ) {
boolean hasBeenScheduled = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
JobScheduler scheduler = (JobScheduler) context.getSystemService( Context.JOB_SCHEDULER_SERVICE );
for ( JobInfo jobInfo : scheduler.getAllPendingJobs() ) {
if ( jobInfo.getId() == JOB_ID ) {
hasBeenScheduled = true;
break;
}
}
}
return hasBeenScheduled;
}
public static void stopTTSJob(Context context)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
JobScheduler scheduler = (JobScheduler) context.getSystemService( Context.JOB_SCHEDULER_SERVICE );
for ( JobInfo jobInfo : scheduler.getAllPendingJobs() ) {
if ( jobInfo.getId() == JOB_ID ) {
scheduler.cancel(JOB_ID);
break;
}
}
}
}
TTSJobScheduledServiceclass
package services;
import android.annotation.TargetApi;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Intent;
import android.os.Build;
import utilities.PhoneUtils;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class TTSJobScheduledService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
Intent service = new Intent(getApplicationContext(), TTSService.class);
getApplicationContext().startService(service);
PhoneUtils.scheduleTTSJob(getApplicationContext()); // reschedule the job
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
return true;
}
}
TTSService 代码class:
package services;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.speech.tts.TextToSpeech;
import android.support.annotation.Nullable;
public class TTSService extends Service {
private static TextToSpeech voice =null;
public static TextToSpeech getVoice() {
return voice;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
// not supporting binding
return null;
}
public TTSService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try{
voice = new TextToSpeech(TTSService.this, new TextToSpeech.OnInitListener() {
@Override
public void onInit(final int status) {
}
});
}
catch(Exception e){
e.printStackTrace();
}
return Service.START_STICKY;
}
@Override
public void onDestroy() {
clearTtsEngine();
super.onDestroy();
}
public static void clearTtsEngine()
{
if(voice!=null)
{
voice.stop();
voice.shutdown();
voice = null;
}
}
}
此外,我在清单文件中添加了这项新服务:
<service
android:name="services.TTSJobScheduledService"
android:label="TTS service"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
我会在用户打开我的应用程序(如果尚未安排)、设备重启或应用程序在 Google Play 上升级时安排此服务。
然而,在我发布此应用程序更新后不久,我开始遇到 Android 8.0.0 及更高版本设备的此堆栈跟踪崩溃:
Caused by java.lang.IllegalStateException: Not allowed to start service Intent { cmp=abc.fgh.com.abc/services.TTSService }: app is in background uid UidRecord{6dafcbe u0a250 TRNB idle procs:1 seq(0,0,0)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1505)
at android.app.ContextImpl.startService(ContextImpl.java:1461)
at android.content.ContextWrapper.startService(ContextWrapper.java:644)
at services.TTSJobScheduledService.onStartJob(Unknown Source:15)
at android.app.job.JobService.onStartJob(JobService.java:71)
at android.app.job.JobServiceEngine$JobHandler.handleMessage(JobServiceEngine.java:108)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:180)
at android.app.ActivityThread.main(ActivityThread.java:6950)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:835)
据我所知,这不应该发生,因为我使用的是 JobScheduledService scheduleTTSJob() 调用,而不是代码中的普通 startService() 调用。它没有在我测试它的设备 (Moto G6) 上崩溃,它也有 Android 8.0.0,但现在在 Android 8.0.0 及更高版本的这些其他设备上崩溃。
As I understand, this should not be happening given that I am using a JobScHeduledService scheduleTTSJob() call and not a plain startService() call from my code
您肯定是在代码中使用普通的 startService()
调用。它在您的 onStartJob()
方法中:
@Override
public boolean onStartJob(JobParameters params) {
Intent service = new Intent(getApplicationContext(), TTSService.class);
getApplicationContext().startService(service);
PhoneUtils.scheduleTTSJob(getApplicationContext()); // reschedule the job
return true;
}
这就是堆栈跟踪显示您正在崩溃的地方。
Please let me know what is the fix to be made in my code
或者:
删除那个 startService()
调用,或者
使用ContextCompat.startForegroundService()
并让TTSService
在启动时调用startForeground()
,或者
如果适用,将 TTSService
逻辑移动到您直接从 onStartJob()
[=40= 执行的后台线程中的一些非服务代码中]
很难给你更具体的建议,因为我们不知道 TTSService
是什么。
为了满足我的应用程序的 Android 目标 API 26 要求,我修改了我的应用程序中的后台服务,以使用 JobScheduler 而不是仅作为普通服务启动。这是我更新的代码。
在我的 PhoneUtils class,
public static void scheduleTTSJob(Context context) {
if(isTTSJobServiceOn(context))
{
return;
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
ComponentName serviceComponent = new ComponentName(context, TTSJobScheduledService.class);
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, serviceComponent);
builder.setMinimumLatency(1 * 1000); // wait at least
builder.setOverrideDeadline(3 * 1000); // maximum delay
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
jobScheduler.schedule(builder.build());
}
}
public static boolean isTTSJobServiceOn( Context context ) {
boolean hasBeenScheduled = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
JobScheduler scheduler = (JobScheduler) context.getSystemService( Context.JOB_SCHEDULER_SERVICE );
for ( JobInfo jobInfo : scheduler.getAllPendingJobs() ) {
if ( jobInfo.getId() == JOB_ID ) {
hasBeenScheduled = true;
break;
}
}
}
return hasBeenScheduled;
}
public static void stopTTSJob(Context context)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
JobScheduler scheduler = (JobScheduler) context.getSystemService( Context.JOB_SCHEDULER_SERVICE );
for ( JobInfo jobInfo : scheduler.getAllPendingJobs() ) {
if ( jobInfo.getId() == JOB_ID ) {
scheduler.cancel(JOB_ID);
break;
}
}
}
}
TTSJobScheduledServiceclass
package services;
import android.annotation.TargetApi;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Intent;
import android.os.Build;
import utilities.PhoneUtils;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class TTSJobScheduledService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
Intent service = new Intent(getApplicationContext(), TTSService.class);
getApplicationContext().startService(service);
PhoneUtils.scheduleTTSJob(getApplicationContext()); // reschedule the job
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
return true;
}
}
TTSService 代码class:
package services;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.speech.tts.TextToSpeech;
import android.support.annotation.Nullable;
public class TTSService extends Service {
private static TextToSpeech voice =null;
public static TextToSpeech getVoice() {
return voice;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
// not supporting binding
return null;
}
public TTSService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try{
voice = new TextToSpeech(TTSService.this, new TextToSpeech.OnInitListener() {
@Override
public void onInit(final int status) {
}
});
}
catch(Exception e){
e.printStackTrace();
}
return Service.START_STICKY;
}
@Override
public void onDestroy() {
clearTtsEngine();
super.onDestroy();
}
public static void clearTtsEngine()
{
if(voice!=null)
{
voice.stop();
voice.shutdown();
voice = null;
}
}
}
此外,我在清单文件中添加了这项新服务:
<service
android:name="services.TTSJobScheduledService"
android:label="TTS service"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
我会在用户打开我的应用程序(如果尚未安排)、设备重启或应用程序在 Google Play 上升级时安排此服务。
然而,在我发布此应用程序更新后不久,我开始遇到 Android 8.0.0 及更高版本设备的此堆栈跟踪崩溃:
Caused by java.lang.IllegalStateException: Not allowed to start service Intent { cmp=abc.fgh.com.abc/services.TTSService }: app is in background uid UidRecord{6dafcbe u0a250 TRNB idle procs:1 seq(0,0,0)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1505)
at android.app.ContextImpl.startService(ContextImpl.java:1461)
at android.content.ContextWrapper.startService(ContextWrapper.java:644)
at services.TTSJobScheduledService.onStartJob(Unknown Source:15)
at android.app.job.JobService.onStartJob(JobService.java:71)
at android.app.job.JobServiceEngine$JobHandler.handleMessage(JobServiceEngine.java:108)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:180)
at android.app.ActivityThread.main(ActivityThread.java:6950)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:835)
据我所知,这不应该发生,因为我使用的是 JobScheduledService scheduleTTSJob() 调用,而不是代码中的普通 startService() 调用。它没有在我测试它的设备 (Moto G6) 上崩溃,它也有 Android 8.0.0,但现在在 Android 8.0.0 及更高版本的这些其他设备上崩溃。
As I understand, this should not be happening given that I am using a JobScHeduledService scheduleTTSJob() call and not a plain startService() call from my code
您肯定是在代码中使用普通的 startService()
调用。它在您的 onStartJob()
方法中:
@Override
public boolean onStartJob(JobParameters params) {
Intent service = new Intent(getApplicationContext(), TTSService.class);
getApplicationContext().startService(service);
PhoneUtils.scheduleTTSJob(getApplicationContext()); // reschedule the job
return true;
}
这就是堆栈跟踪显示您正在崩溃的地方。
Please let me know what is the fix to be made in my code
或者:
删除那个
startService()
调用,或者使用
ContextCompat.startForegroundService()
并让TTSService
在启动时调用startForeground()
,或者如果适用,将
[=40= 执行的后台线程中的一些非服务代码中]TTSService
逻辑移动到您直接从onStartJob()
很难给你更具体的建议,因为我们不知道 TTSService
是什么。