Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

HttpClient configuration #12423

Closed
wants to merge 1 commit into from
Closed

HttpClient configuration #12423

wants to merge 1 commit into from

Conversation

tobrun
Copy link
Member

@tobrun tobrun commented Jul 18, 2018

WIP:

This PR allows to exclude the okhttp dependency from our SDK and replace the http client integration with another. In the test app I'm now showcasing this with using ion instead of okhttp.

Notes:

  • I had to disable telemetry completely as I was hitting NoClassDefFoundError, stripping out okhttp also removes it from telemetry. We need an additional configuration handling this.
  • I had to introduce a collection to manage ongoing requests, with the old system this wasn't necessary but with this static approach we need additional bookkeeping.

Todo:

  • revisit naming
  • add javadoc
  • fix telemetry integration
  • remove ion sample integration from testapp
  • run large scale tests on AWS device farm to detect regressions

@tobrun tobrun added the Android Mapbox Maps SDK for Android label Jul 18, 2018
@tobrun tobrun added this to the android-v6.4.0 milestone Jul 18, 2018
@tobrun tobrun self-assigned this Jul 18, 2018
@zugaldia
Copy link
Member

I had to disable telemetry completely as I was hitting NoClassDefFoundError, stripping out okhttp also removes it from telemetry. We need an additional configuration handling this.

cc: @electrostat for coordination.

@tobrun tobrun force-pushed the tvn-configurable-httpclient branch from 6f06a7b to 599f37b Compare July 18, 2018 14:04
Copy link
Member

@LukasPaczos LukasPaczos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing work so far!

Idea - how about using generics on the calls/requests list, managing that in the abstract 'HttpRequest` and only requesting call creation and cancellation from the child?

currentCall = calls.get(key);
if (call.equals(currentCall)) {
calls.delete(key);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can safely return here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, also updated code block to:
Which should also be faster as it avoids using get(key)

      synchronized (calls) {
        Call currentCall;
        for (int i = 0; i < calls.size(); i++) {
          currentCall = calls.valueAt(i);
          if (call.equals(currentCall)) {
            calls.delete(calls.keyAt(i));
            return;
          }
        }
      }

Request.Builder builder = new Request.Builder()
.url(resourceUrl)
.tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE))
.addHeader("User-Agent", getUserAgent());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need lazy init here instead of initialization on the object creation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was a remains of the old implementation, updated to create this inline in the class field.

mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(MapboxMap mapboxMap) {
mapboxMap.getUiSettings().setCompassFadeFacingNorth(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes in SimpleMapActivity.java seem unnecessary.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be removed, that was a test for a different issue


public abstract void cancelRequest(long nativePtr);

public static void execute(NativeHttpRequest httpRequest, long nativePtr, String resourceUrl,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be package-private?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it can, great catch

static final int PERMANENT_ERROR = 2;

public static synchronized void setHttpRequest(@Nullable HttpRequest requestImpl) {
httpRequest = requestImpl;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to invoke a cancelAll method here because we will lose the reference to the calls when the request impl is changed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of this PR is to be able exclude okhttp, a cancelAll invocation in this code path on the OkHttpRequest will result in the initiating OkHttpRequest and the classloader will hit a NoClassDefFoundError.

imo switching out httpclients on the fly isn't a supported use-case. You either use the default okhttp implementation or you provide an alternate implementation before requesting any resources.

I'm down with different suggestions to tackle this but don't think there is much we can do other than documenting this clearly in the javadoc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, let's document it extensively then.

What about throwing an exception when resources were already requested and a user wants to switch the implementation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

getInstance().executeRequest(httpRequest, nativePtr, resourceUrl, etag, modified);
}

public static void cancel(long nativePtr) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be package-private?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it can, great catch

@electrostat
Copy link

@tobrun @zugaldia Regarding Telemetry, we need a non-okhttp solution (similar to what you have here) to perform requests and allow that to be parent sdk settable? Am i correct in that assumption? If so, I can start working on a solution for this.

@tobrun
Copy link
Member Author

tobrun commented Jul 19, 2018

@LukasPaczos

Idea - how about using generics on the calls/requests list, managing that in the abstract 'HttpRequest` and only requesting call creation and cancellation from the child?

I liked this idea but isn't possible due to the call list needing to be static (to be shared between instances of HttpRequest) and you can't reference the generic type in a static context. Please point out if I'm missing something.

@tobrun tobrun force-pushed the tvn-configurable-httpclient branch from 599f37b to a7de858 Compare July 19, 2018 10:01
@LukasPaczos
Copy link
Member

Yes, you are right, this would require an additional, non-static container for all the requests. Not sure if this is worth pursuing as the current setup allows more flexibility at the cost of complexity. I feel like this is a good tradeoff in this case.

log(Log.ERROR, String.format("[HTTP] Unable to parse resourceUrl %s", resourceUrl));
}

final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic like computing the host is not specific to OkHttp so I think it should be available in a delegate.


private static synchronized HttpRequest getInstance() {
if (httpRequest == null) {
httpRequest = new OkHttpRequest();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of the goal of having modules is that OkHttp does not need to be compiled into the APK. I think there should be a DefaultModules.java file that sets all the default modules. Then for someone who wants to have different modules, they just leave DefaultModules out of the compilation and make their own MyModules.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm aware of two techniques to remove code from an aar/binary:

  • gradle exclusion
  • proguard

Please elaborate how they just leave DefaultModules out of the compilation and make their own MyModules.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking an option in the build rules to leave DefaultModules.java out - so I think that would be gradle exclusion though I'm not actually familiar with gradle.

@tobrun
Copy link
Member Author

tobrun commented Jul 20, 2018

Moving forward in #12449 where we opt to use a compile time configuration vs runtime.

@tobrun tobrun closed this Jul 20, 2018
@tobrun tobrun deleted the tvn-configurable-httpclient branch July 20, 2018 16:55
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Android Mapbox Maps SDK for Android
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants