2020import java .util .ArrayList ;
2121import java .util .Collections ;
2222import java .util .Comparator ;
23+ import java .util .Iterator ;
2324import java .util .List ;
25+ import java .util .ListIterator ;
2426import java .util .ServiceConfigurationError ;
2527import java .util .ServiceLoader ;
2628
@@ -35,19 +37,53 @@ private ServiceProviders() {
3537 * If this is Android, returns all available implementations in {@code hardcoded}.
3638 * The list is sorted in descending priority order.
3739 */
40+ //@Deprecated
3841 public static <T > List <T > loadAll (
3942 Class <T > klass ,
4043 Iterable <Class <?>> hardcoded ,
4144 ClassLoader cl ,
4245 final PriorityAccessor <T > priorityAccessor ) {
43- Iterable <T > candidates ;
44- if (isAndroid (cl )) {
45- candidates = getCandidatesViaHardCoded (klass , hardcoded );
46+ return loadAll (klass , ServiceLoader .load (klass , cl ).iterator (), hardcoded , priorityAccessor );
47+ }
48+
49+ /**
50+ * If this is not Android, returns all available implementations discovered via
51+ * {@link ServiceLoader}.
52+ * If this is Android, returns all available implementations in {@code hardcoded}.
53+ * The list is sorted in descending priority order.
54+ *
55+ * <p>{@code serviceLoader} should be created with {@code ServiceLoader.load(MyClass.class,
56+ * MyClass.class.getClassLoader()).iterator()} in order to be detected by R8 so that R8 full mode
57+ * will keep the constructors for the provider classes.
58+ */
59+ public static <T > List <T > loadAll (
60+ Class <T > klass ,
61+ Iterator <T > serviceLoader ,
62+ Iterable <Class <?>> hardcoded ,
63+ final PriorityAccessor <T > priorityAccessor ) {
64+ Iterator <T > candidates ;
65+ if (serviceLoader instanceof ListIterator ) {
66+ // A rewriting tool has replaced the ServiceLoader with a List of some sort (R8 uses
67+ // ArrayList, AppReduce uses singletonList). We prefer to use such iterators on Android as
68+ // they won't need reflection like the hard-coded list does. In addition, the provider
69+ // instances will have already been created, so it seems we should use them.
70+ //
71+ // R8: https://r8.googlesource.com/r8/+/490bc53d9310d4cc2a5084c05df4aadaec8c885d/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
72+ // AppReduce: service_loader_pass.cc
73+ candidates = serviceLoader ;
74+ } else if (isAndroid (klass .getClassLoader ())) {
75+ // Avoid getResource() on Android, which must read from a zip which uses a lot of memory
76+ candidates = getCandidatesViaHardCoded (klass , hardcoded ).iterator ();
77+ } else if (!serviceLoader .hasNext ()) {
78+ // Attempt to load using the context class loader and ServiceLoader.
79+ // This allows frameworks like http://aries.apache.org/modules/spi-fly.html to plug in.
80+ candidates = ServiceLoader .load (klass ).iterator ();
4681 } else {
47- candidates = getCandidatesViaServiceLoader ( klass , cl ) ;
82+ candidates = serviceLoader ;
4883 }
4984 List <T > list = new ArrayList <>();
50- for (T current : candidates ) {
85+ while (candidates .hasNext ()) {
86+ T current = candidates .next ();
5187 if (!priorityAccessor .isAvailable (current )) {
5288 continue ;
5389 }
@@ -84,15 +120,14 @@ static boolean isAndroid(ClassLoader cl) {
84120 }
85121
86122 /**
87- * Loads service providers for the {@code klass} service using {@link ServiceLoader}.
123+ * For testing only: Loads service providers for the {@code klass} service using {@link
124+ * ServiceLoader}. Does not support spi-fly and related tricks.
88125 */
89126 @ VisibleForTesting
90127 public static <T > Iterable <T > getCandidatesViaServiceLoader (Class <T > klass , ClassLoader cl ) {
91128 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.
94129 if (!i .iterator ().hasNext ()) {
95- i = ServiceLoader . load ( klass ) ;
130+ return null ;
96131 }
97132 return i ;
98133 }
0 commit comments