public class

WorkContinuationImpl

extends WorkContinuation

 java.lang.Object

androidx.work.WorkContinuation

↳androidx.work.impl.WorkContinuationImpl

Gradle dependencies

compile group: 'androidx.work', name: 'work-runtime', version: '2.10.0-alpha03'

  • groupId: androidx.work
  • artifactId: work-runtime
  • version: 2.10.0-alpha03

Artifact androidx.work:work-runtime:2.10.0-alpha03 it located at Google repository (https://maven.google.com/)

Overview

A concrete implementation of WorkContinuation.

Summary

Constructors
publicWorkContinuationImpl(WorkManagerImpl workManagerImpl, java.util.List<WorkRequest> work)

publicWorkContinuationImpl(WorkManagerImpl workManagerImpl, java.lang.String name, ExistingWorkPolicy existingWorkPolicy, java.util.List<WorkRequest> work)

publicWorkContinuationImpl(WorkManagerImpl workManagerImpl, java.lang.String name, ExistingWorkPolicy existingWorkPolicy, java.util.List<WorkRequest> work, java.util.List<WorkContinuationImpl> parents)

Methods
protected WorkContinuationcombineInternal(java.util.List<WorkContinuation> continuations)

public abstract Operationenqueue()

Enqueues the instance of WorkContinuation on the background thread.

public java.util.List<java.lang.String>getAllIds()

public ExistingWorkPolicygetExistingWorkPolicy()

public java.util.List<java.lang.String>getIds()

public java.lang.StringgetName()

public java.util.List<WorkContinuationImpl>getParents()

public java.util.List<WorkRequest>getWork()

public abstract <any>getWorkInfos()

Returns a of a java.util.List of WorkInfos that provides information about the status of each OneTimeWorkRequest in this WorkContinuation, as well as their prerequisites.

public abstract LiveData<java.util.List>getWorkInfosLiveData()

Returns a LiveData list of WorkInfos that provide information about the status of each OneTimeWorkRequest in this WorkContinuation, as well as their prerequisites.

public WorkManagerImplgetWorkManagerImpl()

public booleanhasCycles()

public booleanisEnqueued()

public voidmarkEnqueued()

Marks the WorkContinuationImpl as enqueued.

public static java.util.Set<java.lang.String>prerequisitesFor(WorkContinuationImpl continuation)

public abstract WorkContinuationthen(java.util.List<OneTimeWorkRequest> work)

Adds new OneTimeWorkRequest items that depend on the successful completion of all previously added OneTimeWorkRequests.

from WorkContinuationcombine, then
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public WorkContinuationImpl(WorkManagerImpl workManagerImpl, java.util.List<WorkRequest> work)

public WorkContinuationImpl(WorkManagerImpl workManagerImpl, java.lang.String name, ExistingWorkPolicy existingWorkPolicy, java.util.List<WorkRequest> work)

public WorkContinuationImpl(WorkManagerImpl workManagerImpl, java.lang.String name, ExistingWorkPolicy existingWorkPolicy, java.util.List<WorkRequest> work, java.util.List<WorkContinuationImpl> parents)

Methods

public WorkManagerImpl getWorkManagerImpl()

public java.lang.String getName()

public ExistingWorkPolicy getExistingWorkPolicy()

public java.util.List<WorkRequest> getWork()

public java.util.List<java.lang.String> getIds()

public java.util.List<java.lang.String> getAllIds()

public boolean isEnqueued()

public void markEnqueued()

Marks the WorkContinuationImpl as enqueued.

public java.util.List<WorkContinuationImpl> getParents()

public abstract WorkContinuation then(java.util.List<OneTimeWorkRequest> work)

Adds new OneTimeWorkRequest items that depend on the successful completion of all previously added OneTimeWorkRequests.

Parameters:

work: One or more OneTimeWorkRequest to add as dependents

Returns:

A WorkContinuation that allows for further chaining of dependent OneTimeWorkRequests

public abstract LiveData<java.util.List> getWorkInfosLiveData()

Returns a LiveData list of WorkInfos that provide information about the status of each OneTimeWorkRequest in this WorkContinuation, as well as their prerequisites. If the state or outputs of any of the work changes, any attached Observers will trigger.

Returns:

A LiveData containing a list of WorkInfos; you must use LiveData.observe(LifecycleOwner, Observer) to receive updates

public abstract <any> getWorkInfos()

Returns a of a java.util.List of WorkInfos that provides information about the status of each OneTimeWorkRequest in this WorkContinuation, as well as their prerequisites.

Returns:

A of a java.util.List of WorkInfos

public abstract Operation enqueue()

Enqueues the instance of WorkContinuation on the background thread.

Returns:

An Operation that can be used to determine when the enqueue has completed

protected WorkContinuation combineInternal(java.util.List<WorkContinuation> continuations)

public boolean hasCycles()

Returns:

true If there are cycles in the WorkContinuationImpl.

public static java.util.Set<java.lang.String> prerequisitesFor(WorkContinuationImpl continuation)

Returns:

the java.util.Set of pre-requisites for a given WorkContinuationImpl.

Source

/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.work.impl;

import static androidx.work.OperationKt.launchOperation;

import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.lifecycle.LiveData;
import androidx.work.ArrayCreatingInputMerger;
import androidx.work.ExistingWorkPolicy;
import androidx.work.Logger;
import androidx.work.OneTimeWorkRequest;
import androidx.work.Operation;
import androidx.work.WorkContinuation;
import androidx.work.WorkInfo;
import androidx.work.WorkRequest;
import androidx.work.impl.utils.EnqueueRunnable;
import androidx.work.impl.utils.StatusRunnable;
import androidx.work.impl.workers.CombineContinuationsWorker;

import com.google.common.util.concurrent.ListenableFuture;

import kotlin.Unit;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * A concrete implementation of {@link WorkContinuation}.
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class WorkContinuationImpl extends WorkContinuation {

    private static final String TAG = Logger.tagWithPrefix("WorkContinuationImpl");

    private final WorkManagerImpl mWorkManagerImpl;
    private final String mName;
    private final ExistingWorkPolicy mExistingWorkPolicy;
    private final List<? extends WorkRequest> mWork;
    private final List<String> mIds;
    private final List<String> mAllIds;
    private final List<WorkContinuationImpl> mParents;

    private boolean mEnqueued;
    private Operation mOperation;

    @NonNull
    public WorkManagerImpl getWorkManagerImpl() {
        return mWorkManagerImpl;
    }

    @Nullable
    public String getName() {
        return mName;
    }

    @NonNull
    public ExistingWorkPolicy getExistingWorkPolicy() {
        return mExistingWorkPolicy;
    }

    @NonNull
    public List<? extends WorkRequest> getWork() {
        return mWork;
    }

    @NonNull
    public List<String> getIds() {
        return mIds;
    }

    @NonNull
    public List<String> getAllIds() {
        return mAllIds;
    }

    public boolean isEnqueued() {
        return mEnqueued;
    }

    /**
     * Marks the {@link WorkContinuationImpl} as enqueued.
     */
    public void markEnqueued() {
        mEnqueued = true;
    }

    @Nullable
    public List<WorkContinuationImpl> getParents() {
        return mParents;
    }

    public WorkContinuationImpl(
            @NonNull WorkManagerImpl workManagerImpl,
            @NonNull List<? extends WorkRequest> work) {
        this(
                workManagerImpl,
                null,
                ExistingWorkPolicy.KEEP,
                work,
                null);
    }

    public WorkContinuationImpl(
            @NonNull WorkManagerImpl workManagerImpl,
            @Nullable String name,
            @NonNull ExistingWorkPolicy existingWorkPolicy,
            @NonNull List<? extends WorkRequest> work) {
        this(workManagerImpl, name, existingWorkPolicy, work, null);
    }

    public WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
            @Nullable String name,
            @NonNull ExistingWorkPolicy existingWorkPolicy,
            @NonNull List<? extends WorkRequest> work,
            @Nullable List<WorkContinuationImpl> parents) {
        mWorkManagerImpl = workManagerImpl;
        mName = name;
        mExistingWorkPolicy = existingWorkPolicy;
        mWork = work;
        mParents = parents;
        mIds = new ArrayList<>(mWork.size());
        mAllIds = new ArrayList<>();
        if (parents != null) {
            for (WorkContinuationImpl parent : parents) {
                mAllIds.addAll(parent.mAllIds);
            }
        }
        for (int i = 0; i < work.size(); i++) {
            if (existingWorkPolicy == ExistingWorkPolicy.REPLACE
                    && work.get(i).getWorkSpec().getNextScheduleTimeOverride() != Long.MAX_VALUE) {
                // We can't enforce a minimum period on non-first overrides if REPLACE is used,
                // since it gives us a new WorkSpec every time.
                throw new IllegalArgumentException(
                        "Next Schedule Time Override must be used with ExistingPeriodicWorkPolicy"
                                + "UPDATE (preferably) or KEEP");
            }
            String id = work.get(i).getStringId();
            mIds.add(id);
            mAllIds.add(id);
        }
    }

    @Override
    public @NonNull WorkContinuation then(@NonNull List<OneTimeWorkRequest> work) {
        if (work.isEmpty()) {
            return this;
        } else {
            return new WorkContinuationImpl(mWorkManagerImpl,
                    mName,
                    ExistingWorkPolicy.KEEP,
                    work,
                    Collections.singletonList(this));
        }
    }

    @Override
    public @NonNull LiveData<List<WorkInfo>> getWorkInfosLiveData() {
        return mWorkManagerImpl.getWorkInfosById(mAllIds);
    }

    @NonNull
    @Override
    public ListenableFuture<List<WorkInfo>> getWorkInfos() {
        return StatusRunnable.forStringIds(mWorkManagerImpl.getWorkDatabase(),
                mWorkManagerImpl.getWorkTaskExecutor(), mAllIds);
    }

    @Override
    public @NonNull Operation enqueue() {
        // Only enqueue if not already enqueued.
        if (!mEnqueued) {
            // The runnable walks the hierarchy of the continuations
            // and marks them enqueued using the markEnqueued() method, parent first.
            mOperation = launchOperation(
                    mWorkManagerImpl.getConfiguration().getTracer(),
                    "EnqueueRunnable_" + getExistingWorkPolicy().name(),
                    mWorkManagerImpl.getWorkTaskExecutor().getSerialTaskExecutor(),
                    () -> {
                        EnqueueRunnable.enqueue(this);
                        return Unit.INSTANCE;
                    });
        } else {
            Logger.get().warning(TAG,
                    "Already enqueued work ids (" + TextUtils.join(", ", mIds) + ")");
        }
        return mOperation;
    }

    @Override
    protected @NonNull WorkContinuation combineInternal(
            @NonNull List<WorkContinuation> continuations) {
        OneTimeWorkRequest combinedWork =
                new OneTimeWorkRequest.Builder(CombineContinuationsWorker.class)
                        .setInputMerger(ArrayCreatingInputMerger.class)
                        .build();

        List<WorkContinuationImpl> parents = new ArrayList<>(continuations.size());
        for (WorkContinuation continuation : continuations) {
            parents.add((WorkContinuationImpl) continuation);
        }

        return new WorkContinuationImpl(mWorkManagerImpl,
                null,
                ExistingWorkPolicy.KEEP,
                Collections.singletonList(combinedWork),
                parents);
    }

    /**
     * @return {@code true} If there are cycles in the {@link WorkContinuationImpl}.
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public boolean hasCycles() {
        return hasCycles(this, new HashSet<String>());
    }

    /**
     * @param continuation The {@link WorkContinuationImpl} instance.
     * @param visited      The {@link Set} of {@link androidx.work.impl.model.WorkSpec} ids
     *                     marked as visited.
     * @return {@code true} if the {@link WorkContinuationImpl} has a cycle.
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    private static boolean hasCycles(
            @NonNull WorkContinuationImpl continuation,
            @NonNull Set<String> visited) {

        // mark the ids of this workContinuation as visited
        // before we check if the parents have cycles.
        visited.addAll(continuation.getIds());

        Set<String> prerequisiteIds = prerequisitesFor(continuation);
        for (String id : visited) {
            if (prerequisiteIds.contains(id)) {
                // This prerequisite has already been visited before.
                // There is a cycle.
                return true;
            }
        }

        List<WorkContinuationImpl> parents = continuation.getParents();
        if (parents != null && !parents.isEmpty()) {
            for (WorkContinuationImpl parent : parents) {
                // if any of the parent has a cycle, then bail out
                if (hasCycles(parent, visited)) {
                    return true;
                }
            }
        }

        // Un-mark the ids of the workContinuation as visited for the next parent.
        // This is because we don't want to change the state of visited ids for subsequent parents
        // This is being done to avoid allocations. Ideally we would check for a
        // hasCycles(parent, new HashSet<>(visited)) instead.
        visited.removeAll(continuation.getIds());
        return false;
    }

    /**
     * @return the {@link Set} of pre-requisites for a given {@link WorkContinuationImpl}.
     */
    @NonNull
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static Set<String> prerequisitesFor(@NonNull WorkContinuationImpl continuation) {
        Set<String> preRequisites = new HashSet<>();
        List<WorkContinuationImpl> parents = continuation.getParents();
        if (parents != null && !parents.isEmpty()) {
            for (WorkContinuationImpl parent : parents) {
                preRequisites.addAll(parent.getIds());
            }
        }
        return preRequisites;
    }
}