使用JobScheduler与Notification时Android应用崩溃问题排查
嘿,你的崩溃问题其实很典型,一眼就能定位到核心原因:
崩溃的核心原因
在后台线程的run方法里,你调用display时传的this是那个匿名Runnable对象,而不是你的Job1(JobService实例)!
JobService本身是Context的子类,完全可以作为通知所需的Context,但在Runnable的内部作用域里,this指向的是当前的Runnable实例——它根本不是Context类型。当display方法拿着这个无效的"Context"去调用getSystemService或者NotificationManagerCompat.from(ctx)时,自然会抛出异常,直接导致应用崩溃。
快速修复步骤
只需要修改一行代码,明确传递JobService的合法Context:
// 把原来的this改成Job1.this display(id++ , "channel1" , "Description" , "title" , Job1.this);
这一步就能直接解决崩溃问题。
额外优化建议(避免后续踩坑)
虽然上面的修复能解决崩溃,但你的JobService逻辑还有不符合Android设计规范的地方,容易引发其他问题:
无限循环不符合JobScheduler的设计初衷
JobScheduler的任务不适合长期运行的无限循环,系统会限制单个Job的运行时长,强行挂起的Job可能被系统终止。而且你现在的代码里cancelled变量根本没在循环里判断,即使Job被系统取消,线程还会一直跑下去。如果要实现周期性通知,更合理的方式是让每个Job只执行一次,然后告诉系统重新调度:
@Override public boolean onStartJob(JobParameters params) { Log.i(tag, "Job has been started"); // 直接显示本次通知 display(id++ , "channel1" , "Description" , "title" , this); // 通知系统任务完成,并且需要重新调度 jobFinished(params, true); return false; // 任务已在主线程完成,不需要后台线程 }然后在构建JobInfo时添加周期性设置:
JobInfo jinfo = new JobInfo.Builder(1, cname) .setRequiresCharging(false) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setPersisted(true) .setPeriodic(5000) // 注意:Android 7.0+之后,周期性任务的最小间隔是15分钟,若需要更短间隔需用其他方案 .build();完善取消逻辑(如果坚持用线程循环)
要是你一定要用自己的线程循环,记得在循环里判断cancelled变量,并且在任务结束后调用jobFinished:private void Job(JobParameters params){ new Thread(new Runnable() { @Override public void run() { while (!cancelled){ Log.i(tag, "Job is Running"); display(id++ , "channel1" , "Description" , "title" , Job1.this); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); break; } } // 任务结束后通知系统 jobFinished(params, true); } }).start(); }
最后提个醒
Android 8.0+对后台通知的限制越来越严格,尤其是Android 12+,即使你用JobService,也可能无法在应用完全关闭时每5秒弹出通知——这种高频后台操作很容易被系统的后台限制策略拦截。如果是刚需,可能需要考虑使用前台服务来实现。
内容的提问来源于stack exchange,提问作者Someone

