Given an absolute Uri
and a relative Uri
or relative path, how do you get the absolute Uri
pointing to the relative location?
For example, suppose we have the Uri
for file:///android_asset/dir
, pointing to a location in our assets. Further suppose that elsewhere, we have a relative path of /foo
. The absolute Uri
for that relative path should be file:///android_asset/foo
. Is there something on Uri
, or elsewhere in the Android SDK, that I am missing that give me that result Uri
?
Uri.withAppendedPath()
is not the answer, as all it seems to do is handle trailing directory separators:
Uri abs=Uri.parse("file:///android_asset/");
Uri rel=Uri.withAppendedPath(abs, "/foo");
Assert.assertEquals("file:///android_asset/foo", rel.toString());
// actually returns file:///android_asset//foo
Uri abs2=Uri.parse("file:///android_asset/dir");
Uri rel2=Uri.withAppendedPath(abs2, "/foo");
Assert.assertEquals("file:///android_asset/foo", rel2.toString());
// actually returns file:///android_asset/dir//foo
Uri.Builder
, via buildUpon()
on Uri
, is not an improvement:
Uri rel3=abs.buildUpon().appendPath("/foo").build();
Assert.assertEquals("file:///android_asset/foo", rel3.toString());
// actually returns file:///android_asset/%2Ffoo
Uri rel4=abs.buildUpon().appendEncodedPath("/foo").build();
Assert.assertEquals("file:///android_asset/foo", rel4.toString());
// actually returns file:///android_asset//foo
In a pinch I can try using java.net.URL
and its URL(URL context, String spec)
constructor, or just roll some code for it, but I was hoping to stay in the realm of Android Uri
values if possible, just for any quirks differentiating URL
and Uri
.
Android doesn't make this easy.
In my case, I had to take a base url that may or may not have an included path:
http://www.myurl.com/myapi/
...and append a REST API method path, like:
api/where/agencies-with-coverage.json
...to produce the entire url:
http://www.myurl.com/myapi/api/where/agencies-with-coverage.json
Here's how I did it (compiled from various methods within the app - there may be a simpler way of doing this):
String baseUrlString = "http://www.myurl.com/myapi/";
String pathString = "api/where/agencies-with-coverage.json";
Uri.Builder builder = new Uri.Builder();
builder.path(pathString);
Uri baseUrl = Uri.parse(baseUrlString);
// Copy partial path (if one exists) from the base URL
Uri.Builder path = new Uri.Builder();
path.encodedPath(baseUrl.getEncodedPath());
// Then, tack on the rest of the REST API method path
path.appendEncodedPath(builder.build().getPath());
// Finally, overwrite builder with the full URL
builder.scheme(baseUrl.getScheme());
builder.encodedAuthority(baseUrl.getEncodedAuthority());
builder.encodedPath(path.build().getEncodedPath());
// Final Uri
Uri finalUri = builder.build();
In my case, the Builder classes for the API client code assembled the path
prior to combining it with the baseURL
, so that explains the order of things above.
If I've pulled together the above code correctly, it should handle port numbers as well as spaces in the URL string.
I pulled this source code from the OneBusAway Android app, specifically the ObaContext
class. Note that this code on Github also handles the additional case where the user typed in a baseUrl
(String serverName = Application.get().getCustomApiUrl()
in the above code) that should override the region base URL (mRegion.getObaBaseUrl()
), and the user-entered URL may not have http://
in front of it.
The unit tests that pass for the above code on Github, including cases where port numbers and spaces are included in the baseUrl
and path
, and the leading/trailing /
may or may not be included, are here on Github. Related issues on Github where I was banging my head on the wall to try and get this all to work - 72, 126, 132.
I haven't tried this with non-HTTP URIs, but I believe it may work more generally.
There is an equivalent to urllib.parse.urljoin
(Python) in Android URI.create(baseUrl).resolve(path)
.
import java.net.URI
URI.create("https://dmn92m25mtw4z.cloudfront.net/helpvids/f3_4/hls_480/480.m3u8")
.resolve("0.ts")
// output:
// https://dmn92m25mtw4z.cloudfront.net/helpvids/f3_4/hls_480/0.ts
Sean Barbeau answer returns wrong URL, it's just appending the 0.ts
to the url.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With