2015-09-15 20 views
10

Dato un assoluto Uri e un relativo Uri o percorso relativo, come si ottiene l'assoluto Uri indicando la posizione relativa?Come si risolve un Uri relativo?

Ad esempio, supponiamo di avere lo Uri per file:///android_asset/dir, che punta a una posizione nelle nostre risorse. Supponiamo inoltre che altrove, abbiamo un percorso relativo di /foo. L'assoluto Uri per quel percorso relativo deve essere file:///android_asset/foo. C'è qualcosa su Uri o altrove nell'SDK di Android, che mi manca che mi dia quel risultato Uri?

Uri.withAppendedPath() non è la risposta, come tutto sembra fare è gestire finali separatori di directory:

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, tramite buildUpon() su Uri, non è un miglioramento:

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 un pizzico Posso provare a usare java.net.URL e il suo costruttore URL(URL context, String spec), o semplicemente rotolare del codice per questo, ma speravo di rimanere nel regno di Android Uri valori se possibile, solo per eventuali stranezze che differiscono da URL e da Uri.

+0

non è necessario aggiungere la barra per appendPath: lo fa per te. Ho il mio metodo getBaseUriBuilder per ottenere Uri.Builder con schema + root. Quindi tutto ciò che serve è aggiungere il percorso e interrogare i parametri ... –

+0

"Quindi tutto ciò che serve è aggiungere il percorso" - ci sono molte possibili strutture di percorso relative. '/ foo' è solo uno. C'è anche 'pippo','./Foo', '../ pippo',' ../../ foo/barra', e così via. Molti useranno parte del percorso dell'assoluto 'Uri', altri no (ad es. '/ Foo') no. Sto cercando di gestire tutte le situazioni, se possibile. – CommonsWare

+0

vedi Costruttori di file: 'File (dir file, nome stringa)' e/o 'File (stringa dirPath, nome stringa)' e 'File # getCanonicalPath()' – pskink

risposta

4

Android non lo rende facile.

Nel mio caso, ho dovuto prendere un URL di base che possono o non possono avere un percorso incluso:

http://www.myurl.com/myapi/

... e aggiungere un metodo del percorso API REST, come:

api/where/agencies-with-coverage.json

... per produrre l'intero URL:

http://www.myurl.com/myapi/api/where/agencies-with-coverage.json

Ecco come ho fatto (compilato da vari metodi all'interno della app - ci può essere un modo più semplice per farlo):

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(); 

Nel mio caso, le classi Builder per il codice del client API assemblate la path prima di abbinarlo allo baseURL, in modo da spiegare l'ordine delle cose sopra.

Se ho riunito correttamente il codice precedente, dovrebbe gestire i numeri di porta e gli spazi nella stringa dell'URL.

Ho estratto questo codice sorgente dallo OneBusAway Android app, in particolare dallo ObaContext class. Si noti che questo codice su Github gestisce anche il caso aggiuntivo in cui l'utente digitava un baseUrl (String serverName = Application.get().getCustomApiUrl() nel codice precedente) che dovrebbe ignorare l'URL di base della regione (mRegion.getObaBaseUrl()) e l'URL inserito dall'utente potrebbe non avere http:// di fronte ad esso .

I test di unità che passano per il codice precedente su Github, compresi i casi in cui numeri di porta e gli spazi sono incluse nel baseUrl e path e leader/uscita / può o non può essere inclusa, sono here on Github. Problemi correlati su Github in cui stavo sbattendo la testa contro il muro per cercare di far funzionare tutto - 72, 126, 132.

Non ho provato questo con URI non HTTP, ma credo che possa funzionare più in generale.

+0

Whoops, ha avuto un refuso nel codice iniziale, riparato ora. –

+0

E, risolto un altro errore di battitura. –

+0

Tecnicamente, questo non risponde alla domanda. Quello che stai descrivendo è quello che ho chiamato "basta rotolare del codice per esso" nella domanda. Da questo e dai commenti, deduco che non c'è una semplice soluzione in-SDK che mi sia sfuggita. Quindi, ti concederò la taglia. Grazie per l'aiuto! – CommonsWare