Using absolute URLs with Retrofit

Recently Square has released the Retrofit v2.0.0 BETA and it has a built-in support for dynamic URLs. Even though the Library is in Beta, based on what Jake Wharton told us in DroidCon NYC 2015, all the apis are stable and will not change. I'm personally adding it to my production so its up to you.

You will find the following links useful if you decide to do the upgrade:
Jake Wharton presentation @ DroidCon NYC 2015
A very good guide on the changes

In simple word, you can now use the api annotations (like @GET or @POST and others) without any path and then you pass in a @URL to your api method that the method will use to call.

----------------Retrofit 1.x

I figured out a nice way for doing this and would like to share it.

The trick is to use the dynamic URL as your End Point in the creation of RestAdapter and then have a empty path on your API interface.

Here is how I did it:

public RestAdapter getHostAdapter(String baseHost){
    RestAdapter restAdapter = new RestAdapter.Builder()
            .setEndpoint(baseHost)
            .setRequestInterceptor(requestInterceptor)
            .build();

    return restAdapter;
}

I build my restAdapter using this method and then I have this in my interface:
(this will not work if your URL has query parameters added to it. See next answer for solution to that case)

public interface General {
    @GET("/")
    void getSomething(Callback<SomeObject> callback);
}

and finally using them like this:

getHostAdapter("YOUR_DYNAMIC_URL").create(General.class)
    .getSomething(new Callback<SomeObject>(){
        ...
    })

Hope it helps.


In case that your URL has query parameters on it, the above solution will not work since it will add the '/' at the end of your base URL. for example if your URL is

https://www.google.com/?q=test

then the above solution will try to send the request to

https://www.google.com/?q=test/

which will fail because of mall format.

What we can do is one extra step and parsing the url. By parsing I mean just taking out all URL parameters and sending them in a QueryMap.

Here is how:

We should have the same structure describe above with a little change to our interface

public interface General {
    @GET("/")
    void getSomething(@QueryMap Map<String,String> queryMap, Callback<SomeObject> callback);
}

I just added a QueryMap to the above interface and now we can use this parser method:

public static void getSomething(@NonNull String urlString, @NonNull Callback<SomeObject> callback){
    Uri uri = Uri.parse(urlString);
    Set<String> queryParameterNames = uri.getQueryParameterNames();
    String host = uri.getHost();
    HashMap<String,String> queryMap = new HashMap<>();
    Iterator<String> iterator = queryParameterNames.iterator();

    while(iterator.hasNext()){
        String queryName = iterator.next();
        String queryParameter = uri.getQueryParameter(queryName);
        queryMap.put(queryName, queryParameter);
    }

    getHostAdapter(host)
        .create(General.class)
        .getSomething(queryMap, callback);
}

now you can call this method like this:

getSomething("https://www.google.com/?q=test");

Enjoy coding.

Note: QueryMap was added on Retrofit v1.4.0


I also need a path on my URL, so I did this:

    @GET("/{path}")
void getMatcherUrl(@Path(value = "path", encode = false) String path, @QueryMap Map<String, String> queryMap, RestCallback<MatcherResponse> matcherResponse);

/**
     * Need to create a custom method because i need to pass a absolute url to the retrofit client
     *
     * @param urlString
     * @param matcherResponse
     */
    public void getMatcherUrl(@NonNull String urlString, @NonNull RestCallback<MatcherResponse> matcherResponse) {
        Uri uri = Uri.parse(urlString);
        Set<String> queryParameterNames = uri.getQueryParameterNames();
        String host = uri.getHost();
        String path = (uri.getPath().startsWith("/")) ? uri.getPath().substring(1) : uri.getPath();
        HashMap<String, String> queryMap = new HashMap<>();
        Iterator<String> iterator = queryParameterNames.iterator();

        while (iterator.hasNext()) {
            String queryName = iterator.next();
            String queryParameter = uri.getQueryParameter(queryName);
            queryMap.put(queryName, queryParameter);
        }

        getApiCoreService(host)
                .getMatcherUrl(path, queryMap, matcherResponse);
    }

    public ApiCoreService getApiCoreService(String host) {
        if (StringUtils.isEmpty(host))
            this.endpoint = new RestEndpoint(RemoteConfigurationManager.getInstance().getApiCore(), "ApiCore");
        else
            this.endpoint = new RestEndpoint(host, "ApiCore");
        return apiCoreService;
    }