部分设备上JobService无法运行的问题排查求助
JobService在部分Android设备上无法启动,通知推送失败问题排查
我开发了一款定时推送通知的应用,用JobService实现应用关闭后仍能自动运行。但遇到了问题:自己的设备运行正常,部分朋友的设备上Job完全无法启动,通知也推不出来。
JobService类代码
package com.medicina.mednews; public class JobServiceNotification extends JobService { private static final String TAG = "JobServiceNotification "; private boolean jobCancelled = false; String android_id; FirebaseDatabase db = FirebaseDatabase.getInstance(); DatabaseReference reference, refJob; @Override public boolean onStartJob(JobParameters params) { Log.d(TAG, "Job started"); doBackgroundWork(params); return true; } private void doBackgroundWork(final JobParameters params) { new Thread(new Runnable() { @Override public void run() { android_id = Settings.Secure.getString(getApplicationContext().getContentResolver(), Settings.Secure.ANDROID_ID); reference= db.getReference().child("Usuarios").child(android_id).child("NoticiasDoPython"); refJob = db.getReference().child("Usuarios").child(android_id).child("JobServiceFinished"); reference.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) { } @Override public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) { } @Override public void onChildRemoved(@NonNull DataSnapshot snapshot) { PrincipalModel principalModell3=snapshot.getValue(PrincipalModel.class); Log.i("G3",""+principalModell3); Log.i("GT",snapshot.getKey()); String aa = principalModell3.getData(); String[] aaS = aa.split(" "); String idNotification = aaS[4]; idNotification = idNotification.replace(":","").trim(); Log.i("IdNotification",idNotification); addNotification(principalModell3.getTitulo(),principalModell3.getDescrição(),idNotification); } @Override public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) { } @Override public void onCancelled(@NonNull DatabaseError error) { } }); for (int i = 0; i < 10; i++) { copyFirebaseData(); Log.i("Execução","Executei = "+i); if (jobCancelled) { return; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } Log.d(TAG, "Job finished"); DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); Date date = new Date(); Date currentTime = Calendar.getInstance().getTime(); refJob.child(dateFormat.format(date)).setValue(""+currentTime); jobFinished(params, false); } }).start(); } @Override public boolean onStopJob(JobParameters params) { Log.d(TAG, "Job cancelled before completion"); jobCancelled = true; return true; } public void copyFirebaseData() { android_id = Settings.Secure.getString(getApplicationContext().getContentResolver(), Settings.Secure.ANDROID_ID); DatabaseReference questionNodes = FirebaseDatabase.getInstance().getReference().child("Usuarios").child(android_id).child("NoticiasDoPython"); final DatabaseReference toUsersQuestions = FirebaseDatabase.getInstance().getReference().child("Usuarios").child(android_id).child("NoticiasMovidasPeloAndroid"); questionNodes.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { for (DataSnapshot questionCode : dataSnapshot.getChildren()) { String KeyChild = questionCode.getKey(); Log.i("KEYYY",KeyChild); String DateAntiga = questionCode.child("Data").getValue(String.class); String DescricaoAntiga = questionCode.child("Descrição").getValue(String.class); String TituloAntiga = questionCode.child("Titulo").getValue(String.class); String LinkAntiga = questionCode.child("Link").getValue(String.class); String ImagemAntiga = questionCode.child("Imagem").getValue(String.class); String Id = questionCode.child("IdNoticia").getValue(String.class); toUsersQuestions.child(KeyChild).child("Id").setValue(Id); toUsersQuestions.child(KeyChild).child("Imagem").setValue(ImagemAntiga); toUsersQuestions.child(KeyChild).child("Data").setValue(DateAntiga); toUsersQuestions.child(KeyChild).child("Descrição").setValue(DescricaoAntiga); toUsersQuestions.child(KeyChild).child("Link").setValue(LinkAntiga); toUsersQuestions.child(KeyChild).child("Titulo").setValue(TituloAntiga); questionNodes.child(KeyChild).removeValue(); } } @Override public void onCancelled(DatabaseError databaseError) { } }); } private void addNotification(String titleNot,String DescriptNot, String idNotification) { NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("default", "Channel name", NotificationManager.IMPORTANCE_DEFAULT); channel.setDescription("Channel description"); notificationManager.createNotificationChannel(channel); } NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "default") .setSmallIcon(R.drawable.ic_baseline_nuvem_deletar) .setContentTitle(getResources().getString(R.string.app_name)) .setDefaults(Notification.DEFAULT_ALL) .setContentTitle(titleNot) .setPriority(Notification.PRIORITY_HIGH) .setPriority(NotificationManager.IMPORTANCE_HIGH) .setStyle(new NotificationCompat.BigTextStyle() .bigText(DescriptNot)) .setCategory(NotificationCompat.CATEGORY_MESSAGE) .setContentText(DescriptNot); builder.setSound(Settings.System.DEFAULT_NOTIFICATION_URI); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); //ESTAMOS DEFININDO UM ID ALEATÓRIO PARA GERAR MÚLTIPLAS NOTIFICAÇÕES Calendar cal = Calendar.getInstance(); long millisecondOfDay = TimeUnit.HOURS.toMillis(cal.get(Calendar.HOUR_OF_DAY)) + TimeUnit.MINUTES.toMillis(cal.get(Calendar.MINUTE)) + TimeUnit.SECONDS.toMillis(cal.get(Calendar.SECOND)) + cal.get(Calendar.MILLISECOND); manager.notify(Integer.parseInt(idNotification), builder.build()); //IREMOS DEFINIR UM ID COM UUID, MAS DEPOIS TEREMOS QUE MUDAR POIS ISSO NÃO ESTÁ CERTO !!! POSSIVELMENTE TEREMOS QUE IR PRO JSON //ESTAMOS DEFININDO UM ID ALEATÓRIO PARA GERAR MÚLTIPLAS NOTIFICAÇÕES } }
Manifest配置
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.QUICKBOOT_POWERON" /> <application android:name=".FirebaseHandler" android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" android:theme="@style/Theme.Mednews" tools:targetApi="31"> <receiver android:name=".StartReceiver" android:exported="true" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> </intent-filter> </receiver> <service android:name=".JobServiceNotification" android:permission="android.permission.BIND_JOB_SERVICE" /> <activity android:name=".MedMenu.activity.MainActivity" android:exported="true" android:screenOrientation="portrait" android:theme="@style/AppTheme.NoActionBar" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <meta-data android:name="preloaded_fonts" android:resource="@array/preloaded_fonts" /> </application> </manifest>
我试过用标准JobService类,但部分设备还是无法运行,通知推不出来。
问题排查与修复方案
1. 厂商后台限制是核心原因
国内小米、华为、OPPO、vivo等厂商都有独立的后台管理机制,会主动清理非白名单应用的后台任务,包括JobService。
- 解决办法:在应用启动页或设置页面引导用户手动将应用加入后台白名单,比如小米路径是「设置→应用设置→应用管理→你的应用→省电策略→无限制」,不同厂商路径不同,可以针对性做引导提示。
2. Job调度策略不符合系统规则
JobScheduler会根据系统电量、网络、空闲状态调度任务,约束过严会导致任务无法触发:
- 检查你的Job调度代码(未贴出),如果不是必须,不要设置
setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED),放宽为NETWORK_TYPE_ANY - Android 7.0+定时任务最小间隔是15分钟,系统可能会调整实际执行时间,不要依赖精确到秒的触发
- 若需精确执行,Android 6.0+可使用
setExactAndAllowWhileIdle(),但注意会增加电量消耗,且Android 12+需要额外申请SCHEDULE_EXACT_ALARM权限:
同时要在运行时请求用户授权。<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
3. 当前JobService逻辑存在生命周期问题
你的代码中,线程执行10次循环后就调用jobFinished(params, false),会标记Job完成,后续Firebase的ChildEventListener将无法触发;另外copyFirebaseData里的Firebase操作是异步的,Thread.sleep(1000)无法保证操作完成,可能导致数据处理异常。
- 修复建议:如果需要持续监听Firebase事件,不要过早调用
jobFinished,或者改用Firebase Cloud Messaging(FCM)推送通知,比自行轮询更可靠。
4. 改用WorkManager提高兼容性
WorkManager是Jetpack官方组件,自动适配不同Android版本和厂商后台限制,比JobService更稳定:
- 将JobService逻辑迁移到
Worker或CoroutineWorker中 - 使用
PeriodicWorkRequest实现定时任务,系统会自动处理后台限制和设备重启后的任务恢复
内容的提问来源于stack exchange,提问作者João Oliveira Ferreira
相关产品推荐
相关产品推荐

