I have a webview with HTML webpage which is downloaded from server and i populate dynamic data and add rows of information to this webpage. I have used the following code on that webpage to enable javascript:
class JSClass {
Context jsContext;
JSClass(Context c){
jsContext = c;
}
public void getHTMLContent(String info)
{
Log.i(TAG, "Info: "+info);
}
}
WebView webview = (WebView) findViewById(R.id.webView);
webview.loadDataWithBaseURL("", htmlcontent, "text/html", "utf-8", "");
webview.getSettings().setJavaScriptEnabled(true);
webview.setWebChromeClient(new WebChromeClient());
webview.addJavascriptInterface(new JSClass(this), "Android");
webview.loadUrl("javascript:"+
"var rows = document.getElementsByClassName('info');"+
"if(rows !== null){"+
"for(var i=0;i<rows.length;i++){"+
"rows[i].onclick = function(){"+
"var workId = 0;"+
"workId = parseInt(this.cells[0].innerHTML);"+
"window.Android.getHTMLContent(workId);"+
"}}}"
);
when i click on any row, i cal an intent to download images from the server depending on the row data. It works fine on first click, but the second time i click, the application closes without any error. I don't understand the reason. Please help. Thanks in advance
I think you need to use loadDataWithBaseURL again instead of just loadUrl.
See this link. There is a section explaining loadData that says:
Note that JavaScript's same origin policy means that script running in a page loaded using this method will be unable to access content loaded using any scheme other than 'data', including 'http(s)'. To avoid this restriction, use loadDataWithBaseURL() with an appropriate base URL.
FINALLY!! fixed it.. the adapter was holding previously downloaded images as well as the images downloaded during second click and i don't understand why the application was closing without giving any memory leaks as well.. anyways its working fine now after using
adapter.clear();
or re-initializing the adapter to null after all the image thumbs are downloaded(i do not know if this is very efficient). After the adapter is cleared, use
adapter.notifyDataSetChanged();
Related
I am having a bit of trouble getting my application to correctly run some JS on a page using the onPageFinished method.
The code below is contained within a class I've created that extends AsyncTask to fetch and parse a JSON file held elsewhere.
I am able to fetch the JSON file correctly, parse the data and the url for the WebView is obtained and set. Everything works loads as it should until I attempt to run some JS with the onPageFinished method.
//onPostExecute method runs when the doInBackground method is completed
#SuppressLint("SetJavaScriptEnabled")
#Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
//Casting as WebView as findViewById doesnt explicity return a value type.
webView = (WebView) findViewById(R.id.webView);
//Obtaining the websettings of the webView
WebSettings webViewSettings = webView.getSettings();
//Setting Javascript enabled
webViewSettings.setJavaScriptEnabled(true);
webView.setWebViewClient(new webViewClient(){
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
webView.loadUrl("document.getElementById('field_133').value = 'Test';");
Log.d("onPageFinished", "The Page has finished loading");
}
});
//Obtaining the first item in the cellRef List Array - From here we will access the Url data for the train operator.
parsedUrl = cellRef.get(0).getUrl();
//load the page we parsed from online file
webView.loadUrl(parsedUrl);
Log.d("loadUrl", "Now load the parsed Url");
}
All I am looking to do at the moment is test that the JS can correctly populate a textbox once the page has loaded with the value of "Test" - However, the WebView appears to be stuck in a loop of loading & refreshing (seeing repeated logcat prints of "The page has finished loading") when trying to run:
webView.loadUrl("document.getElementById('field_133').value = 'Test';");
Is this the correct way of trying to inject some JS into the WebView in Android? Apologies if there is something obvious missing, the majority of my experience lies in Swift.
Any help would be appreciated.
Thanks
Try "javascript:" before the code.
I use this, works perfectly:
loadUrl("javascript:(function() { document.getElementsByTagName('video')[0].play(); })()");
Problem:
For an Android WebView, I need an html doc downloaded from a CDN.
I need to periodically re-download the document to see if it's been
updated.
I want the WebView to load this document, but only load a
new version if it is different from its current version to preserve
page variables (I have no influence on the document and cannot change
the way it runs).
My solution was to natively load the document using a HttpURLConnection and use webView.loadDataWithBaseURL(...). This way I can diff the document versions and only call this method when there's a new document. Also, this document can then fetch additional resources using the baseUrl.
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient(){ /*...*/ });
webView.setWebViewClient(new WebViewClient(){ /*...*/ });
String html = getHtmlDoc("http://somecdn.com/doc.html");
webView.loadDataWithBaseURL("http://somecdn.com/", html, "text/html", "utf-8", null);
The unexpected behavior:
D/Console(xxxx): onPageStarted:http://somecdn.com/doc.html
D/Console(xxxx): onLoadResource:http://somecdn.com/script.js
D/Console(xxxx): onPageFinished:http://somecdn.com/doc.html
But when I call:
webView.loadUrl("javascript:console.log(typeof some_object)");
Where some_object is defined in script.js, the following is printed:
D/Console(xxxx): undefined
There are no errors being reported by WebViewClient.onReceivedError(...). What am I missing? Is there a better means of loading the doc.html only if there's a new version? I don't have any access to the CDN outside of downloading content. I am currently testing on Android 4.1.1 but will need to support FROYO-KITKAT+
EDIT:
Solution:
Per marcin.kosiba's recommendation, I'm going to use a HEAD Request with If-Modified-Since specified:
SimpleDateFormat ifModifiedFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
ifModifiedFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
final HttpURLConnection conn = (HttpURLConnection)(new URL("http://somecdn.com/doc.html")).openConnection();
conn.setRequestMethod("HEAD");
conn.setRequestProperty("If-Modified-Since", ifModifiedFormat.format(new Date(/* last fetched date */)));
conn.setRequestProperty("Accept-Encoding", "*");
conn.connect();
if(conn.getResponseCode() == 200) {
webView.reload();
}
There is a bug with HEAD requests in Android where an EOFException is thrown, adding conn.setRequestProperty("Accept-Encoding", "*"); or conn.setRequestProperty("Accept-Encoding", ""); resolves this issue.
Since you need to reload the page in the WebView anyway, instead of downloading the html by yourself you could just ask the server if it had changed and if it has ask the webview to reload it? See this answer https://stackoverflow.com/a/1938603/2977376 for how to use If-Modified-Since to do ask the server if the contents had changed.
I have been able to get content out of WebView using javascript and loadUrl() method having specified an interface thats called from javascript string that is injected into WebView.The problem is that this only works for me when the loadUrl() method is present in onPageFinished() method in the WebView client. What I want to do is I want to get the content out of the WebView (with the content already loaded). The WebView is in an activity instrumentation test case and I can for instance use findAll() method and that works fine. For some reason I can not use loadUrl() and get the desired behaviour (which is injecting javascript and getting content out of the WebView with a help of an interface).
PLease help.
Thanks
Pawel
EDIT:
Just adding code to show what I am doing exactly:
Yes I understand that but my problem is that I am trying to do it within a test case this way:
public void testWebView() throws Exception {
solo.sleep(3000); // wait for views to load on the screen
WebView a=null;
ArrayList<View> views = solo.getCurrentViews(); // I am using solo object to get views for the screen currently loaded
for(View s:views)
{
if (s instanceof WebView)
{
a = (WebView)s; // this is where I get my WebView
}
}
Instrumentation inst = getInstrumentation();
inst.runOnMainSync(new Runnable()
{
public void run()
{
int d =a.findAll("something"); // this method runs fine on the object and i get the desired result
WebSettings settings = a.getSettings();
settings.setJavaScriptEnabled(true);
a.loadUrl("javascript:document.location = document.getElementById('google').getAttribute('href')"); // this javascript is never executed and that is my problem
}
});
}
You can inject javascript in a loaded page much the same way you can do it in desktop browsers - via inline javascript entered into navigation bar.
Bind some Java object so that it can be called from Javascript with WebView:
addJavascriptInterface(javaObjectExposed, "JSname")
Force execute javascript within an existing page by
WebView.loadUrl("javascript:window.JSname.passData("some data from page");");
I've got a very basic WebView which works until I try to add a custom webViewClient where it stops processing JavaScript. Am I doing something wrong? Is there another way to get rid of the address bar and menu options in the WebView?
browser = (WebView) findViewById(R.id.webkit);
WebSettings webSettings = browser.getSettings();
webSettings.setJavaScriptEnabled(true);
// uncommenting this line will remove address bar, but also stop JavaScript from loading
//browser.setWebViewClient(new InternalWebViewClient());
// even uncommenting this line will stop JavaScript from loading
//browser.setWebViewClient(new WebViewClient());
browser.setWebChromeClient(new InternalWebChromeClient());
if (savedInstanceState != null) {
browser.restoreState(savedInstanceState);
} else {
browser.loadUrl("http://site.with.javascript");
}
In my app I use the following and there is no address bar, and JavaScript works (modified to match your naming):
browser = (WebView) findViewById(R.id.webkit);
browser.getSettings().setJavaScriptEnabled(true);
browser.loadUrl("http://site.with.javascript");
I don't do anything with setWebViewClient or setWebChromeClient and it works as described.
I think the problem with your code is that you enable JavaScript on the default (Internal)WebViewClient and/or WebChromeClient then you replace those with new ones that now have new properties.
If you move the setJavaScriptEnabled(true) call to come after those new assignments (and before the loadUrl I think your code would work.
For some reason the webkit runs JS differently than the browser - I ended up getting around the issue by forcing some JS to run with the following line after the page had loaded:
browser.loadUrl("javascript:document.getElementById('something').do.something()");
I was helped by such a decision. Wrap computing in an anonymous function.
"javascript:" + "(function(){ <YOUR CODE> })();"
I'm got a custom webview setup which is working pretty well, but I'd like to be able to either:
1, change the url hash without the webview reloading the page (it would lose the state of my js app)
2, call some js that sits within my web page from within android. I can't change any JS within the site, unfortunately, so can't custom write any js to put on the site especially for the job, the only stuff I have control over is the Android app.
Can anyone think of a way of doing either of these?
Thanks in advance.
M
Or if you just want to change the hash, you can use:
view.loadUrl("javascript:location.hash=\"#" +fragment+"\"");
For #2, your Activity can invoke JavaScript methods. All you have to do is call the loadUrl method with the appropriate JavaScript call:
mWebView.loadUrl("javascript:wave()");
From:
http://developer.android.com/resources/articles/using-webviews.html
I know this question is old but this is what works for me in Android 4.4:
String fragment = "#test"
mWebView.evaluateJavascript("location.hash=\"" + fragment + "\";", new ValueCallback<String>() {
#Override
public void onReceiveValue(String value) {
// Yes, I like to log data :-)
Log.d("MyApp", "onReceiveValue(value): " + value);
}
});