1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import java.util.concurrent.ConcurrentLinkedQueue;
20import java.util.concurrent.ExecutorService;
21import java.util.concurrent.Executors;
22
23/**
24 * Internal utility class to keep track of process-global work that's
25 * outstanding and hasn't been finished yet.
26 *
27 * This was created for writing SharedPreference edits out
28 * asynchronously so we'd have a mechanism to wait for the writes in
29 * Activity.onPause and similar places, but we may use this mechanism
30 * for other things in the future.
31 *
32 * @hide
33 */
34public class QueuedWork {
35
36    // The set of Runnables that will finish or wait on any async
37    // activities started by the application.
38    private static final ConcurrentLinkedQueue<Runnable> sPendingWorkFinishers =
39            new ConcurrentLinkedQueue<Runnable>();
40
41    private static ExecutorService sSingleThreadExecutor = null; // lazy, guarded by class
42
43    /**
44     * Returns a single-thread Executor shared by the entire process,
45     * creating it if necessary.
46     */
47    public static ExecutorService singleThreadExecutor() {
48        synchronized (QueuedWork.class) {
49            if (sSingleThreadExecutor == null) {
50                // TODO: can we give this single thread a thread name?
51                sSingleThreadExecutor = Executors.newSingleThreadExecutor();
52            }
53            return sSingleThreadExecutor;
54        }
55    }
56
57    /**
58     * Add a runnable to finish (or wait for) a deferred operation
59     * started in this context earlier.  Typically finished by e.g.
60     * an Activity#onPause.  Used by SharedPreferences$Editor#startCommit().
61     *
62     * Note that this doesn't actually start it running.  This is just
63     * a scratch set for callers doing async work to keep updated with
64     * what's in-flight.  In the common case, caller code
65     * (e.g. SharedPreferences) will pretty quickly call remove()
66     * after an add().  The only time these Runnables are run is from
67     * waitToFinish(), below.
68     */
69    public static void add(Runnable finisher) {
70        sPendingWorkFinishers.add(finisher);
71    }
72
73    public static void remove(Runnable finisher) {
74        sPendingWorkFinishers.remove(finisher);
75    }
76
77    /**
78     * Finishes or waits for async operations to complete.
79     * (e.g. SharedPreferences$Editor#startCommit writes)
80     *
81     * Is called from the Activity base class's onPause(), after
82     * BroadcastReceiver's onReceive, after Service command handling,
83     * etc.  (so async work is never lost)
84     */
85    public static void waitToFinish() {
86        Runnable toFinish;
87        while ((toFinish = sPendingWorkFinishers.poll()) != null) {
88            toFinish.run();
89        }
90    }
91
92    /**
93     * Returns true if there is pending work to be done.  Note that the
94     * result is out of data as soon as you receive it, so be careful how you
95     * use it.
96     */
97    public static boolean hasPendingWork() {
98        return !sPendingWorkFinishers.isEmpty();
99    }
100
101}
102