Sunday, April 15, 2012

GWT & Phonegap 1.6 - Issues with loading on android

Some of you might be experiencing issues while loading your GWT App in a phonegap environment.

The app might not be starting at all if you are using android 4.x with phonegap and you simply see a white screen instead. A suggested workaround (that many use) is to change to the cross site iframe linker in your gwt compile, which would mean that you would be using a different bootup mechanism for your gwt app. Which is a workaround for this situation and can help, but causes other issues.

So I did a little digging the last weekend and took a close look at the boot load of a GWT app in a phonegap environment. The standard GWT linker creates an iframe in which it loads the content of the GWT Compiler (a html file with javascript). This page is actually what you see in your phonegap app as a blank white page.

By default phonegap on android thinks that all local navigation (urls which start with file://) should be handled by phonegap and should load the url in the browser. This is fine for apps that need navigation like jquerymobile apps, but its not a good idea for GWT apps since it interferes with GWT bootup.

This is what happens in the CordovaWebClient by default:

// If our app or file:, then load into a new Cordova webview container by starting a new instance of our activity.
// Our app continues to run.  When BACK is pressed, our app is redisplayed.
if (url.startsWith("file://") || url.indexOf(this.ctx.baseUrl) == 0 || ctx.isUrlWhiteListed(url)) {
                this.ctx.loadUrl(url);
}


So we need to prevent this by implementing our own web client:


package de.kurka.cordova;

import org.apache.cordova.CordovaWebViewClient;
import org.apache.cordova.DroidGap;
import android.webkit.WebView;

public class GWTCordovaWebViewClient extends CordovaWebViewClient {

public GWTCordovaWebViewClient(DroidGap ctx) {
super(ctx);
}
@Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
       if(url.startsWith("file://"))
       {
       return false;
       }
       
       return super.shouldOverrideUrlLoading(view, url);
       
    }

}

And we need to override the init method of our man activity to use it like this:
package de.kurka.cordova;

import org.apache.cordova.CordovaChromeClient;
import org.apache.cordova.DroidGap;

import android.os.Bundle;
import android.webkit.WebView;

public class HelloCordovaActivity extends DroidGap {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.loadUrl("file:///android_asset/www/index.html");
    }
    
    @Override
    public void init() {
    super.init(new WebView(this), new GWTCordovaWebViewClient(this), new CordovaChromeClient(this));
    }
}


This fixes the white screen issue with phonegap 1.6 and GWT apps.


22 comments:

  1. Hello Daniel,

    I have this exact same problem and this is the only location on the internet that explains what to do. I am not very good in making android apps in phonegap (actually just started), so I need a bit more explanation. Where do I exactly put the code that you propose?

    ReplyDelete
  2. Hello Daniel,
    I just found out that Eclipse helps the programmer quite a bit: it suggested where to put the definition of GWTCorodovaWeb.etc by itself. Wow. Now the only thing it complains about is the line super(ctx)... Can you explain?

    ReplyDelete
  3. Hello Daniel,

    I found out that too. Just didn't read carefully enough. You're brilliant and saved my day!!! Thanks again for sharing all of this with us.

    ReplyDelete
  4. Thank you so much, you just made my day

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Does it work with phonegap 1.9 ?
    I try but i got a problem with super.init(new WebView(this)...) so I change to super.init(new CordovaWebView(this)...)

    It works, I didn't have the blank page anymore but only the html is displaying, nothing more (I put a window.alert in my gwt code but nothing happens)

    ReplyDelete
  7. sry for disturbing, got it !
    I didn't catch my timeout exception then i couldn't see the js file problem

    ty =)

    ReplyDelete
  8. There is the same problem with gwt-phonegap 1.8.1, and I can not find de.kurka.cordova in the sources - to fix the problem. Anyy suggetions, please ?

    ReplyDelete
  9. Can You, please, elaborate what classes exactly should be added to Android project? Can You show proper project structure for both ANdroid and GWT ?

    ReplyDelete
  10. Sergo, you need to add the new file to the phonegap project (e.g. HelloCorvoda project). You can change the package name for your own need.

    ReplyDelete
  11. I tried the solution but it still has the white screen issue. I am using phonegap 2.0 and gwt-phonegap 1.8.1. I am playing the gwt-phonegap show case (git clone https://code.google.com/p/gwt-phonegap.showcase/). Any hints? Thanks!

    ReplyDelete
  12. please use the mgwt / gwt-phonegap user group to discuss issues and questions: https://groups.google.com/group/mgwt

    ReplyDelete
  13. Hi , I deployed my mgwt application on tomcat server and it is loading url from there instead of assess/www/ folder and I want to use phonegap functionality but it doesn't work. I couldn't figure it out and I don't know how to write above code for application deployed on tomcat. I couldn't make it work and I want to use it for android device back button. Could you please help me to make it work for different deployment on tomcat. I am using gwt 2.4 sdk , gwt-phonegap-1.8.0 and mgwt-1.1.2-20120721.065118-17.

    ReplyDelete
  14. Hi!

    For the latest PhoneGap version, I tried out the following variation of your suggestion, which does away with the need for a separate new class. When extending the DroidGap class, add:

    @Override
    public void init() {
    super.init(new CordovaWebView(this), new CordovaWebViewClient(this) {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
    return url.startsWith("file://")? false:
    super.shouldOverrideUrlLoading(view, url);
    }
    }, new CordovaChromeClient(this));
    }

    AFAIK, it works just as well; hope I'm not missing anything!

    Thanks for your code!

    ReplyDelete
    Replies
    1. Hi Federico Kereki,
      Awesome Its working well in PhoneGap2.0.
      Thanks!!!

      Delete
  15. actually inlining the override is a good idea to reduce external classes!

    ReplyDelete
  16. This comment has been removed by the author.

    ReplyDelete
  17. Hello,

    Is this recommendation still valid, or should we now just add the tag add-linker name="xsiframe" in the .gwt.xml file?
    None of these is working for me (shouldOverrideUrlLoading is never called), using phonegap 2.2 on Android 4

    ReplyDelete
  18. It would also be interesting to know if there's a solution for the use with ripple. Is it possible? I'm using the xsiframe linker anyway for superdevmode but the GWT App doesn't catch phonegap events nevertheless.

    ReplyDelete
  19. Hi I am getting a blank white screen. I tried making web client. any suggestions please.

    ReplyDelete
  20. Thank you Daniel

    With the lastest version of cordova this customization is not needed anymore.

    However I got two errors, first I named my gwt module "cordova" (rename-to="cordova") and that breaks the xsiframe linker when trying to call "cordova.onScriptDownloaded()", it took me years to resolve this one.

    I resolved a second error adding the "device" plugin to the cordova project, without it gwt-phonegap just doesn't work.

    ReplyDelete