WorkManager作业在一段时间后无法重新计划或者未能执行。

3

最近我在使用WorkManager重新安排任务时遇到了问题。目前发现,当使用Okhttp发出请求并在AuthInterceptor上引发错误后,它会卡住,没有其他任务被启动。

这是管理工作计划的JobOrganizer类的第一步。它链接了第一个作业队列。还有更多未粘贴在此处的作业,但主要区别是第一个链接作业不需要WiFi网络限制,而其他作业则需要。

object JobOrganizer {

    const val WORK_INTERVAL: Long = 20
    const val SCH_DATA_UPDATE_WORK_RESCHEDULE = "scheduled_data_update_work_reschedule"
    const val SCH_DATA_UPDATE_WORK = "scheduled_data_update_work"

    private val schDataUpdateJob: OneTimeWorkRequest
        get() = OneTimeWorkRequestBuilder<SCHDataUpdateJob>()
                .addTag(SCH_DATA_UPDATE_WORK)
                .setConstraints(wifiConstraint)
                .build()

    val wifiConstraint: Constraints
        get() = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.UNMETERED)
                .setRequiresDeviceIdle(false)
                .setRequiresBatteryNotLow(false)
                .setRequiresCharging(false)
                .setRequiresStorageNotLow(false)
                .build()

    fun getWorkInfos(context: Context, tag: String): LiveData<List<WorkInfo>> {
        val workManager = WorkManager.getInstance(context)
        return workManager.getWorkInfosByTagLiveData(tag)
    }

    private fun clearWorks(workManager: WorkManager) {
        workManager.pruneWork()
    }

    private fun cancelSCHJobs(context: Context) {
        val workManager = WorkManager.getInstance(context)
        workManager.cancelAllWorkByTag(SCH_DATA_UPDATE_WORK )
        clearWorks(workManager)
    }

    fun scheduleJobs(context: Context) {
        cancelSCHJobs(context)
        WorkManager.getInstance(context)
                .beginWith(schTypesDownloadJob)
                .then(schDownloadJob)
                .then(schDataUpdateJob)
                .then(schDataUploadJob)
                .then(schCleanupJob)
                .enqueue()
        FirebaseAnalytics.getInstance(context).logEvent(AnalyticsEvents.Sync.SYNC_SCH_CONFIGURE_FORM_CLEANUP, Bundle())
    }
}

AuthInterceptor类

class AuthInterceptor(private val context: Context?) : Interceptor {

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {

        val originalRequest = chain.request()

        if (context == null) {
            return chain.proceed(originalRequest)
        }

        val auth = AuthRepository(context).getAuth()
        if (auth.isNullOrEmpty()) {
            return chain.proceed(originalRequest)
        }

        val version = String.format(
                "%s: %s (build %s)",
                BuildConfig.FLAVOR,
                BuildConfig.VERSION_NAME,
                BuildConfig.VERSION_CODE
        )

        val compressedRequest = originalRequest.newBuilder()
                .header("Authorization", String.format("Bearer %s", auth[0].token))
                .header("mobile-app-version", version)
                .build()
        return chain.proceed(compressedRequest)
    }

}

更新工作会重新安排自己,并延迟30分钟。主要的try / catch用于处理AuthInterceptor错误。

class SCHDataUpdateJob(var context : Context, params : WorkerParameters) : Worker(context, params) {

    override fun doWork(): Result {
        FirebaseAnalytics.getInstance(context).logEvent(AnalyticsEvents.Sync.SYNC_SCH_UPDATE_START, Bundle())
        var success = UPDElementTypesJob(context).doWork()
        if (!success) {
            FirebaseAnalytics.getInstance(context).logEvent(AnalyticsEvents.Sync.SYNC_UPD_ELEMENTTYPES_ERROR, Bundle())
            Log.e("SYNC", "SCHDataUpdateJob UPDElementTypesJob error")
        }
        FirebaseAnalytics.getInstance(context).logEvent(AnalyticsEvents.Sync.SYNC_SCH_UPDATE_FINISH, Bundle())

        val dataUpdateWorkRequest = OneTimeWorkRequestBuilder<SCHDataUpdateJob>()
                .setInitialDelay(JobOrganizer.WORK_INTERVAL, TimeUnit.MINUTES)
                .addTag(JobOrganizer.SCH_DATA_UPDATE_WORK)
                .setConstraints(JobOrganizer.wifiConstraint)
                .build()

        WorkManager.getInstance(applicationContext)
                .enqueue(dataUpdateWorkRequest)

        Log.e("SYNC", "SCHDataUpdateJob finished")
        return Result.success()
    }
}

这是调用scheduleJobs()的片段。
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    super.onCreateView(inflater, container, savedInstanceState)

    mainView = inflater.inflate(R.layout.fragment_draft_list, container, false)

    sync = mainView!!.findViewById(R.id.sync)

    sync.onClick {
        mFirebaseAnalytics!!.logEvent(AnalyticsEvents.UPDATE_BUTTON_CLICKED, Bundle())
        if (!ConnectionUtils.isConnectedToWifi(activity!!.applicationContext)) {
            showConnectivityDialog()
        } else {
            sync.visibility = View.GONE
            doAsync {
                JobOrganizer.scheduleJobs(context!!)
            }
        }
    }

    if (forceDownload) {
        JobOrganizer.scheduleJobs(context!!)
    }

    return mainView!!
}

有时这个最后一个任务不会被安排或者不能运行。有什么线索吗?

谢谢。


你在哪里以及如何调用 scheduleJobs() 函数?你能否通过更新问题来展示给我们,以便我们可以找出答案? - Harry Timothy
scheduleJobs()方法在登录后立即调用,开始同步进程,在碎片内部运行。您也可以通过单击该碎片的更新按钮来调用scheduleJobs()方法。两者均在onCreateView中声明。 - reixa
我认为我知道答案,但还有一个最后的条件需要您验证:如果您在JobOrganizer.scheduleJobs(context!!)FirebaseAnalytics.getInstance(context)(在JobOrganizer内部)处设置断点,然后进行调试,它们会被调用吗? - Harry Timothy
第一次调用它们时,两者都会触发作业,但在某个时候它们停止被重新安排。当SCHUpdateJob成功时,它会重新安排自己。 - reixa
你有在任何地方返回 Result.failure() 吗?如果是这样,其他的任务将不会运行。也许当 AuthInterceptor 返回一个错误响应时,你确实放置了一个 Result.failure()。然而我不能确定,因为你没有分享发送请求的代码。 - Furkan Yurdakul
1
请确保在作业序列执行时,您的代码中没有任何地方调用 cancelSCHJobs。此外,如果您想要重新安排作业以在30分钟后执行,请改用 PeriodicRequestBuilder 而不是 OneTimeUniqueRequestBuilder 并使用不同的标签。如果两个作业的标签相同,则可能无法按照您的期望进行替换。 - Furkan Yurdakul
1个回答

0

在这里,您可以学习如何使用工作管理器。

创建一个新项目,并在app/build.gradle文件中添加WorkManager依赖项。

implementation "android.arch.work:work-runtime:1.0.0"

创建一个名为Worker的基类:-
package com.wave.workmanagerexample;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
/**
 * Created on : Mar 26, 2019
 * Author     : AndroidWave
 */
public class NotificationWorker extends Worker {
    private static final String WORK_RESULT = "work_result";
    public NotificationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }
    @NonNull
    @Override
    public Result doWork() {
        Data taskData = getInputData();
        String taskDataString = taskData.getString(MainActivity.MESSAGE_STATUS);
        showNotification("WorkManager", taskDataString != null ? taskDataString : "Message has been Sent");
        Data outputData = new Data.Builder().putString(WORK_RESULT, "Jobs Finished").build();
        return Result.success(outputData);
    }
    private void showNotification(String task, String desc) {
        NotificationManager manager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
        String channelId = "task_channel";
        String channelName = "task_name";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new
                    NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT);
            manager.createNotificationChannel(channel);
        }
        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), channelId)
                .setContentTitle(task)
                .setContentText(desc)
                .setSmallIcon(R.mipmap.ic_launcher);
        manager.notify(1, builder.build());
    }
}

创建WorkRequest:

现在,让我们转到MainActivity并创建一个WorkRequest来执行刚刚创建的工作。首先,我们将创建WorkManager。这个WorkManager将排队并管理我们的工作请求。

 WorkManager mWorkManager = WorkManager.getInstance();

现在我们将创建OneTimeWorkRequest,因为我想创建一个只会执行一次的任务。
OneTimeWorkRequest mRequest = new OneTimeWorkRequest.Builder(NotificationWorker.class).build();

使用此代码,我们构建了一个仅执行一次的工作请求。
使用WorkManager将请求排队:
btnSend.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mWorkManager.enqueue(mRequest);
        }
    });

获取特定任务的状态:-
mWorkManager.getWorkInfoByIdLiveData(mRequest.getId()).observe(this, new Observer<WorkInfo>() {
        @Override
        public void onChanged(@Nullable WorkInfo workInfo) {
            if (workInfo != null) {
                WorkInfo.State state = workInfo.getState();
                tvStatus.append(state.toString() + "\n");
            }
        }
    });

最后,MainActivity看起来像这样。

package com.wave.workmanagerexample;
import android.arch.lifecycle.Observer;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
public class MainActivity extends AppCompatActivity {
    public static final String MESSAGE_STATUS = "message_status";
    TextView tvStatus;
    Button btnSend;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvStatus = findViewById(R.id.tvStatus);
        btnSend = findViewById(R.id.btnSend);
        final WorkManager mWorkManager = WorkManager.getInstance();
        final OneTimeWorkRequest mRequest = new OneTimeWorkRequest.Builder(NotificationWorker.class).build();
        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mWorkManager.enqueue(mRequest);
            }
        });
        mWorkManager.getWorkInfoByIdLiveData(mRequest.getId()).observe(this, new Observer<WorkInfo>() {
            @Override
            public void onChanged(@Nullable WorkInfo workInfo) {
                if (workInfo != null) {
                    WorkInfo.State state = workInfo.getState();
                    tvStatus.append(state.toString() + "\n");
                }
            }
        });
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接