Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Problem when viewing web pages in QML using android.webkit.WebView



  • Hi. I'm trying to create WebView-analogue QML component for Android. The problem is, when I call LoadUrl method for android.webkit.WebView I get a crash somewhere in com.android.webview.chromium.WebViewChromium. URL in loadUrl method can be any, even "about:blank". More details below:

    Some code:

    @AndroidWebView::AndroidWebView(QQuickItem *parent)
    : QQuickItem(parent)
    {
    setFlag(QQuickItem::ItemHasContents);
    }@

    @void AndroidWebView::componentComplete()
    {
    qDebug() << "AndroidWebView::componentComplete";

    QQuickItem::componentComplete();
    
    // Get activity object of our application
    QAndroidJniObject qObjActivity = QtAndroid::androidActivity();
    
    // Create WebView and set default background color
    qObjWebView = QAndroidJniObject("android/webkit/WebView", "(Landroid/content/Context;)V", qObjActivity.object<jobject>());
    jint backgroundColor = 0xFF000000;
    qObjWebView.callObjectMethod("setBackgroundColor", "(I)V", backgroundColor);
    
    // Construct LayoutParams object
    jint width = 100;
    jint height = 100;
    jint x = 0;
    jint y = 0;
    QAndroidJniObject qObjLayoutParams("android/widget/AbsoluteLayout/LayoutParams", "(IIII)V", width, height, x, y);
    
    // Add WebView as content view for our activity
    qObjActivity.callObjectMethod("addContentView", "(Landroid/view/View;Landroid/view/ViewGroup/LayoutParams;)V", qObjWebView.object<jobject>(), qObjLayoutParams.object<jobject>());
    

    }@

    @void AndroidWebView::loadURL(QString url)
    {
    qDebug() << "AndroidWebView::loadURL" << url;
    QAndroidJniObject qObjUrlString = QAndroidJniObject::fromString(url);
    // The crash happens when I call this method
    qObjWebView.callObjectMethod("loadUrl", "(Ljava/lang/String;)V", qObjUrlString.object<jstring>());
    }@

    Here is what I have got in QML:
    @AndroidWebView {
    id: androidWebView
    anchors.fill: parent
    Component.onCompleted: androidWebView.loadURL("about:blank");
    }@

    EDIT:
    And here is a log:
    @D/Qt (21155): ../MyApp/android/AndroidWebView.cpp:10 (AndroidWebView::AndroidWebView(QQuickItem*)): AndroidWebView::componentComplete
    D/Qt (21155): ../MyApp/android/AndroidWebView.cpp:82 (void AndroidWebView::loadURL(QString)): AndroidWebView::loadURL "about:blank"
    V/WebViewChromiumFactoryProvider(21155): Binding Chromium to main looper Looper (main, tid 1) {42dec630}
    I/LibraryLoader(21155): Expected native library version number "",actual native library version number ""
    I/chromium(21155): [INFO:library_loader_hooks.cc(116)] Chromium logging enabled: level = 0, default verbosity = 0
    I/BrowserStartupController(21155): Initializing chromium process, renderers=0
    E/AudioManagerAndroid(21155): BLUETOOTH permission is missing!
    E/dalvikvm(21155): JNI ERROR (app bug): attempt to use stale local reference 0x1
    E/dalvikvm(21155): VM aborting
    F/libc (21155): Fatal signal 6 (SIGABRT) at 0x000052a3 (code=-6), thread 21171 (qtproject.MyApp)
    D/AndroidRuntime(21155): Shutting down VM
    W/dalvikvm(21155): threadid=1: thread exiting with uncaught exception (group=0x41600d88)
    E/AndroidRuntime(21155): FATAL EXCEPTION: main
    E/AndroidRuntime(21155): Process: org.qtproject.MyApp, PID: 21155
    E/AndroidRuntime(21155): java.lang.NullPointerException
    E/AndroidRuntime(21155): at com.android.webview.chromium.WebViewChromium$15.run(WebViewChromium.java:616)
    E/AndroidRuntime(21155): at com.android.webview.chromium.WebViewChromium$WebViewChromiumRunQueue.drainQueue(WebViewChromium.java:113)
    E/AndroidRuntime(21155): at com.android.webview.chromium.WebViewChromium$WebViewChromiumRunQueue$1.run(WebViewChromium.java:100)
    E/AndroidRuntime(21155): at android.os.Handler.handleCallback(Handler.java:733)
    E/AndroidRuntime(21155): at android.os.Handler.dispatchMessage(Handler.java:95)
    E/AndroidRuntime(21155): at android.os.Looper.loop(Looper.java:212)
    E/AndroidRuntime(21155): at android.app.ActivityThread.main(ActivityThread.java:5135)
    E/AndroidRuntime(21155): at java.lang.reflect.Method.invokeNative(Native Method)
    E/AndroidRuntime(21155): at java.lang.reflect.Method.invoke(Method.java:515)
    E/AndroidRuntime(21155): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:877)
    E/AndroidRuntime(21155): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)
    E/AndroidRuntime(21155): at dalvik.system.NativeStart.main(Native Method)@

    One more interesting fact, when I don't call loadURL that crash not happens, but I can't see a view. There must be black square 100x100 pixels, if my code is correct, but I can't see anything like this. Am I doing it wrong?

    Thanks!



  • I have checked in several forums and I have found this:

    The "stale local reference" error means that you're saving a local reference to some Java object between JNI calls;

    You need to convert that reference to a global reference using the NewGlobalRef method before doing anything that would cause the reference to persist outside the scope of the one JNI call.

    @jclass jc = env->FindClass(callbacks.name);
    // Since Android ICS, class references are not global so we need to peg a
    // global reference to the jclass returned by FindClass(), otherwise we get
    // following error in the log:
    // "JNI ERROR (app bug): attempt to use stale local reference 0xHHHHHHHH".
    callbacks._class = static_cast<jclass>(env->NewGlobalRef(jc));@

    In you case try to convert that reference to a global reference.....

    @jclass jc = env->FindClass(callbacks.name);
    callbacks._class = static_cast<jclass>(env->NewGlobalRef(jc));@

    in any case I think that the error is the following block:

    @void AndroidWebView::loadURL(QString url)
    {
    qDebug() << "AndroidWebView::loadURL" << url;
    QAndroidJniObject qObjUrlString = QAndroidJniObject::fromString(url);
    // The crash happens when I call this method
    qObjWebView.callObjectMethod("loadUrl", "(Ljava/lang/String;)V", qObjUrlString.object<jstring>());
    }@

    because loadUrl is a method of QDesktopServices class and I don't see this class in your code.

    your code will be the following:

    @void AndroidWebView::loadURL(QString url)
    {
    qDebug() << "AndroidWebView::loadURL" << url;

    QAndroidJniObject javaClass("QDesktopServices");

    QAndroidJniEnvironment env;

    jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());

    objectClass.callObjectMethod("loadUrl", "(Ljava/lang/String;)V", qObjUrlString.object<jstring>());

    }@


  • Lifetime Qt Champion

    Hi,

    There's now the "QtWebView module":https://qt.gitorious.org/qt/qtwebview/source/a2dd3fb028d731b3971e442db81b693c98849900 that can be used on android



  • [quote author="nologinma" date="1408629120"]
    You need to convert that reference to a global reference using the NewGlobalRef method before doing anything that would cause the reference to persist outside the scope of the one JNI call.
    [/quote]
    Hi! Thanks for help. I tried to call NewGlobalRef for all jobjects and jclasses I using in my code, but I still get the same error.

    [quote author="nologinma" date="1408629120"]
    because loadUrl is a method of QDesktopServices class and I don't see this class in your code.
    [/quote]
    But I think I don't need to call any methods of QDesktopServices class. loadUrl, in this case, is the method of android.webkit.WebView class and described "here":http://developer.android.com/reference/android/webkit/WebView.html#loadUrl(java.lang.String).

    [quote author="SGaist" date="1408630724"]Hi,
    There's now the "QtWebView module":https://qt.gitorious.org/qt/qtwebview/source/a2dd3fb028d731b3971e442db81b693c98849900 that can be used on android[/quote]
    Hi. Thanks, this looks very closely to my needs. But I don't sure how I should build this? Can I build it with Qt5.3?


  • Lifetime Qt Champion

    Using qmake, AFAIK yes



  • [quote author="SGaist" date="1408723811"]Using qmake, AFAIK yes[/quote]
    Got these results:
    Alexeys-MacBook-Pro:qtwebview alexeykolikov$ ~/Qt5.3.1/5.3/android_armv7/bin/qmake
    Alexeys-MacBook-Pro:qtwebview alexeykolikov$ make
    cd src/ && ( test -e Makefile || /Users/alexeykolikov/Qt5.3.1/5.3/android_armv7/bin/qmake /Users/alexeykolikov/Documents/github/qtwebview/src/src.pro -o Makefile ) && make -f Makefile
    cd webview/ && ( test -e Makefile || /Users/alexeykolikov/Qt5.3.1/5.3/android_armv7/bin/qmake /Users/alexeykolikov/Documents/github/qtwebview/src/webview/webview.pro -o Makefile ) && make -f Makefile
    Project WARNING: You should probably load(qt_build_config) first in webview.pro for QtWebView, as the latter also load()s qt_module.
    Project MESSAGE: Not doing so may lead to qt_module.prf overriding compiler/linker options in your .pro file.
    Project MESSAGE: Ignore this warning with CONFIG+=no_qt_module_warning if you know what you are doing.
    Project ERROR: Module does not define version.
    make[1]: *** [sub-webview-make_first] Error 3
    make: *** [sub-src-make_first] Error 2

    I guess it's not supposed to be built in this way. Looks like I should build it as a new Qt module like described "here":http://qt-project.org/wiki/Creating-a-new-module-or-tool-for-Qt#55af8d3b581bc7a96ac2a08eb19da92c. Please correct me if I'm wrong.



  • In my app I have used the following called in c++

    @QAndroidJniObject m_url = QAndroidJniObject::fromString("storage/sdcard0/gps/gps.kml");
    QAndroidJniObject m_application_type = QAndroidJniObject::fromString("application/vnd.google-earth.kml+xml");
    QAndroidJniObject::callStaticMethod<void>("org/qtproject/example/Chronometer/Next72Utility", "openUrl", "(Ljava/lang/String;Ljava/lang/String;)V",m_url.object<jstring>(),m_application_type.object<jstring>());@

    and the java class is the following:

    @//
    // Next72Utility.java
    //
    package org.qtproject.example.Chronometer;

    import android.content.Context;
    import android.app.Activity;
    import java.lang.Runnable;

    import android.content.Intent;
    import java.io.File;
    import android.net.Uri;

    public class Next72Utility extends org.qtproject.qt5.android.bindings.QtActivity
    {

    public static Next72Utility m_istance;

    public Next72Utility()
    {
    m_istance = this;
    }

    public static void openUrl(final String m_url, final String m_application_type)
    {
    m_istance.runOnUiThread(new Runnable() {
    public void run() {

             Intent intent = new Intent();
             intent.setAction(android.content.Intent.ACTION_VIEW);
             File file = new File&#40;m_url&#41;;
             intent.setDataAndType(Uri.fromFile&#40;file&#41;, m_application_type);
             m_istance.startActivity(intent);
         }
       });
    

    }

    }
    @

    I hope that solves your case.


  • Lifetime Qt Champion

    Indeed, it seems that there are some details missing. Patch underway



  • [quote author="nologinma" date="1408733638"]
    and the java class is the following.[/quote]

    Hi. What do I need to do for inclusion of .java files in my QtCreator poject?



  • I have write the steps to introduce the java code into qt project into follwing post: ? "[SOLVED] Qt on Android : How to Vibrate the device ?":https://qt-project.org/forums/viewreply/190435/

    I hope that it is usefull here.....



  • Now it works! Thank you very much, man!


Log in to reply