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