In my WebView-based app, when the app accesses http://mobile.nytimes.com/ my WebViewClient receives 6-7 onPageFinished(), the first 3-4 with WebView.getUrl() returning the original URL (http://mobile.nytimes.com/) but then it returns "data:text/html" for the rest.
Apparently, data:text/html is a valid URI. Also see https://url.spec.whatwg.org/#fetch-scheme .
However, in my code, I need to instantiate a URL from WebView's url like this:
new URL(myWebView.getUrl());
and unfortunately this throws a MalformedURLException when WebView.getUrl() returns 'data:text/html'.
Is there a way to convert the 'data:text/html' string to a valid URL string so that new URL() will not throw the MalformedURLException?
As you wrote data: is a valid URI not an URL. https://en.wikipedia.org/wiki/Data_URI_scheme
Related
In WebHistoryItem's documentation says:
URL
Return the url of this history item. The url is the base url of this history item. See getTargetUrl() for the url that is the actual target of this history item.
Original URL
Return the original url of this history item. This was the requested url, the final url may be different as there might have been redirects while loading the site.
It makes me confused enough. What I want to know is, what are differences between them? Can you give me some examples?
Thanks in advance.
Well, after I did some research I found differences between them. Original URL is a URL before redirected. Suppose that you opening a shorten link like this https://bit•ly/sG98iK, then you will be redirected to a web page with the following URL https://example.com/android/tutorial/webview.html. We call https://bit•ly/sG98iK as original URL, and https://example.com/android/tutorial/webview.html as URL, i.e. the URL after redirected.
Notice that original URL is nullable.
When you load a website using an URL, the website may redirect to another website with a different URL.
Assume the scenario is like this: Website 1 (URL1) -> Website2 (URL2) -> Website3 (URL3)
then, getUrl will give you URL3. Meanwhile getOriginalURL will return URL1
Using Glide v4 and OkHttp3, how can I detect a redirection and load another url when it happens?
My usecase: I use the Glide v4 library with OkHttp3 to download pictures in my app. Sometimes when a picture is not available, a redirection is performed by the server to provide another picture instead of the one I originaly wanted. I can see it in my browser because when I request url A, I finally land on url B with the second picture. When that happens I want to instead load url C that is derived from url A (so not a static url).
At the moment I can detect the redirection using an OkHttp3 Interceptor:
public Response intercept(#NonNull Chain chain) throws IOException {
String requestUrl = chain.request().url().toString();
Response response = chain.proceed(chain.request());
String responseUrl = response.request().url().toString();
boolean redirect = !requestUrl.equals(responseUrl);
if (redirect) {
Timber.d("Detected redirection");
}
return response;
}
but then I don't know how to cleanly load url C. I don't see how I can load another url in the interceptor, and if I throw an IOException to handle the error later in a Glide RequestListener it will just result in a GlideException so I can't determine why it was throw.
OkHttp should be automatically redirecting by default and loading the final content. See OkHttpClient.Builder.followRedirects.
https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.Builder.html#followRedirects-boolean-
My own testing suggests this is working, e.g.
$ oksocial 'https://httpbin.org/redirect-to?url=https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg'
For detecting it, I assume your interceptor is registered via addInterceptor instead of addNetworkInterceptor. Is that correct?
It is also worth ensuring the redirect is via a 30X response and not via a HTML refresh.
The solution I ended up with is pretty ugly: the request interceptor I showed in the question raise an IOException when a redirection is detected. I will then use a GlideListener to detect the error, and load the url C. The ugly part is that in the GlideListener I can't determine the cause of the error, it can be a redirection but it can also be a network error or anything else.
A cleaner version of this solution can probably be achieved with a custom OkHttpUrlLoader but it's too much code for my simple usecase.
This is my solution in Glide v4 / OkHttp3
String redirect = response.header("location");
if(redirect != null && !request.url().toString().equals(redirect)) {
// if redirected your code here
}
After making a second network request using Volley, I always get this error. It doesn't seem to matter what the url I put in is. Volley always claims it is malformed.
08-04 20:16:26.885 14453-14470/com.thredup.android E/Volley﹕ [994] NetworkDispatcher.run: Unhandled exception java.lang.RuntimeException: Bad URL
java.lang.RuntimeException: Bad URL
at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:127)
at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:110)
Caused by: java.net.MalformedURLException: Protocol not found:
at java.net.URL.<init>(URL.java:176)
at java.net.URL.<init>(URL.java:125)
at com.android.volley.toolbox.HurlStack.performRequest(HurlStack.java:101)
at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:93)
Investigating further, I put a couple logs in HurlStack. In
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders),
the request that fails is REQUEST [ ] 0x0 LOW 26."
Thus, line 101 of HurlStack : URL parsedUrl = new URL(url);
fails with an empty url (request.getUrl() is empty).
I am using OkHttpStack (extending HurlStack).
Any ideas on what could be causing this?
actually the problem is with your url not with the volley. Your Url is not a URI. There is no protocol component in it. It needs http:// or whatever other protocol you intend. If you have the http in your url make sure where it is correctly formed or not.
For example your url formation should be like this
public String URL = "http://www.w3schools.com/webservices/tempconvert.asmx";
Don’t forget to read the URL Specification and make sure the URL you are providing is valid.
Make Sure that you have passed the URL as the second parameter in JsonObjectRequest or StringRequest. I made the same mistake which produced the same error like what you faced.
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Method.POST, URL, null, ResponseListener, ErrorListener);
Use
http://
OR
https://
prefix to your URL
example:
example.com/information.json
write it as
http://example.com/information.json
this Exception occur while you are hitting an Url that is not prefixed withhttp// or https//.so check there is therehttp// is with your URL.
you can get more information here and see these links
I have an app that heavily uses the Android WebView to display my custom HTML content. The latest Android update (4.4/Kit-Kat/SDK-19) featured a redesigned WebView.
One of my users with a Nexus 5 reported a problem where some links cause the app to crash. I ran in the 4.4 emulator and debug into my WebViewClient's shouldOverrideUrlLoading() method. On all previously tested Android versions (2.2-4.3) the url String passed into the method had my custom url with "/" characters in it. In 4.4 the exact same link now has "\" characters in their place.
This doesn't make any sense to me. I load the HTML exactly the same, so somehow the new WebView converted all my slashes into backslashes.
Why does the new WebView do this?
Changes in URL handling are a known issue. Please see the migration guide for more detail.
The behaviour in this particular case will depend on what your base URL's scheme is, from what you're describing I'm guessing your base URL's scheme is "http(s)://" in which case the Chromium WebView performs URL normalization.
You might want to consider using the URI class to handle the discrepancy between the Classic and Chromium WebViews in this case.
I did more debugging and discovered I actually have the question reversed. Turns out the older versions of WebView did conversions of the URL, not the new one.
I load HTML with a format similar to this into a WebView:
link
I use the double back slashes as delimiters and parse the data later when the link is clicked. In older versions of WebView it converted my double backslash characters into forward slashes. It had been so long since I was in that code, I forgot I adjusted my code to use forward slashes rather than the backslashes in the original HTML.
The new version of WebView leaves my custom URL intact, giving me the exact same string as my original HTML. So turns out the old WebView is the problem not the new one.
The new WebView applies additional restrictions when requesting resources and resolving links that use a custom URL scheme. For example, if you implement callbacks such as shouldOverrideUrlLoading() or shouldInterceptRequest(), then WebView invokes them only for valid URLs.
If you are using a custom URL scheme or a base URL and notice that your app is receiving fewer calls to these callbacks or failing to load resources on Android 4.4, ensure that the requests specify valid URLs that conform to RFC 3986.
For example, the new WebView may not call your shouldOverrideUrlLoading() method for links like this:
Show Profile
The result of the user clicking such a link can vary:
If you loaded the page by calling loadData() or loadDataWithBaseURL() with an invalid or null base URL, then you will not receive the shouldOverrideUrlLoading() callback for this type of link on the page.
Note: When you use loadDataWithBaseURL() and the base URL is invalid or set null, all links in the content you are loading must be absolute.
If you loaded the page by calling loadUrl() or provided a valid base URL with loadDataWithBaseURL(), then you will receive the shouldOverrideUrlLoading() callback for this type of link on the page, but the URL you receive will be absolute, relative to the current page. For example, the URL you receive will be "http://www.example.com/showProfile" instead of just "showProfile".
Instead of using a simple string in a link as shown above, you can use a custom scheme such as the following:
Show Profile
You can then handle this URL in your shouldOverrideUrlLoading() method like this:
// The URL scheme should be non-hierarchical (no trailing slashes)
private static final String APP_SCHEME = "example-app:";
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith(APP_SCHEME)) {
urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
respondToData(urlData);
return true;
}
return false;
}
If you can't alter the HTML then you may be able to use loadDataWithBaseURL() and set a base URL consisting of a custom scheme and a valid host, such as "example-app:///". For example:
webView.loadDataWithBaseURL("example-app://example.co.uk/", HTML_DATA,
null, "UTF-8", null);
The valid host name should conform to RFC 3986 and it's important to include the trailing slash at the end, otherwise, any requests from the loaded page may be dropped.
to avoid webview below 4.4 convert backslash to forward slash, I just escape my url, then in Java code, use URI.decode to get the real url.That works for me.
Stuck on an issue with http post data.
So we've created a webview to work in.
Then called a third party webservice this ask's to provide a postback url and some parameters, the webservice then processes the paramaters and redirects the user (our app) to the postback url we provided, with some data as a http post.
We want to get the post data from this postback url but within the webview or within our app.
Tried intercepting the url load with this code:
final WebView webview = new WebView(this);
webview.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Grab data here...
return super.shouldOverrideUrlLoading(view, url);
}
});
However can't seem to find any objects or methods to gain access to the data within this method.
The other attempt was to give them the postback url as an intent within our app like:
myscheme://someText/someParam
this would have started a new activity when the postback is called (as we have set up an intent-filter within our android-manifest), this intent fires up our app activity within a browser but not within our webview, but anyway again we can't see how to access the post data from this.
Thanks for looking,
Any ideas?
Didn't find a way to do this. Basically can't get at data posted at a webview or a browser intent.
Instead the solution we went with is:
Postback to a server, get the postdata here (PHP) and save in a DB, then retrieve the data from this DB within the phone.