• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 
17 package com.android.internal.listeners;
18 
19 import android.annotation.Nullable;
20 
21 import java.util.concurrent.Executor;
22 import java.util.function.Supplier;
23 
24 /**
25  * Interface (trait) for executing listener style operations on an executor.
26  */
27 public interface ListenerExecutor {
28 
29     /**
30      * An listener operation to perform.
31      *
32      * @param <TListener> listener type
33      */
34     interface ListenerOperation<TListener> {
35         /**
36          * Performs the operation on the given listener.
37          */
operate(TListener listener)38         void operate(TListener listener) throws Exception;
39 
40         /**
41          * Called before this operation is to be run. An operation may be canceled before it is run,
42          * in which case this method may not be invoked. {@link #onPostExecute(boolean)} will only
43          * be invoked if this method was previously invoked. This callback is invoked on the
44          * calling thread.
45          */
onPreExecute()46         default void onPreExecute() {}
47 
48         /**
49          * Called if the operation fails while running. Will not be invoked in the event of a
50          * unchecked exception, which will propagate normally. This callback is invoked on the
51          * executor thread.
52          */
onFailure(Exception e)53         default void onFailure(Exception e) {}
54 
55         /**
56          * Called after the operation may have been run. Will always be invoked for every operation
57          * which has previously had {@link #onPreExecute()} invoked. Success implies that the
58          * operation was run to completion with no failures. If {@code success} is true, this
59          * callback will always be invoked on the executor thread. If {@code success} is false, this
60          * callback may be invoked on the calling thread or executor thread.
61          */
onPostExecute(boolean success)62         default void onPostExecute(boolean success) {}
63 
64         /**
65          * Will always be called once for every operation submitted to
66          * {@link #executeSafely(Executor, Supplier, ListenerOperation)}, no matter if the operation
67          * was run or not. This method is always invoked last, after every other callback. Success
68          * implies that the operation was run to completion with no failures. If {@code success}
69          * is true, this callback will always be invoked on the executor thread. If {@code success}
70          * is false, this callback may be invoked on the calling thread or executor thread.
71          */
onComplete(boolean success)72         default void onComplete(boolean success) {}
73     }
74 
75     /**
76      * An callback for listener operation failure.
77      *
78      * @param <TListenerOperation> listener operation type
79      */
80     interface FailureCallback<TListenerOperation extends ListenerOperation<?>> {
81 
82         /**
83          * Called if a listener operation fails while running with a checked exception. This
84          * callback is invoked on the executor thread.
85          */
onFailure(TListenerOperation operation, Exception exception)86         void onFailure(TListenerOperation operation, Exception exception);
87     }
88 
89     /**
90      * See {@link #executeSafely(Executor, Supplier, ListenerOperation, FailureCallback)}.
91      */
executeSafely(Executor executor, Supplier<TListener> listenerSupplier, @Nullable ListenerOperation<TListener> operation)92     default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
93             @Nullable ListenerOperation<TListener> operation) {
94         executeSafely(executor, listenerSupplier, operation, null);
95     }
96 
97     /**
98      * Executes the given listener operation on the given executor, using the provided listener
99      * supplier. If the supplier returns a null value, or a value during the operation that does not
100      * match the value prior to the operation, then the operation is considered canceled. If a null
101      * operation is supplied, nothing happens. If a failure callback is supplied, this will be
102      * invoked on the executor thread in the event a checked exception is thrown from the listener
103      * operation.
104      */
executeSafely( Executor executor, Supplier<TListener> listenerSupplier, @Nullable TListenerOperation operation, @Nullable FailureCallback<TListenerOperation> failureCallback)105     default <TListener, TListenerOperation extends ListenerOperation<TListener>> void executeSafely(
106             Executor executor, Supplier<TListener> listenerSupplier,
107             @Nullable TListenerOperation operation,
108             @Nullable FailureCallback<TListenerOperation> failureCallback) {
109         if (operation == null) {
110             return;
111         }
112 
113         TListener listener = listenerSupplier.get();
114         if (listener == null) {
115             return;
116         }
117 
118         boolean executing = false;
119         boolean preexecute = false;
120         try {
121             operation.onPreExecute();
122             preexecute = true;
123             executor.execute(() -> {
124                 boolean success = false;
125                 try {
126                     if (listener == listenerSupplier.get()) {
127                         operation.operate(listener);
128                         success = true;
129                     }
130                 } catch (Exception e) {
131                     if (e instanceof RuntimeException) {
132                         throw (RuntimeException) e;
133                     } else {
134                         operation.onFailure(e);
135                         if (failureCallback != null) {
136                             failureCallback.onFailure(operation, e);
137                         }
138                     }
139                 } finally {
140                     operation.onPostExecute(success);
141                     operation.onComplete(success);
142                 }
143             });
144             executing = true;
145         } finally {
146             if (!executing) {
147                 if (preexecute) {
148                     operation.onPostExecute(false);
149                 }
150                 operation.onComplete(false);
151             }
152         }
153     }
154 }
155