From e05a8b677ca717e1d63dae566adb37da0032dda0 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Mon, 21 Mar 2022 09:59:46 -0700 Subject: [PATCH] android: Use Provider to construct OkHttpChannelBuilder Doing any reflection on OkHttpChannelBuilder requires that all methods can have their arguments resolved. We'd like to make okhttp an optional dependency (to support okhttp 2 and 3/4 simultaneously). But making okhttp optional means we can no longer construct OkHttpChannelBuilder reflectively. We swap to the Provider that doesn't have this problem. See #8971. Note that ManagedChannelProvider itself only exposes its methods as protected, so they wouldn't be accessible. However OkHttpChannelProvider has its methods public. It is an open question of whether ManagedChannelProvider's methods should become public, but in any case we can hide a public OkHttpChannelProvider inside a package-private class so it is only accessable via reflection. So this code assuming public methods doesn't prevent future implementation hiding. --- .../grpc/android/AndroidChannelBuilder.java | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java b/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java index dadab1c830c9..6da80fa77111 100644 --- a/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java +++ b/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java @@ -55,14 +55,33 @@ public final class AndroidChannelBuilder extends ForwardingChannelBuilder OKHTTP_CHANNEL_BUILDER_CLASS = findOkHttp(); + @Nullable private static final Object OKHTTP_CHANNEL_PROVIDER = findOkHttp(); - private static Class findOkHttp() { + private static Object findOkHttp() { + Class klass; try { - return Class.forName("io.grpc.okhttp.OkHttpChannelBuilder"); + klass = Class.forName("io.grpc.okhttp.OkHttpChannelProvider"); } catch (ClassNotFoundException e) { + Log.w(LOG_TAG, "Failed to find OkHttpChannelProvider", e); return null; } + Object provider; + try { + provider = klass.getConstructor().newInstance(); + } catch (Exception e) { + Log.w(LOG_TAG, "Failed constructing OkHttpChannelProvider", e); + return null; + } + try { + if (!(Boolean) klass.getMethod("isAvailable").invoke(provider)) { + Log.w(LOG_TAG, "OkHttpChannelProvider.isAvailable() returned false"); + return null; + } + } catch (Exception e) { + Log.w(LOG_TAG, "Failed checking OkHttpChannelProvider.isAvailable()", e); + return null; + } + return provider; } private final ManagedChannelBuilder delegateBuilder; @@ -113,15 +132,15 @@ public static AndroidChannelBuilder usingBuilder(ManagedChannelBuilder builde } private AndroidChannelBuilder(String target) { - if (OKHTTP_CHANNEL_BUILDER_CLASS == null) { - throw new UnsupportedOperationException("No ManagedChannelBuilder found on the classpath"); + if (OKHTTP_CHANNEL_PROVIDER == null) { + throw new UnsupportedOperationException("Unable to load OkHttpChannelProvider"); } try { delegateBuilder = (ManagedChannelBuilder) - OKHTTP_CHANNEL_BUILDER_CLASS - .getMethod("forTarget", String.class) - .invoke(null, target); + OKHTTP_CHANNEL_PROVIDER.getClass() + .getMethod("builderForTarget", String.class) + .invoke(OKHTTP_CHANNEL_PROVIDER, target); } catch (Exception e) { throw new RuntimeException("Failed to create ManagedChannelBuilder", e); }