Qt5.6 安卓下的WebView如何实现JavaScript通信?
扫描二维码
随时随地手机看文章
Qt官方的Webview仅仅在Qtquick中支持安卓和ios,安卓的官方实现是调用安卓系统自带的浏览器API,但是Qt官方没有写js交互,于是研究了一通宵得出几个解决方案:
1.使用Qt官方的QML webview和HTML5的Websockt,在js中使用websockt,在qt qucik中使用websocket服务器,结果可行,可惜安卓很多版本不支持Websockt,于是乎3个小时过去了。
2.既然websockt行不通,就想了下socket.io库,能运行~可惜qt quick的Websockt服务器不支持socket.io的协议,于是乎 3小时又浪费了
3.使用HTML sockt或post,post要执行的函数,用QML的eval函数来执行字符串代码,于是乎用xhl来发送post数据,然后用C++建立TcpSocket来接收,但是我放弃了,解析HTML封包不划算,为了发送4个字符他会发一大堆HTML协议字符串…因为 socket由C++接收.websockt在 qt quick中,js post->sockt->qt quick 要跨3层交互才收的到数据,实在蛋疼…更重要的是还要避免意外情况,于是乎2小时又浪费了
4.最后花了5个小时,在国外看到一些开源代码,经过修改 实现了原生交互,依然是调用安卓的webview,内嵌到qt中
具体实现:c++ –> java –> js
因为安卓的浏览器和java的交互是最稳定的
先生成Qt 安卓项目,先编译一次,在目录下新建android文件夹(QT中可以创建) 和 assets文件夹
把androud-build中的src目录下全部文件复制到android目录,打开eclipse,在QtActivity包下添加一个类:
下面代码是参考过来的:
package org.qtproject.qt5.android.bindings; public interface NativeCalls { public void createNewWebView(int tag); public void removeWebView(int tag); public void moveWebView(int tag, int x, int y); public void resizeWebView(int tag, int w, int h); public void attachWebViewToMainLayout(int tag); public void loadUrlAtWebView(int tag, String url); public void loadHtmlAtWebView(int tag, String html); }
然后再添加一个类,用来继承webview,定制一些功能,比如开启js和弹窗等,并且添加JavaScript交互接口,接口函数在C++中定义,在JAVA中声明、
package org.qtproject.qt5.android.bindings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.FrameLayout; import android.webkit.*; import android.content.Context; import android.util.Log; public class Cjavascript { public Cjavascript ( ) { } public native int testcall2(); public native void testcall3(); public native void testcall4(); public void testcall() { Log.i("testcalltestcalltestcalltestcall", "call:"); } }
package org.qtproject.qt5.android.bindings; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Color; import android.webkit.JsResult; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; public class MyWebView extends WebView { public MyWebView(Context context) { super(context); init(); // TODO 自动生成的构造函数存根 } @SuppressLint("SetJavaScriptEnabled") public void init() { this.getSettings().setJavaScriptEnabled(true); this.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); //否则不会执行js的Alert this.setWebChromeClient(new WebChromeClient() { public boolean onJsAlert(WebView view, String url, String message, JsResult result) { //Auto-generated method stub return super.onJsAlert(view, url, message, result); } }); this.setWebViewClient(new WebViewClient()); this.setBackgroundColor(Color.BLACK); this.addJavascriptInterface(new Cjavascript(), "obj"); } }
最后就是修改Qt自己生成的QtActivity类,在作用域中添加以下代码即可:
下面的代码大部分参考别人的,少量修改。官方源码中都是使用RunOnUiThread,下面的是用handle进行多线程界面操作。
//-------------WEBVIEW---------------- protected ArrayListwebViewsList = new ArrayList(); //----------------------------------------------------------------------------- protected MyWebView findWebViewByTag(int tag) { MyWebView webViewRes = null; for (int i = 0; i < webViewsList.size(); i++) { MyWebView wv = (MyWebView) webViewsList.get(i); if (((Integer)wv.getTag()).intValue() == tag) { webViewRes = wv; break; } } return webViewRes; } //----------------------------------------------------------------------------- @Override public void createNewWebView(int tag) { Message msg = new Message(); msg.what = tag; createNewWebViewHandler.sendMessage(msg); } protected Handler createNewWebViewHandler = new Handler() { @SuppressLint("SetJavaScriptEnabled") @Override public void handleMessage(Message msg) { MyWebView webView = new MyWebView(QtActivity.this); webView.setTag(msg.what); // webView.getSettings().setJavaScriptEnabled(true); // webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); // //否则不会执行js的Alert // webView.setWebChromeClient(new WebChromeClient() // { public boolean onJsAlert(WebView view, String url, String message, // JsResult result) // { // //Auto-generated method stub // return super.onJsAlert(view, url, message, result); // } // // }); // webView.setWebViewClient(new WebViewClient()); // webView.setBackgroundColor(Color.BLACK); webViewsList.add(webView); } }; //----------------------------------------------------------------------------- @Override public void removeWebView(int tag) { Message msg = new Message(); msg.what = tag; removeWebViewHandler.sendMessage(msg); } protected Handler removeWebViewHandler = new Handler() { @Override public void handleMessage(Message msg) { MyWebView webView = findWebViewByTag(msg.what); if (webView != null) { webViewsList.remove(webView); FrameLayout mainLayout = (FrameLayout) findViewById(R.id.content); mainLayout.removeView(webView); } } }; //----------------------------------------------------------------------------- @Override public void moveWebView(int tag, int x, int y) { Message msg = new Message(); msg.what = tag; msg.arg1 = x; msg.arg2 = y; moveWebViewHandler.sendMessage(msg); } protected Handler moveWebViewHandler = new Handler() { @Override public void handleMessage(Message msg) { View viewToMove = null; viewToMove = findWebViewByTag(msg.what); if (viewToMove != null) { FrameLayout.LayoutParams params = (android.widget.FrameLayout.LayoutParams) viewToMove .getLayoutParams(); params.leftMargin = msg.arg1; params.topMargin = msg.arg2; viewToMove.setLayoutParams(params); } } }; //----------------------------------------------------------------------------- @Override public void resizeWebView(int tag, int w, int h) { Message msg = new Message(); msg.what = tag; msg.arg1 = w; msg.arg2 = h; resizeWebViewHandler.sendMessage(msg); } protected Handler resizeWebViewHandler = new Handler() { @Override public void handleMessage(Message msg) { View viewToResize = null; viewToResize = findWebViewByTag(msg.what); if (viewToResize != null) { FrameLayout.LayoutParams params = (android.widget.FrameLayout.LayoutParams) viewToResize .getLayoutParams(); params.width = msg.arg1; params.height = msg.arg2; viewToResize.setLayoutParams(params); } } }; //----------------------------------------------------------------------------- @Override public void attachWebViewToMainLayout(int tag) { Message msg = new Message(); msg.what = tag; attachWebViewToMainLayoutHandler.sendMessage(msg); } protected Handler attachWebViewToMainLayoutHandler = new Handler() { @Override public void handleMessage(Message msg) { View viewToAttach = null; viewToAttach = findWebViewByTag(msg.what); if (viewToAttach != null) { FrameLayout mainLayout = (FrameLayout) findViewById(R.id.content); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(100, 100); params.leftMargin = 0; params.topMargin = 0; mainLayout.addView(viewToAttach, params); } } }; //----------------------------------------------------------------------------- @Override public void loadUrlAtWebView(int tag, String url) { Log.i("url", "curid:"+Thread.currentThread().getId()); Message msg = new Message(); msg.what = tag; msg.obj = url; loadUrlAtWebViewHandler.sendMessage(msg); } protected Handler loadUrlAtWebViewHandler = new Handler() { @Override public void handleMessage(Message msg) { Log.i("url", "mainid:"+Thread.currentThread().getId()); MyWebView webView = findWebViewByTag(msg.what); if (webView != null) { webView.loadUrl(msg.obj.toString()); Log.i("url", msg.obj.toString()); } //Log.i("TestNative*********", "call:"); //TestNative testNative = new TestNative(); //testNative.Test(); } }; //----------------------------------------------------------------------------- @Override public void loadHtmlAtWebView(int tag, String html) { Message msg = new Message(); msg.what = tag; msg.obj = html; loadHtmlAtWebViewHandler.sendMessage(msg); } protected Handler loadHtmlAtWebViewHandler = new Handler() { @Override public void handleMessage(Message msg) { MyWebView webView = findWebViewByTag(msg.what); if (webView != null) { webView.loadData(msg.obj.toString(), "text/html; charset=UTF-8", "UTF-8"); } } };
C++层,注册JavaScript函数:
#ifndef JAVA_JAVASCTRIPTFUNC_H #define JAVA_JAVASCTRIPTFUNC_H #include#include#includeclass JAVA_javasctriptFunc { public: JAVA_javasctriptFunc(); bool regfuc(); }; #endif // JAVA_JAVASCTRIPTFUNC_H #include "java_javasctriptfunc.h" int cout=0; static jint testcall2(JNIEnv *env, jobject thiz) { cout++; qInfo() << "MY C+++: testcall2"<<QThread::currentThreadId() ; return cout; // QMessageBox::information(NULL,"HEHE","33"); } static void testcall3(JNIEnv *env, jobject thiz) { qInfo() << "MY C+++: testcall3"<<QThread::currentThreadId() ; // QMessageBox::information(NULL,"HEHE","33"); } static void testcall4(JNIEnv *env, jobject thiz) { qInfo() << "MY C+++: testcall4"<GetObjectClass(javaClass.object()); qDebug() << "find ExtendsQtNative - " << clazz; bool result = false; if(clazz) { jint ret = env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])); env->DeleteLocalRef(clazz); qDebug() << "RegisterNatives return - " << ret; result = ret >= 0; } if(env->ExceptionCheck()) env->ExceptionClear(); return result; }
WEBVIEW的C++层实现:
#pragma once #include#include#includeclass CAndroidNativeCallsSender { public: CAndroidNativeCallsSender(); ~CAndroidNativeCallsSender(); void createNewWebView( int id ) const; void removeWebView( int id ) const; void attachWebViewToMainLayout( int id ) const; void moveWebView( int id, int x, int y ) const; void resizeWebView( int id, int w, int h ) const; void loadUrlAtWebView( int id, QString const& url ) const; void loadHtmlAtWebView( int id, QString const& html ) const; protected: jmethodID m_createNewWebViewMID; jmethodID m_removeWebViewMID; jmethodID m_attachWebViewToMainLayoutMID; jmethodID m_moveWebViewMID; jmethodID m_resizeWebViewMID; jmethodID m_loadUrlAtWebViewMID; jmethodID m_loadHtmlAtWebViewMID; jobject m_objectRef; };
#include "AndroidNativeCallsSender.h" #include//----------------------------------------------------------------------------- CAndroidNativeCallsSender::CAndroidNativeCallsSender() { QAndroidJniEnvironment jniEnv; QAndroidJniObject qObjAct = QAndroidJniObject::callStaticObjectMethod( "org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;" ); jobject objAct = qObjAct.object(); m_objectRef = jniEnv->NewGlobalRef( objAct ); jclass cls = jniEnv->GetObjectClass( objAct ); if( cls ) { m_createNewWebViewMID = jniEnv->GetMethodID( cls, "createNewWebView", "(I)V" ); m_removeWebViewMID = jniEnv->GetMethodID( cls, "removeWebView", "(I)V" ); m_attachWebViewToMainLayoutMID = jniEnv->GetMethodID( cls, "attachWebViewToMainLayout", "(I)V" ); m_moveWebViewMID = jniEnv->GetMethodID( cls, "moveWebView", "(III)V" ); m_resizeWebViewMID = jniEnv->GetMethodID( cls, "resizeWebView", "(III)V" ); m_loadUrlAtWebViewMID = jniEnv->GetMethodID( cls, "loadUrlAtWebView", "(ILjava/lang/String;)V" ); m_loadHtmlAtWebViewMID = jniEnv->GetMethodID( cls, "loadHtmlAtWebView", "(ILjava/lang/String;)V" ); } } //----------------------------------------------------------------------------- CAndroidNativeCallsSender::~CAndroidNativeCallsSender() { if( m_objectRef != NULL ) { QAndroidJniEnvironment jniEnv; jniEnv->DeleteGlobalRef( m_objectRef ); } } //----------------------------------------------------------------------------- void CAndroidNativeCallsSender::createNewWebView( int id ) const { if( m_createNewWebViewMID ) { QAndroidJniEnvironment jniEnv; jniEnv->CallVoidMethod( m_objectRef, m_createNewWebViewMID, id ); } } //----------------------------------------------------------------------------- void CAndroidNativeCallsSender::removeWebView( int id ) const { if( m_removeWebViewMID ) { QAndroidJniEnvironment jniEnv; jniEnv->CallVoidMethod( m_objectRef, m_removeWebViewMID, id ); } } //----------------------------------------------------------------------------- void CAndroidNativeCallsSender::attachWebViewToMainLayout( int id ) const { if( m_attachWebViewToMainLayoutMID ) { QAndroidJniEnvironment jniEnv; jniEnv->CallVoidMethod( m_objectRef, m_attachWebViewToMainLayoutMID, id ); } } //----------------------------------------------------------------------------- void CAndroidNativeCallsSender::moveWebView( int id, int x, int y ) const { if( m_moveWebViewMID ) { QAndroidJniEnvironment jniEnv; jniEnv->CallVoidMethod( m_objectRef, m_moveWebViewMID, id, x, y ); } } //----------------------------------------------------------------------------- void CAndroidNativeCallsSender::resizeWebView( int id, int w, int h ) const { if( m_resizeWebViewMID ) { QAndroidJniEnvironment jniEnv; jniEnv->CallVoidMethod( m_objectRef, m_resizeWebViewMID, id, w, h ); } } //----------------------------------------------------------------------------- void CAndroidNativeCallsSender::loadUrlAtWebView( int id, QString const& url ) const { if( m_loadUrlAtWebViewMID ) { QAndroidJniEnvironment jniEnv; jstring jurl = jniEnv->NewStringUTF( url.toUtf8().constData() ); jniEnv->CallVoidMethod( m_objectRef, m_loadUrlAtWebViewMID, id, jurl ); jniEnv->DeleteLocalRef( jurl ); } } //----------------------------------------------------------------------------- void CAndroidNativeCallsSender::loadHtmlAtWebView( int id, QString const& html ) const { if( m_loadUrlAtWebViewMID ) { QAndroidJniEnvironment jniEnv; jstring jhtml = jniEnv->NewStringUTF( html.toUtf8().constData() ); jniEnv->CallVoidMethod( m_objectRef, m_loadHtmlAtWebViewMID, id, jhtml ); jniEnv->DeleteLocalRef( jhtml ); } }
#pragma once #include#include#include "AndroidNativeCallsSender.h" class QtCustomAndroidWebView : public QWidget { Q_OBJECT public: explicit QtCustomAndroidWebView( QWidget *parent = 0 ); ~QtCustomAndroidWebView(); void loadURL( QString const& url ) const; void loadHtmlData( QString const& data ) const; void move( int x, int y ); void move( QPoint const& p ); void resize( int w, int h ); int androidID() const { return m_androidID; } protected: static int sm_tag; int m_androidID; CAndroidNativeCallsSender m_nativeSender; int generateNewTag(); signals: public slots: };
#include "QtCustomAndroidWebView.h" //#include//----------------------------------------------------------------------------- int QtCustomAndroidWebView::sm_tag = 0; //----------------------------------------------------------------------------- QtCustomAndroidWebView::QtCustomAndroidWebView( QWidget *parent ) : QWidget( parent ) , m_androidID( generateNewTag() ) { // if( parent && parent->layout() ) // parent->layout()->addWidget( this ); m_nativeSender.createNewWebView( androidID() ); m_nativeSender.attachWebViewToMainLayout( androidID() ); } //----------------------------------------------------------------------------- QtCustomAndroidWebView::~QtCustomAndroidWebView() { m_nativeSender.removeWebView( androidID() ); } //----------------------------------------------------------------------------- void QtCustomAndroidWebView::loadURL( QString const& url ) const { m_nativeSender.loadUrlAtWebView( androidID(), url ); } //----------------------------------------------------------------------------- void QtCustomAndroidWebView::loadHtmlData( QString const& data ) const { m_nativeSender.loadHtmlAtWebView( androidID(), data ); } //----------------------------------------------------------------------------- void QtCustomAndroidWebView::move( int x, int y ) { QWidget::move( x, y ); m_nativeSender.moveWebView( androidID(), x, y ); } //----------------------------------------------------------------------------- void QtCustomAndroidWebView::move( QPoint const& p ) { QtCustomAndroidWebView::move( p.x(), p.y() ); } //----------------------------------------------------------------------------- void QtCustomAndroidWebView::resize( int w, int h ) { QWidget::resize( w, h ); m_nativeSender.resizeWebView( androidID(), w, h ); } //----------------------------------------------------------------------------- int QtCustomAndroidWebView::generateNewTag() { sm_tag++; return sm_tag; }
测试代码
#include "mytest.h" #include "QtCustomAndroidWebView.h" #include#include#include#includeMyTest::MyTest(QWidget *parent) : QWidget(parent) { vbox = new QVBoxLayout(this); btn = new QPushButton("call"); btn->setText("CALL"); edit = new QTextEdit(); vbox->addWidget(btn); vbox->addWidget(edit); // webview = new QtCustomAndroidWebView(); // webview->resize(this->width(),this->height()); // webview->move(0,0); // webview->loadURL("http://www.baidu.com"); // vbox->addWidget(webview); QScreen *screen = QApplication::primaryScreen(); webview = new QtCustomAndroidWebView( ); webview->resize(this->width(), 200); webview->loadURL("file:///android_asset/index.html"); //注册js函数 javajavasctriptFunc = new JAVA_javasctriptFunc(); if(javajavasctriptFunc->regfuc()==false) { qDebug()<addWidget(webview); connect(btn,SIGNAL(clicked(bool)),this,SLOT(onclick())); } void MyTest::onclick() { int cout=0; while (1) { QThread::msleep(200); QString s; s.sprintf("javascript:test2(%d)",cout); cout++; webview->loadURL(s); // 执行js脚本 返回值可参考Qt android src中WebView的实现 webview->loadURL("javascript:test()"); } }
$(function(){ $("button").click(function(){ var x=obj.testcall2(); $("#list1").append("Inbox"+x+""); $('#list1').listview("refresh"); }); });
如何调用JavaScript能获取返回值,可参考Qt的源码,其实没啥必要,只需要注册一个函数给js调用,让他把返回值弄进来就是
m_webSettingsSetDisplayZoomControls = webSettings.getClass().getMethod("setDisplayZoomControls", boolean.class); if (Build.VERSION.SDK_INT > 18) { m_webViewEvaluateJavascript = m_webView.getClass().getMethod("evaluateJavascript", String.class, ValueCallback.class); }
public void runJavaScript(final String script, final long callbackId) { if (script == null) return; if (Build.VERSION.SDK_INT < 19 || m_webViewEvaluateJavascript == null) return; m_activity.runOnUiThread(new Runnable() { @Override public void run() { try { m_webViewEvaluateJavascript.invoke(m_webView, script, callbackId == -1 ? null : new ValueCallback() { @Override public void onReceiveValue(String result) { c_onRunJavaScriptResult(m_id, callbackId, result); } }); } catch (Exception e) { e.printStackTrace(); } } }); }