Задание планировщика заданий Android не выполняется после закрытия приложения

Моя работа выполняется отлично, если приложение открыто, но если я закрою приложение, служба работы никогда не выполняется. Я следил за учебником здесь; Job Scheduler — Code In Flow, и он просто не запускается (или, может быть, запускается, а мы не знаю, он работает?). Я добавил уведомления и методы Log.d в свои методы, чтобы я мог видеть, выполняется ли задание, и пока приложение открыто, сообщения журнала и уведомления проходят, как и ожидалось.

Любая помощь будет оценена по достоинству. Спасибо

ОБНОВЛЕНИЕ 2
================
Я обнаружил, что, поскольку задание выполняется, когда приложение открыто, если я закрою приложение, задание отменяется без вызова onStopJob. теоретически, если я не ошибаюсь, планировщик заданий должен продолжать работу независимо от того, запущено приложение или нет. сообщение Log.d выглядит так

D/global_PEA: Job Scheduled //Schedule Job Button Pressed
D/global_PEA: Job Started //ExJobService runs
D/global_PEA: run: 0
D/global_PEA: run: 1
D/global_PEA: run: 2
D/global_PEA: run: 3
D/global_PEA: run: 4
// App closed and the output and notifications stop
// in theory it should continue till 9

=======

ОБНОВЛЕНИЕ
================
Вот дамп оболочки adb моего приложения. вроде по расписанию? adb_shellDump

Я не могу получить какие-либо уведомления или сообщения Log.d, когда принудительно запускаю задание из adb, например
adb shell cmd jobscheduler run -f com.raaif.pea 902

что наводит меня на мысль, что, возможно, задание действительно выполняется, но пользователю об этом ничего не показывается, как уведомление. Есть ли способ уведомить приложение, даже если оно откроет приложение, когда работа будет выполнена?

==============

вот мои файлы кода

public class HomeFragment extends Fragment {

    private Context mContext;

    private Button btn1;
    private Button btn2;

    @Override
    public void onAttach(Context context) {
        mContext = context;
        super.onAttach(context);
    }

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {

        View root = inflater.inflate(R.layout.fragment_home, container, false);

        btn1 = root.findViewById(R.id.button1);
        btn2= root.findViewById(R.id.button2);

        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                scheduleJob();
            }
        });
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                cancelJob();
            }
        });


        return root;
    }

    public void scheduleJob(){
        ComponentName componentName = new ComponentName(mContext, ExJobService.class);
        JobInfo info = new JobInfo.Builder(JOB_ID_1, componentName)
                .setRequiresCharging(false)
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
                .setPersisted(true)
                .setPeriodic(15 * 60 * 1000)
                .build();

        JobScheduler scheduler = (JobScheduler) mContext.getSystemService(JOB_SCHEDULER_SERVICE);
        int resultcode = scheduler.schedule(info);
        if(resultcode==JobScheduler.RESULT_SUCCESS){
            Log.d(globals.appTag,"Job Scheduled");
        }else{
            Log.d(globals.appTag,"Job Scheduling failed");
        }
    }

    public void cancelJob(){
        JobScheduler scheduler = (JobScheduler)getContext().getSystemService(JOB_SCHEDULER_SERVICE);
        scheduler.cancel(JOB_ID_1);
        Log.d(globals.appTag,"Job cancelled");
    }
}

А вот мой класс JobService

import android.app.Notification;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Intent;
import android.util.Log;

import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

import static com.raaif.pea.Constants.CHANNEL_ID_NOTIFICATION_HIGH;
import static com.raaif.pea.Constants.HIGH_NOTIF_ID;

public class ExJobService extends JobService {
    private Globals globals = Globals.getInstance();
    private boolean jobcancelled = false;
    private boolean reschedulestatus = false;

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.d(globals.appTag, "Job Started");
        //output low notification
        notify("Scheduled JobService", "Starting Background Work");
        doBackgroundWork(params);
        return true;
    }

    private void doBackgroundWork(final JobParameters params) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    if (jobcancelled) return;
                    Log.d(globals.appTag, "run: " + i);
                    ExJobService.this.notify("Thread Iterator",
                            String.format("Iteration Count: %d", i));
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Log.d(globals.appTag, "Job Finished");
                ExJobService.this.notify("Scheduled JobService", "Background Work Finished");
                jobFinished(params, reschedulestatus);
                Log.d(globals.appTag, "Job Service Params returned with " + reschedulestatus);
                ExJobService.this.notify("Scheduled JobService", "Job Params returned");
            }
        }).start();
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.d(globals.appTag, "Job Cancelled before completion");
        jobcancelled = true;
        notify("OnStopJob", "Job Cancelled");
        return false;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //return Service.START_STICKY;
        return super.onStartCommand(intent, flags, startId);
    }

    public void notify(String title, String content) {
        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);

        Notification notification =
                new NotificationCompat.Builder(ExJobService.this, CHANNEL_ID_NOTIFICATION_HIGH)
                .setSmallIcon(R.drawable.ic_memory_black_24dp)
                .setContentTitle(title)
                .setContentText(content)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setOnlyAlertOnce(false)
                .build();
        notificationManager.notify(HIGH_NOTIF_ID, notification);
    }

}

Манифест.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.raaif.pea">

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">

        <!-- SERVICES -->
       <service android:name=".ExJobService"
           android:permission="android.permission.BIND_JOB_SERVICE" />

        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

person XcOder    schedule 05.02.2020    source источник
comment
ваш код выглядит нормально, можете ли вы проверить, действительно ли задание запланировано, выполнив эту команду над adb adb shell dumpsys jobscheduler   -  person H4SN    schedule 05.02.2020
comment
Вы заставили его работать?   -  person Tobi    schedule 07.11.2020


Ответы (2)


Я вижу, здесь есть проблема, но я не знаю, как вы с ней справитесь?! посмотри на это:

public class ExJobService extends JobService {
    ...
    private boolean reschedulestatus = false;

    @Override
    public boolean onStartJob(JobParameters params) {
        ....
        doBackgroundWork(params);
        return true;
    }

    private void doBackgroundWork(final JobParameters params) {
        new Thread(new Runnable() {
                ....
                jobFinished(params, reschedulestatus);
                ....
        }).start();
    }
}

Вы передаете метод false в jobFinished, который непреднамеренно не перепланирует задание.

person A Farmanbar    schedule 05.02.2020
comment
Просто чтобы подтвердить, я только что перестроил код с jobFinished(params, true); безуспешно. Я изменил его на false, пытаясь заставить его работать. изначально у меня было это правда. Я просто забыл переключить его обратно в true - person XcOder; 05.02.2020
comment
@XcOder это даже по расписанию? adb показывает, что это sdheduled? - person A Farmanbar; 05.02.2020
comment
Я прикрепил дамп приложения adb jobscheduler. я предполагаю, что он показывает мое приложение там, потому что оно запланировано? я не слишком уверен, как читать дамп adb - person XcOder; 05.02.2020
comment
@XcOder есть 2 условия, которые вы должны найти. 1 - это запланировано, но не работает 2 - это не запланировано, но работает. какой из них? - person A Farmanbar; 05.02.2020

При периодическом выполнении чего-либо в фоновом режиме — JobScheduler, WorkManager, AlarmManager и т. д. — вы должны учитывать, что вашего процесса может не быть рядом, когда вам пора выполнять свою работу. Android разветвит процесс для вас, но он «начинается с нуля». Все, что ваш пользовательский интерфейс мог настроить в памяти, например базу данных, было бы предназначено для какого-то предыдущего процесса и могло бы не быть настроено в новом процессе.

person Dipankar Baghel    schedule 05.02.2020
comment
так в любом случае я могу отправить уведомление после выполнения задания, возможно, не открывая/запуская приложение? - person XcOder; 05.02.2020