[android] Use URI builder in Android or create URL with variables

I'm developing an Android app. I need to build a URI for my app to make an API request. Unless there's another way to put a variable in a URI, this is the easiest way I've found. I found that you need to use Uri.Builder, but I'm not quite sure how to. My url is:

http://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=[redacted]&mapid=value 

My scheme is http, authority is lapi.transitchicago.com, path is /api/1.0, path segment(s) is ttarrivals.aspx, and query string is key=[redacted]&mapid=value.

My code is below:

Intent intent = getIntent();
String value = intent.getExtras().getString("value");
Uri.Builder builder = new Uri.Builder();
builder.scheme("http")
    .authority("www.lapi.transitchicago.com")
    .appendPath("api")
    .appendPath("1.0")
    .appendPath("ttarrivals.aspx")
    .appendQueryParameter("key", "[redacted]")
    .appendQueryParameter("mapid", value);

I understand that I can do URI.add, but how do I integrate it into the Uri.Builder? Should I add everything like URI.add(scheme), URI.add(authority) and so on? Or is that not the way to do it? Also, is there any other easier way to add a variable to a URI/URL?

This question is related to android uri uribuilder

The answer is


Excellent answer from above turned into a simple utility method.

private Uri buildURI(String url, Map<String, String> params) {

    // build url with parameters.
    Uri.Builder builder = Uri.parse(url).buildUpon();
    for (Map.Entry<String, String> entry : params.entrySet()) {
        builder.appendQueryParameter(entry.getKey(), entry.getValue());
    }

    return builder.build();
}

There is another way of using Uri and we can achieve the same goal

http://api.example.org/data/2.5/forecast/daily?q=94043&mode=json&units=metric&cnt=7

To build the Uri you can use this:

final String FORECAST_BASE_URL = 
    "http://api.example.org/data/2.5/forecast/daily?";
final String QUERY_PARAM = "q";
final String FORMAT_PARAM = "mode";
final String UNITS_PARAM = "units";
final String DAYS_PARAM = "cnt";

You can declare all this the above way or even inside the Uri.parse() and appendQueryParameter()

Uri builtUri = Uri.parse(FORECAST_BASE_URL)
    .buildUpon()
    .appendQueryParameter(QUERY_PARAM, params[0])
    .appendQueryParameter(FORMAT_PARAM, "json")
    .appendQueryParameter(UNITS_PARAM, "metric")
    .appendQueryParameter(DAYS_PARAM, Integer.toString(7))
    .build();

At last

URL url = new URL(builtUri.toString());

for the example in the second Answer I used this technique for the same URL

http://api.example.org/data/2.5/forecast/daily?q=94043&mode=json&units=metric&cnt=7

Uri.Builder builder = new Uri.Builder();
            builder.scheme("https")
                    .authority("api.openweathermap.org")
                    .appendPath("data")
                    .appendPath("2.5")
                    .appendPath("forecast")
                    .appendPath("daily")
                    .appendQueryParameter("q", params[0])
                    .appendQueryParameter("mode", "json")
                    .appendQueryParameter("units", "metric")
                    .appendQueryParameter("cnt", "7")
                    .appendQueryParameter("APPID", BuildConfig.OPEN_WEATHER_MAP_API_KEY);

then after finish building it get it as URL like this

URL url = new URL(builder.build().toString());

and open a connection

  HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

and if link is simple like location uri, for example

geo:0,0?q=29203

Uri geoLocation = Uri.parse("geo:0,0?").buildUpon()
            .appendQueryParameter("q",29203).build();

here is a good way to explain it:

there are two forms of the URI

1 - Builder(ready to be modified, not ready to be used)

2 - Built(not ready to be modified, ready to be used )

You can create a builder by

Uri.Builder builder = new Uri.Builder();

this gonna return a Builder ready to be modified like this:-

builder.scheme("https");
builder.authority("api.github.com");
builder.appendPath("search");
builder.appendPath("repositories");
builder.appendQueryParameter(PARAMETER_QUERY,parameterValue);

but to use it you have to build it first

retrun builder.build();

or however you gonna use it. and then you have built that is already built for you, ready to use but cannot be modified.

Uri built = Uri.parse("your URI goes here");

this is ready to use but if you want to modify it you need to buildUpon()

Uri built = Uri.parse("Your URI goes here")
           .buildUpon(); //now it's ready to be modified
           .buildUpon()
           .appendQueryParameter(QUERY_PARAMATER, parameterValue) 
           //any modification you want to make goes here
           .build(); // you have to build it back cause you are storing it 
                     // as Uri not Uri.builder

now every time you want to modify it you need to buildUpon() and in the end build().

so Uri.Builder is a Builder type that store a Builder in it. Uri is a Built type that store an already built URI in it.

new Uri.Builder(); rerurns a Builder. Uri.parse("your URI goes here") returns a Built.

and with build() you can change it from Builder to Built. buildUpon() you can change it from Built to Builder. Here is what you can do

Uri.Builder builder = Uri.parse("URL").buildUpon();
// here you created a builder, made an already built URI with Uri.parse
// and then change it to builder with buildUpon();
Uri built = builder.build();
//when you want to change your URI, change Builder 
//when you want to use your URI, use Built

and also the opposite:-

Uri built = new Uri.Builder().build();
// here you created a reference to a built URI
// made a builder with new Uri.Builder() and then change it to a built with 
// built();
Uri.Builder builder = built.buildUpon();

hope my answer helped :) <3


You can do that with lambda expressions;

    private static final String BASE_URL = "http://api.example.org/data/2.5/forecast/daily";

    private String getBaseUrl(Map<String, String> params) {
        final Uri.Builder builder = Uri.parse(BASE_URL).buildUpon();
        params.entrySet().forEach(entry -> builder.appendQueryParameter(entry.getKey(), entry.getValue()));
        return builder.build().toString();
    }

and you can create params like that;

    Map<String, String> params = new HashMap<String, String>();
    params.put("zip", "94043,us");
    params.put("units", "metric");

Btw. If you will face any issue like “lambda expressions not supported at this language level”, please check this URL;

https://stackoverflow.com/a/22704620/2057154


Best answer: https://stackoverflow.com/a/19168199/413127

Example for

 http://api.example.org/data/2.5/forecast/daily?q=94043&mode=json&units=metric&cnt=7

Now with Kotlin

 val myUrl = Uri.Builder().apply {
        scheme("https")
        authority("www.myawesomesite.com")
        appendPath("turtles")
        appendPath("types")
        appendQueryParameter("type", "1")
        appendQueryParameter("sort", "relevance")
        fragment("section-name")
        build()            
    }.toString()

Using appendEncodePath() could save you multiple lines than appendPath(), the following code snippet builds up this url: http://api.openweathermap.org/data/2.5/forecast/daily?zip=94043

Uri.Builder urlBuilder = new Uri.Builder();
urlBuilder.scheme("http");
urlBuilder.authority("api.openweathermap.org");
urlBuilder.appendEncodedPath("data/2.5/forecast/daily");
urlBuilder.appendQueryParameter("zip", "94043,us");
URL url = new URL(urlBuilder.build().toString());