Monday, August 19, 2013

Android - WebView - JavaScript - onPageFinish() and stuff in between

OK... so let start by saying I'm working with some very talented people...

I've been digging in Android sources for days searching for the cause for this, I've even offered a bounty for 100 points which no one claimed.

I've seen many similar issues such as this, across StackOverFlow...

The main issue is that the loading of the page is completed, and then the onPageFinished event is called with a seemly random delay that can range from 0.1 - 40 sec, only after the <GATE-M>DEV_ACTION_COMPLETED</GATE-M> is printed to the log.

Here is the code snippet:

webView = (WebView) view.findViewById(R.id.WebView);

webView.setWebViewClient(new WebViewClient() {

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        logDebug("Loading URL: " + url);
        super.onPageStarted(view, url, favicon);
    }

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        return WrappingClass.this.shouldOverrideUrlLoading(view, url);
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        logInfo("Injecting JavaScript to webview.");
        webView.loadUrl("full-js-here");
    }

    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
        logError("error code:" + errorCode);
        super.onReceivedError(view, errorCode, description, failingUrl);
    }
});

WebSettings webSettings = webView.getSettings();
webSettings.setSavePassword(false);
webSettings.setSaveFormData(false);
webSettings.setJavaScriptEnabled(true);
webView.requestFocus(View.FOCUS_DOWN);
webView.loadUrl("url");

So the solution is quite a hack but it is wonderful... Check it out:

Somewhere in your class declare the following:
final class ObjectExtension {

    @JavascriptInterface
public void onLoad() { logInfo("onLoadCompleted"); WrappingClass.this.onLoadCompleted(); } }
public void onLoadCompleted() {
    webView.loadUrl("full-js-here");
}


And before the URL loading add the following:
webView.addJavascriptInterface(new ObjectExtension(), "webviewScriptAPI");
String fulljs = "javascript:(\n    function() { \n";
fulljs += "        window.onload = function() {\n";
fulljs += "            webviewScriptAPI.onLoad();\n";
fulljs += "        };\n";
fulljs += "    })()\n";
webView.loadUrl(fulljs);
webView.loadUrl("url");

This registers a callback for the onLoad event of the WebView window, which is loaded long time before the onPageFinished is called, because of that Android WebView issue.

So the trick is that we inject the onLoad callback before loading the url, later (next line) we load the url, once the onLoad callback is called, in our onLoad implementation we call to the Java API, which in its turn inject the Javascript into the loaded page, and sometime long after that the onPageFinished is called.

End of story...
---- UPDATE ----

It has been a very long while since that post... I've wrote SocialApp(link at the top Left), which is entirely a WebView application, a multi WebView applications, which runs Javascripts on all of the WebViews and monitor the beginning and ends of scripts, and runs Javascript on a WebViews in the background....

What I'm trying to say is, if you have any questions, I'm pretty sure I can answer them, so ask away...


---- UPDATE ----

It has been a very long while since I posted that update, I really hoped to release it as an open source, and was struggling with it for a long time, but as it was pretty much forced on me due to the architecture I've been decided to append this "CyborgWebView" to Cyborg, which is a license based SDK, You can find it here.

Also, if the post is not clear enough, and you prefer a sample project, let me know...







112 comments:

  1. Not working. It doesn't go to the onLoad() callback. Am I missing something here?

    ReplyDelete
  2. It can be that the page overrides the callback... Post some code?

    ReplyDelete
    Replies
    1. please check this code , i get blank screen , api 28

      screen
      public class MainActivity extends AppCompatActivity {
      WebView webView;
      String webViewUrl;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);


      webView=(WebView)findViewById(R.id.mobile_sso_wv);

      //webView = (WebView) view.findViewById(R.id.WebView);

      webView.setWebViewClient(new WebViewClient() {

      @Override
      public void onPageStarted(WebView view, String url, Bitmap favicon) {
      //logDebug("Loading URL: " + url);
      super.onPageStarted(view, url, favicon);
      }

      @Override
      public boolean shouldOverrideUrlLoading(WebView view, String url) {
      return this.shouldOverrideUrlLoading(view, url);
      }

      @Override
      public void onPageFinished(WebView view, String url) {
      super.onPageFinished(view, url);
      //logInfo("Injecting JavaScript to webview.");

      webView.addJavascriptInterface(new ObjectExtension(), "webviewScriptAPI");
      String fulljs = "javascript:(\n function() { \n";
      fulljs += " window.onload = function() {\n";
      fulljs += " webviewScriptAPI.onLoad();\n";
      fulljs += " };\n";
      fulljs += " })()\n";
      webView.loadUrl(fulljs);

      webView.loadUrl("https://www.qatarmark.com");

      webView.loadUrl("full-js-here");
      }

      @Override
      public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
      //logError("error code:" + errorCode);
      super.onReceivedError(view, errorCode, description, failingUrl);
      }
      });

      WebSettings webSettings = webView.getSettings();
      webSettings.setSavePassword(false);
      webSettings.setSaveFormData(false);
      webSettings.setJavaScriptEnabled(true);
      webView.requestFocus(View.FOCUS_DOWN);
      webView.loadUrl("https://google.com");

      }
      final class ObjectExtension {

      @JavascriptInterface

      public void onLoad() {
      //logInfo("onLoadCompleted");
      MainActivity.this.onLoadCompleted();
      }
      }

      public void onLoadCompleted() {
      webView.loadUrl("full-js-here");
      }
      }

      Delete
  3. webView=(WebView)findViewById(R.id.mobile_sso_wv);
    webViewUrl= "https://example.google.com";

    WebSettings webSettings=webView.getSettings();
    webSettings.setJavaScriptEnabled(true);
    webView.setWebViewClient(new LoginWebViewClient());
    webView.setWebChromeClient(new SignInWebChromeClient());
    webView.addJavascriptInterface(new LoginJsInterface(), "webviewScriptAPI");
    String fulljs = "javascript:(\n (function() {\n";
    fulljs += " window.onload = function({\n";
    fulljs += " webviewScriptAPI.onLoad();\n";
    fulljs += " };\n";
    fulljs += " })()\n";
    webView.loadUrl(fulljs);
    webView.loadUrl(webViewUrl);

    public void onLoadCompleted()
    {
    webView.loadUrl("javascript:document.getElementById('submit_button').style.backgroundColor='#3b3b3b'");

    webView.loadUrl("javascript:document.getElementById('submit_button').style.width='100%'");

    }


    In onLoadcompleted I have changed the submit button color and width.

    ReplyDelete
  4. Where is the LoginJsInterface object?

    ReplyDelete
  5. This is my Javascript interface class

    class LoginJsInterface
    {
    @JavascriptInterface
    public void onLoad()
    {
    Log.d("OnLoadCompleted----------------------->","Yes");
    onLoadCompleted();
    }
    }

    ReplyDelete
    Replies
    1. Are you targeting a real url?
      Because you would only get onLoad once a page has been loaded, if there was an error, you need to catch it in the onError received in your LoginWebViewClient

      Delete
    2. Yes I am targeting a real url. I have given dummy url in the above code. I have implemented onReceivedError() too. But no errors in that callback.

      Delete
  6. Where is the LoginJsInterface object?

    ReplyDelete
  7. See the first part of the code. I have added javascript object through the addJavascriptInterface() method.

    webView.addJavascriptInterface(new LoginJsInterface(), "webviewScriptAPI");

    ReplyDelete
    Replies
    1. Perhaps the page itself overrides the window.onLoad...
      Did you try another URL?

      Delete
    2. Dear Adam,

      Yes I have tried with different URLs but the result is same :(

      Delete
    3. I have tried in jelly bean 4.1.2 and gingerbread 2.3. Could you please send your .apk file or your application? Hope it helps to track the issue.

      Delete
    4. Sorry, but I don't have a sample project...

      Could you try removing the ChromeClient, and stick to the example...

      webView.setWebViewClient(...)
      webSettings.setSavePassword(false);
      webSettings.setSaveFormData(false);
      webSettings.setJavaScriptEnabled(true);
      webView.requestFocus(View.FOCUS_DOWN);

      because this works on so many devices, the only thing can cause this not to work is that something overrides the onLoad listener...

      Delete
    5. Hi Adam,

      Yes tried, unfortunately the result is same. I did not handle shouldOverrideUrlLoading(WebView webView, String url) method. I have returned false in that method. I have also tried what you have given in your example but no luck.

      Delete
    6. WOW... I really don't know why this does not work for you... is this a project you could share with me so I can take a look?

      Delete
    7. Could you please share your mail id ?

      Delete
    8. I found something weird yesterday. It went to JavaScript callback only on android 2.2 if I change the JS code like this otherwise it doesn't went to that callback.

      String fulljs="javascript:window.webviewScriptAPI.onLoad();";
      webView.loadUrl(fulljs);

      But JS never executing. In Other versions of OS it never goes to Javascript callback.

      Delete
    9. We have tested this yesterday and today, and I can say with certainty that this works...

      From the example you've sent me, I've noticed that the the Javascript is not processed... I'll look into it again, but I don't have time to do this until sometime next week...(Work stuff)

      In the meanwhile, try injecting some obvious JS, like window.location.href='yyy' and see that the url actually changes, this way you can know for sure that your script is running!!!

      Also, I think there was an extra '(' in the window.onload setting script in the example you've sent.

      How about you'l add that project to bitbucket, or github, and we can work on it together, and leave future reference for people to find?

      Delete
    10. Now I am trying new workaround for this use case. I will upload my project on github soon.

      Delete
    11. I can also tell you for sure that with Yahoo.com for example, they override the window.onload method, and then we use the onPageFinish callback

      Delete
    12. Dinesh,
      Try replacing the javascript loading line and the actual url call...

      webView.loadUrl(webViewUrl);
      webView.loadUrl(fulljs);

      Delete
  8. Don't know if you'll see this, but i have this working. The only problem is, when i input a form and the page loads the following result (the same URL, different content) onLoad does not seem to be called?

    Thanks for any help..

    ReplyDelete
    Replies
    1. Hi Daniel,

      What I've done is that I've merged the two calls from onLoad, and onPageFinish, into one delegation function, onLoadCompleted, and made sure (with a boolean) that the action it performs would only be called once.

      So, if the onLoad is not called... is the onPageFinished called?

      Delete
  9. Thank you, very much, this is working for me. I hardly found tutorial with passing parametars to javascript in oncreate method. Thank you, again

    ReplyDelete
    Replies
    1. Hi Ana,

      You are welcome, let me know if there is anything else...

      Adam.

      Delete
  10. i have maid a browser using web view , my problem is when the user open a link if he go out of the app or the activity , when he open it again it will open from the default link , i would like it to be like Google browser when u ever go back it will open for u the last link u was in it

    for ex if any way like



    webView.loadUrl("http://www.google.com");
    webView.getNewUrl;

    Url=NewUrl

    ReplyDelete
    Replies
    1. I'm not sure what do you mean... if you want to save the last state of your WebView you will have to store and load it, in the saveState and loadState methods respectively, in the your activity or fragment, or store the state to a sharedpreferences object.

      Delete
  11. Still not sure why onPageFinish take soooo long sometimes..
    Anyway - nice tip! Thank you for the post.

    ReplyDelete
    Replies
    1. p.s.
      If you run multiple WV and switch Activities before onPageFinish is called, than WV thread might be pushed down and execution will be delayed.
      Sometimes, the trigger for executing the WV on the background is when another WV onPageFinish method is executed.

      FYI...

      Delete
    2. Thank you for your reply, This issue was spotted when using a single webview within a single activity. it was one flow that repeated several times and not in a very rapid rate, that is way it took me so long to figure out, that something was actually wrong. After this long time that I've developed an entire infrastructure to overcome so many issues with WebViews I can tell you for sure that it is a bug, and that the workaround is not perfect but works.

      I've also noticed that history update is only called once you've left the page you were visiting, and that if you watch the onProgress in one of the clients, it stalls after reaching 86% or something like that...

      This is really a poor job done, also webview rendering and bg javascript consumes SO MUCH battery it is ridiculous.
      Let me know if I can be of any assistance, and check out SocialApp, it is in small part based on this solution.

      Delete
  12. I just want to thank you for this great solution :)
    But just one comment. This code:

    final class ObjectExtension {

    public void onLoad() {
    logInfo("onLoadCompleted");
    WrappingClass.this.onLoadCompleted();
    }

    }

    should include the @JavascriptInterface and look like this:

    final class ObjectExtension {

    @JavascriptInterface
    public void onLoad() {
    logInfo("onLoadCompleted");
    WrappingClass.this.onLoadCompleted();
    }

    }

    ReplyDelete
  13. i dont know if am too late but am having this same issue....lag before the execution of my javascript codes into my webview, please can i get the complete code for this fix....maybe a .zip please, VERY URGENT please

    ReplyDelete
    Replies
    1. Hi,
      I know this is not the same as a full working example, but it is enough to to compose your own example...

      I already have it all working in my Cyborg project, and unfortunately I don't have time to migrate it into a full working stand alone example, as my implementation uses a custom webview to provide default Javascript to java apis like logs and clicks, and to overcome other android webview issues, like battery consumption among other things...

      If you have a working sample, I might be able to help you out.

      Delete
  14. This information is impressive; I am inspired with your post writing style & how continuously you describe this topic. After reading your post, thanks for taking the time to discuss this, I feel happy about it and I love learning more about this topic.Best Android Training in Velachery | android development course fees in chennai

    ReplyDelete
  15. Interesting. Unfortunatly, standalone sample app is absent

    ReplyDelete
  16. I wanted to thank you for this great read!! I definitely enjoying every little bit of it I have you bookmarked to check out new stuff you post.is article.
    Click here:
    Microsoft azure training in velarchery
    Click here:
    Microsoft azure training in sollinganallur

    ReplyDelete
  17. It is amazing and wonderful to visit your site.Thanks for sharing this information,this is useful to me...
    Blueprism training in Chennai

    Blueprism training in Bangalore

    Blueprism training in Pune

    Blueprism online training

    ReplyDelete
  18. This is a nice post in an interesting line of content.Thanks for sharing this article, great way of bring this topic to discussion.
    java training in chennai | java training in USA

    ReplyDelete
  19. I am sure this post has helped me save many hours of browsing other related posts just to find what I was looking for. Many thanks!
    Microsoft Azure online training
    Selenium online training
    Java online training
    Python online training
    uipath online training

    ReplyDelete
  20. I enjoy what you guys are usually up too. This sort of clever work and coverage! Keep up the wonderful works guysl.Good going.
    redmi service center
    xiaomi service centre chennai
    redmi service center in chennai

    ReplyDelete
  21. David Walsh is Mozilla’s senior web developer, and the core developer for the MooTools Javascript Framework. David’s blog reflects his skills in HTML/5, JS and CSS, and offers a ton of engaging advice and insight into front-end technologies. Even more obvious is his passion for open source contribution and trial-and-error development, making his blog one of the most honest and engaging around.
    Website: davidwalsh.name

    ReplyDelete
  22. David Walsh is Mozilla’s senior web developer, and the core developer for the MooTools Javascript Framework. David’s blog reflects his skills in HTML/5, JS and CSS, and offers a ton of engaging advice and insight into front-end technologies. Even more obvious is his passion for open source contribution and trial-and-error development, making his blog one of the most honest and engaging around.
    Website: davidwalsh.name

    ReplyDelete
  23. Nice and very useful blog. A great and very informative post, Keep up the good work!


    Data Science Courses

    ReplyDelete
  24. Nice blog and absolutely outstanding. You can do something much better but i still say this perfect.Keep trying for the best.
    machine learning course malaysia

    ReplyDelete
  25. Hey, would you mind if I share your blog with my twitter group? There’s a lot of folks that I think would enjoy your content. Please let me know. Thank you.
    Java Training in Chennai | J2EE Training in Chennai | Advanced Java Training in Chennai | Core Java Training in Chennai | Java Training institute in Chennai

    ReplyDelete
  26. Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.
    Hadoop Training in Electronic City

    ReplyDelete
  27. Your good knowledge and kindness in playing with all the pieces were very useful. I don’t know what I would have done if I had not encountered such a step like this.
    Best PHP Training Institute in Chennai|PHP Course in chennai

    Best .Net Training Institute in Chennai
    Oracle DBA Training in Chennai
    RPA Training in Chennai
    UIpath Training in Chennai

    ReplyDelete
  28. I have to voice my passion for your kindness giving support to those people that should have guidance on this important matter.
    AI training chennai | AI training class chennai
    Cloud computing training | cloud computing class chennai



    ReplyDelete
  29. I think this is one of the most significant information for me. And i’m glad reading your article. Thanks for sharing!

    Bangalore Training Academy is a Best Institute of Salesforce Admin Training in Bangalore . We Offer Quality Salesforce Admin with 100% Placement Assistance on affordable training course fees in Bangalore. We also provide advanced classroom and lab facility.

    ReplyDelete
  30. I am happy for sharing on this blog its awesome blog I really impressed. Thanks for sharing.

    Became An Expert In UiPath Course ! Learn from experienced Trainers and get the knowledge to crack a coding interview, @Softgen Infotech Located in BTM.

    ReplyDelete
  31. Awesome Post!!! I really enjoyed reading this article. It's really a nice experience to read your post. Thanks for sharing.
    Data Science Course
    Data Science Course in Marathahalli
    Data Science Course Training in Bangalore

    ReplyDelete
  32. Thanks for sharing such a great information..Its really nice and informative.. microsoft azure training

    ReplyDelete
  33. This piece of information's are really Awesome...The information's are helpful to enhance the careers...Good Creation!!!
    Java training in chennai | Java training in annanagar | Java training in omr | Java training in porur | Java training in tambaram | Java training in velachery

    ReplyDelete
  34. Nice information, valuable and excellent design, as share good stuff with good ideas and concepts, lots of great information and inspiration, both of which I need, thanks to offer such a helpful information here.
    data science certification

    ReplyDelete
  35. A good blog always comes-up with new and exciting information and while reading I have feel that this blog is really have all those quality that qualify a blog to be a one.
    data science certification

    ReplyDelete
  36. Very interesting to read this article.I would like to thank you for the efforts you had made for writing this awesome article. This article inspired me to read more. keep it up.
    Correlation vs Covariance
    Simple linear regression
    data science interview questions


    ReplyDelete
  37. Great post i must say and thanks for the information.
    Data Science Course in Hyderabad

    ReplyDelete
  38. I just got to this amazing site not long ago. I was actually captured with the piece of resources you have got here. Big thumbs up for making such wonderful blog page!
    artificial intelligence course in bangalore

    ReplyDelete
  39. Very interesting to read this article.I would like to thank you for the efforts you had made for writing this awesome article. This article inspired me to read more. keep it up.

    AWS training in Chennai

    AWS Online Training in Chennai

    AWS training in Bangalore

    AWS training in Hyderabad

    AWS training in Coimbatore

    AWS training

    AWS online training

    ReplyDelete
  40. wonderful article. I would like to thank you for the efforts you had made for writing this awesome article. This article resolved my all queries. data science courses

    ReplyDelete
  41. It’s hard to come by experienced people about this subject, but you seem like you know what you’re talking about. I have found something which helped me. Thank you
    Java Training in Chennai

    Java Training in Velachery

    Java Training inTambaram

    Java Training in Porur

    Java Training in Omr

    Java Training in Annanagar

    ReplyDelete
  42. Very interesting, good job and thanks for sharing such a good blog. your article is so convincing that I never stop myself to say something about it. You’re doing a great job. Keep it up…
    Software Testing Training in Chennai

    Software Testing Training in Velachery

    Software Testing Training in Tambaram

    Software Testing Training in Porur

    Software Testing Training in Omr

    Software Testing Training in Annanagar

    ReplyDelete
  43. Nice article and thanks for sharing with us. Its very informative



    Plots in THIMMAPUR

    ReplyDelete
  44. Very interesting to read this article.I would like to thank you for the efforts you had made for writing this awesome article. This article inspired me to read more. keep it up.
    DevOps Training in Chennai

    DevOps Course in Chennai

    ReplyDelete
  45. Great job!!! I recently saw your blog and your blog content is very comprehensive with depth explanation. Thank you much more for sharing with us...!

    Java Training in Chennai

    Java Course in Chennai

    ReplyDelete
  46. I need to thank you for the endeavors you have carefully recorded this site. I'm expecting to see a similar high-grade blog entries from you later on too. Truth be told, your experimental writing capacities has roused me to get my own, own blog now ;)
    live

    ReplyDelete
  47. Very nice article. I enjoyed reading your post. very nice share. I want to twit this to my followers. Thanks
    Data Science Training in Hyderabad
    Data Science Course in Hyderabad

    ReplyDelete
  48. Good content, great work. I appreciated your work on this blog. Maintain this great work.
    Python Course Training with Placements
    Machine Learning Training with Placements

    ReplyDelete
  49. Reach to the best Python Training institute in Chennai for skyrocketing your career, Infycle Technologies. It is the best Software Training & Placement institute in and around Chennai, that also gives the best placement training for personality tests, interview preparation, and mock interviews for leveling up the candidate's grades to a professional level.

    ReplyDelete


  50. That is nice article from you , this is informative stuff . Hope more articles from you . I also want to share some information about Pet Vaccination in vizag

    ReplyDelete
  51. Thanks for posting this info. I just want to let you know that I just checked out your site and I find it very interesting and informative. I can't wait to read lots of your posts.
    business analytics training in hyderabad

    ReplyDelete

  52. Pleasant data, important and incredible structure, as offer great stuff with smart thoughts and ideas, loads of extraordinary data and motivation, the two of which I need, because of offer such an accommodating data here.
    data analytics course in hyderabad

    ReplyDelete
  53. Hi,
    The author's ingenious solution to the delayed onPageFinished event in Android's WebView demonstrates both creativity and expertise. Their willingness to share this valuable workaround is commendable and greatly benefits the developer community.
    Data Analytics Courses In Dubai

    ReplyDelete
  54. Both ingenuity and knowledge can be seen in the author's brilliant solution to the delayed onPageFinished event in Android's WebView. It is excellent that they are willing to disclose this helpful solution, as it tremendously benefits the developer community.
    Data Analytics Courses in Agra

    ReplyDelete
  55. Thank you so much for telling how to put onPageFinish() and stuff in between.
    Visit - Data Analytics Courses in Delhi

    ReplyDelete
  56. The explanation of onPageFinish() and the in-between steps is clear and insightful. Thanks for shedding light on this crucial aspect of mobile app development.

    Digital marketing courses in illinois

    ReplyDelete
  57. Thanks for sharing outstanding and insightful explanation on onPageFinish() and the in-between steps.
    data analyst courses in limerick

    ReplyDelete
  58. The step-by-step breakdown and insights into handling JavaScript interactions during the WebView lifecycle are particularly useful. I appreciate the practical tips and considerations you've provided were very helpful.
    Digital marketing courses in city of Westminster

    ReplyDelete
  59. The step-by-step breakdown clarifies the process effectively. I'm curious to know if you've encountered any common challenges or best practices when dealing with JavaScript in WebView. Great post. keep up the good work.
    Data analytics framework

    ReplyDelete