1717package io .grpc ;
1818
1919import com .google .common .annotations .VisibleForTesting ;
20+ import com .google .common .base .Supplier ;
2021import java .util .ArrayList ;
2122import java .util .Collections ;
2223import java .util .Comparator ;
24+ import java .util .Iterator ;
2325import java .util .List ;
26+ import java .util .ListIterator ;
2427import java .util .ServiceConfigurationError ;
2528import java .util .ServiceLoader ;
2629
@@ -34,20 +37,39 @@ private ServiceProviders() {
3437 * {@link ServiceLoader}.
3538 * If this is Android, returns all available implementations in {@code hardcoded}.
3639 * The list is sorted in descending priority order.
40+ *
41+ * <p>{@code serviceLoader} should be created with {@code ServiceLoader.load(MyClass.class,
42+ * MyClass.class.getClassLoader()).iterator()} in order to be detected by R8 so that R8 full mode
43+ * will keep the constructors for the provider classes.
3744 */
3845 public static <T > List <T > loadAll (
3946 Class <T > klass ,
40- Iterable < Class <?>> hardcoded ,
41- ClassLoader cl ,
47+ Iterator < T > serviceLoader ,
48+ Supplier < Iterable < Class <?>>> hardcoded ,
4249 final PriorityAccessor <T > priorityAccessor ) {
43- Iterable <T > candidates ;
44- if (isAndroid (cl )) {
45- candidates = getCandidatesViaHardCoded (klass , hardcoded );
50+ Iterator <T > candidates ;
51+ if (serviceLoader instanceof ListIterator ) {
52+ // A rewriting tool has replaced the ServiceLoader with a List of some sort (R8 uses
53+ // ArrayList, AppReduce uses singletonList). We prefer to use such iterators on Android as
54+ // they won't need reflection like the hard-coded list does. In addition, the provider
55+ // instances will have already been created, so it seems we should use them.
56+ //
57+ // R8: https://r8.googlesource.com/r8/+/490bc53d9310d4cc2a5084c05df4aadaec8c885d/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
58+ // AppReduce: service_loader_pass.cc
59+ candidates = serviceLoader ;
60+ } else if (isAndroid (klass .getClassLoader ())) {
61+ // Avoid getResource() on Android, which must read from a zip which uses a lot of memory
62+ candidates = getCandidatesViaHardCoded (klass , hardcoded .get ()).iterator ();
63+ } else if (!serviceLoader .hasNext ()) {
64+ // Attempt to load using the context class loader and ServiceLoader.
65+ // This allows frameworks like http://aries.apache.org/modules/spi-fly.html to plug in.
66+ candidates = ServiceLoader .load (klass ).iterator ();
4667 } else {
47- candidates = getCandidatesViaServiceLoader ( klass , cl ) ;
68+ candidates = serviceLoader ;
4869 }
4970 List <T > list = new ArrayList <>();
50- for (T current : candidates ) {
71+ while (candidates .hasNext ()) {
72+ T current = candidates .next ();
5173 if (!priorityAccessor .isAvailable (current )) {
5274 continue ;
5375 }
@@ -84,15 +106,14 @@ static boolean isAndroid(ClassLoader cl) {
84106 }
85107
86108 /**
87- * Loads service providers for the {@code klass} service using {@link ServiceLoader}.
109+ * For testing only: Loads service providers for the {@code klass} service using {@link
110+ * ServiceLoader}. Does not support spi-fly and related tricks.
88111 */
89112 @ VisibleForTesting
90113 public static <T > Iterable <T > getCandidatesViaServiceLoader (Class <T > klass , ClassLoader cl ) {
91114 Iterable <T > i = ServiceLoader .load (klass , cl );
92- // Attempt to load using the context class loader and ServiceLoader.
93- // This allows frameworks like http://aries.apache.org/modules/spi-fly.html to plug in.
94115 if (!i .iterator ().hasNext ()) {
95- i = ServiceLoader . load ( klass ) ;
116+ return null ;
96117 }
97118 return i ;
98119 }
0 commit comments