You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

部分设备上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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 05:01:19