Qt on Android:使用JNI与第三方jar包

很多朋友在论坛和QQ群里问到这个,今天有时间写了个简单的示例。

    功能很简单,允许你输入一个web页面地址,使用Java的下载类库下载后用QTextEdit显示出来。

    版权所有:foruok。转载请注明出处:http://blog.csdn.net/foruok。

效果展示

    初始效果如图1所示:

Qt on Android:使用JNI与第三方jar包_第1张图片

                   图1 useJar示例初始效果

    图2为点击"GET"按钮后下载到对应页面的效果:

Qt on Android:使用JNI与第三方jar包_第2张图片

                   图2 下载页面成功

    下载部分,为了显示如何使用jar包,我用了asynchttpclient,参考我的博文:Android开源框架AsyncHttpClient (android-async-http)使用。

项目创建

    参考《Qt on Android:图文详解Hello World全过程》吧,没什么特别可说的。

    pro文件内添加“QT += androidextras”。

    创建一个AndroidManifest,package命名为an.qt.useJar。

    版权所有:foruok。转载请注明出处:http://blog.csdn.net/foruok。

添加Java源码

    你可以任意的文本编辑器中编辑java源码,然后通过Qt Creator项目视图加到项目里,在其它文件那里鼠标右键点击,选择添加现有文件即可。参考下面几张图吧。

Qt on Android:使用JNI与第三方jar包_第3张图片

                图3 添加Java源码之右键菜单

Qt on Android:使用JNI与第三方jar包_第4张图片

                       图4 添加Java源文件之选择Java源文件

Qt on Android:使用JNI与第三方jar包_第5张图片

                图5 添加Java源文件OK

    修改AndroidManifest,把activity标签的android:name属性值修改为an.qt.useJar.ExtendsQtWithJava。这是必须的,因为我们的ExtendsQtWithJava.java实现的Activity就是这个名字。

    好了,Java代码添加结束。

添加第三方jar包

    这个没什么好说的,放在android/libs目录下即可。看图:

Qt on Android:使用JNI与第三方jar包_第6张图片

              图6 放jar包

    只要放好位置,Qt Creator编译项目时就会把这个jar包打包到APK里。

Java源码使用jar包

    这是java编程的内容了,import包名,然后使用即可。

源码分析

    咱先看Java侧的代码吧。

Java代码

    ExtendsQtWithJava.java:

[java] view plain copy print ? 在CODE上查看代码片
  1. package an.qt.useJar; 
  2. import java.lang.String; 
  3. import android.content.Context; 
  4. import android.content.Intent; 
  5. import android.app.PendingIntent; 
  6. import android.os.Handler; 
  7. import android.os.Message; 
  8. import android.util.Log; 
  9. import android.net.ConnectivityManager; 
  10. import android.net.NetworkInfo; 
  11. import android.net.Uri; 
  12. import android.location.Criteria; 
  13. import android.provider.Settings; 
  14. import android.os.Bundle; 
  15. import android.os.Environment; 
  16. import java.io.File; 
  17. import com.loopj.android.http.AsyncHttpClient; 
  18. import com.loopj.android.http.AsyncHttpResponseHandler; 
  19.  
  20. public class ExtendsQtWithJava extends org.qtproject.qt5.android.bindings.QtActivity 
  21.     private static ExtendsQtWithJava m_instance; 
  22.     private final static String TAG = "extendsQt"
  23.     private static String m_pageUri = null
  24.     private static Handler m_handler = new Handler() { 
  25.         @Override 
  26.         public void handleMessage(Message msg) { 
  27.             switch (msg.what) { 
  28.             case 1
  29.                 if(m_pageUri == null){ 
  30.                     m_pageUri = (String)msg.obj; 
  31.                     m_instance.downloadText(m_pageUri); 
  32.                 }else
  33.                     m_instance.notifyQt(0, (String)msg.obj, "Downloader is Busy now!"); 
  34.                 } 
  35.                 break
  36.             }; 
  37.         } 
  38.     }; 
  39.      
  40.  
  41.     public ExtendsQtWithJava(){ 
  42.         m_instance = this
  43.     } 
  44.  
  45.     public static int networkState(){ 
  46.         ConnectivityManager conMan = (ConnectivityManager) m_instance.getSystemService(Context.CONNECTIVITY_SERVICE); 
  47.         return conMan.getActiveNetworkInfo() == null ? 0 : 1
  48.     } 
  49.  
  50.     public static AsyncHttpClient m_httpc = new AsyncHttpClient(); 
  51.     public static ExtendsQtNative m_nativeNotify = null
  52.  
  53.     public void downloadText(String uri){ 
  54.         Log.d(TAG, "start downloadText"); 
  55.         m_httpc.get(uri, null, new AsyncHttpResponseHandler(){ 
  56.             @Override 
  57.             public void onSuccess(String data){ 
  58.                 notifyQt(1, m_pageUri, data); 
  59.                 m_pageUri = null
  60.             } 
  61.             @Override 
  62.             public void onFailure(Throwable e, String data){ 
  63.                 notifyQt(-1, m_pageUri, data); 
  64.                 m_pageUri = null
  65.             } 
  66.         }); 
  67.     } 
  68.      
  69.     public static void downloadWebPage(String uri){ 
  70.         Log.d(TAG, "downloadWebPage"); 
  71.         m_handler.sendMessage(m_handler.obtainMessage(1, uri)); 
  72.     } 
  73.      
  74.     private void notifyQt(int result, String uri, String data){ 
  75.         if(m_nativeNotify == null){ 
  76.             m_nativeNotify = new ExtendsQtNative(); 
  77.         } 
  78.         m_nativeNotify.OnDownloaded(result, uri, data); 
  79.     } 
package an.qt.useJar;
import java.lang.String;
import android.content.Context;
import android.content.Intent;
import android.app.PendingIntent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.location.Criteria;
import android.provider.Settings;
import android.os.Bundle;
import android.os.Environment;
import java.io.File;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;

public class ExtendsQtWithJava extends org.qtproject.qt5.android.bindings.QtActivity
{
    private static ExtendsQtWithJava m_instance;
    private final static String TAG = "extendsQt";
    private static String m_pageUri = null;
    private static Handler m_handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 1:
                if(m_pageUri == null){
                    m_pageUri = (String)msg.obj;
                    m_instance.downloadText(m_pageUri);
                }else{
                    m_instance.notifyQt(0, (String)msg.obj, "Downloader is Busy now!");
                }
                break;
            };
        }
    };
    

    public ExtendsQtWithJava(){
        m_instance = this;
    }

    public static int networkState(){
        ConnectivityManager conMan = (ConnectivityManager) m_instance.getSystemService(Context.CONNECTIVITY_SERVICE);
        return conMan.getActiveNetworkInfo() == null ? 0 : 1;
    }

    public static AsyncHttpClient m_httpc = new AsyncHttpClient();
    public static ExtendsQtNative m_nativeNotify = null;

    public void downloadText(String uri){
        Log.d(TAG, "start downloadText");
        m_httpc.get(uri, null, new AsyncHttpResponseHandler(){
            @Override
            public void onSuccess(String data){
                notifyQt(1, m_pageUri, data);
                m_pageUri = null;
            }
            @Override
            public void onFailure(Throwable e, String data){
                notifyQt(-1, m_pageUri, data);
                m_pageUri = null;
            }
        });
    }
    
    public static void downloadWebPage(String uri){
        Log.d(TAG, "downloadWebPage");
        m_handler.sendMessage(m_handler.obtainMessage(1, uri));
    }
    
    private void notifyQt(int result, String uri, String data){
        if(m_nativeNotify == null){
            m_nativeNotify = new ExtendsQtNative();
        }
        m_nativeNotify.OnDownloaded(result, uri, data);
    }
}

    ExtendsQtNative.java:

[java] view plain copy print ? 在CODE上查看代码片
  1. package an.qt.useJar; 
  2. import java.lang.String; 
  3.  
  4. public class ExtendsQtNative 
  5.     public native void OnDownloaded(int result, String url, String content); 
package an.qt.useJar;
import java.lang.String;

public class ExtendsQtNative
{
    public native void OnDownloaded(int result, String url, String content);
}

    基本思路是酱紫的:

    Qt调用java的downloadWebPage,Java代码使用asynchttpclient下载一个网页,然后调用ExtendsQtNative通知Qt C++代码。

C++代码

    分两部分,一部分是实现JNI方法。另一部分是调用Java类的方法。

实现JNI方法并注册

    先看与ExtendsQtNative对应的JNI实现,在main.cpp中,都列出吧:

[cpp] view plain copy print ? 在CODE上查看代码片
  1. #include "widget.h" 
  2. #include <QApplication> 
  3. #include <QAndroidJniEnvironment> 
  4. #include <QAndroidJniObject> 
  5. #include <jni.h> 
  6. #include "../simpleCustomEvent.h" 
  7. #include <QDebug> 
  8.  
  9. QObject *g_listener = 0; 
  10.  
  11. // result: -1 failed; 1 success; 0 busy; 
  12. static void onDownloaded(JNIEnv *env, jobject thiz,int result, jstring uri, jstring data) 
  13.     QString qstrData; 
  14.     const char *nativeString = env->GetStringUTFChars(data, 0); 
  15.     qstrData = nativeString; 
  16.     env->ReleaseStringUTFChars(data, nativeString); 
  17.     QCoreApplication::postEvent(g_listener, new SimpleCustomEvent(result, qstrData)); 
  18.  
  19. bool registerNativeMethods() 
  20.     JNINativeMethod methods[] { 
  21.         {"OnDownloaded", "(ILjava/lang/String;Ljava/lang/String;)V", (void*)onDownloaded} 
  22.     }; 
  23.  
  24.     const char *classname = "an/qt/useJar/ExtendsQtNative"
  25.     jclass clazz; 
  26.     QAndroidJniEnvironment env; 
  27.  
  28.     QAndroidJniObject javaClass(classname); 
  29.     clazz = env->GetObjectClass(javaClass.object<jobject>()); 
  30.     qDebug() << "find ExtendsQtNative - " << clazz; 
  31.     bool result = false
  32.     if(clazz) 
  33.     { 
  34.         jint ret = env->RegisterNatives(clazz, 
  35.                                         methods, 
  36.                                         sizeof(methods) / sizeof(methods[0])); 
  37.         env->DeleteLocalRef(clazz); 
  38.         qDebug() << "RegisterNatives return - " << ret; 
  39.         result = ret >= 0; 
  40.     } 
  41.     if(env->ExceptionCheck()) env->ExceptionClear(); 
  42.     return result; 
  43.  
  44. int main(int argc, char *argv[]) 
  45.     QApplication a(argc, argv); 
  46.  
  47.     SimpleCustomEvent::eventType(); 
  48.     registerNativeMethods(); 
  49.  
  50.     Widget w; 
  51.     g_listener = qobject_cast<QObject*>(&w); 
  52.     w.show(); 
  53.  
  54.     return a.exec(); 
#include "widget.h"
#include <QApplication>
#include <QAndroidJniEnvironment>
#include <QAndroidJniObject>
#include <jni.h>
#include "../simpleCustomEvent.h"
#include <QDebug>

QObject *g_listener = 0;

// result: -1 failed; 1 success; 0 busy;
static void onDownloaded(JNIEnv *env, jobject thiz,int result, jstring uri, jstring data)
{
    QString qstrData;
    const char *nativeString = env->GetStringUTFChars(data, 0);
    qstrData = nativeString;
    env->ReleaseStringUTFChars(data, nativeString);
    QCoreApplication::postEvent(g_listener, new SimpleCustomEvent(result, qstrData));
}

bool registerNativeMethods()
{
    JNINativeMethod methods[] {
        {"OnDownloaded", "(ILjava/lang/String;Ljava/lang/String;)V", (void*)onDownloaded}
    };

    const char *classname = "an/qt/useJar/ExtendsQtNative";
    jclass clazz;
    QAndroidJniEnvironment env;

    QAndroidJniObject javaClass(classname);
    clazz = env->GetObjectClass(javaClass.object<jobject>());
    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;
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    SimpleCustomEvent::eventType();
    registerNativeMethods();

    Widget w;
    g_listener = qobject_cast<QObject*>(&w);
    w.show();

    return a.exec();
}

    注册JNI方法,设置一个全局的对象接收通知。具体的,参考Qt帮助来理解。

调用Java方法

    对Java方法的调用在Widget.cpp中。直接看代码吧。

    widget.h:

[cpp] view plain copy print ? 在CODE上查看代码片
  1. #ifndef WIDGET_H 
  2. #define WIDGET_H 
  3.  
  4. #include <QWidget> 
  5. #include <QLineEdit> 
  6. #include <QTextEdit> 
  7. #include <QLabel> 
  8.  
  9. class Widget : public QWidget 
  10.     Q_OBJECT 
  11.  
  12. public
  13.     Widget(QWidget *parent = 0); 
  14.     ~Widget(); 
  15.  
  16.     bool event(QEvent *e); 
  17.  
  18. public slots: 
  19.     void onGet(); 
  20.  
  21. private
  22.     QLineEdit * m_urlEdit; 
  23.     QTextEdit * m_resultView; 
  24.     QLabel * m_stateLabel; 
  25. }; 
  26.  
  27. #endif // WIDGET_H 
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QLineEdit>
#include <QTextEdit>
#include <QLabel>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();

    bool event(QEvent *e);

public slots:
    void onGet();

private:
    QLineEdit * m_urlEdit;
    QTextEdit * m_resultView;
    QLabel * m_stateLabel;
};

#endif // WIDGET_H

    都是界面相关的,没什么好说的。看widget.cpp:

[cpp] view plain copy print ? 在CODE上查看代码片
  1. #include "widget.h" 
  2. #include <QVBoxLayout> 
  3. #include <QPushButton> 
  4. #include "../simpleCustomEvent.h" 
  5. #include <QAndroidJniObject> 
  6. #include <QAndroidJniEnvironment> 
  7.  
  8. Widget::Widget(QWidget *parent) 
  9.     : QWidget(parent) 
  10.     QVBoxLayout *layout = new QVBoxLayout(this); 
  11.     QHBoxLayout *getLayout = new QHBoxLayout(); 
  12.     layout->addLayout(getLayout); 
  13.     m_urlEdit = new QLineEdit("http://blog.csdn.net/foruok"); 
  14.     getLayout->addWidget(m_urlEdit, 1); 
  15.     QPushButton *getButton = new QPushButton("GET"); 
  16.     getLayout->addWidget(getButton); 
  17.     connect(getButton, SIGNAL(clicked()), this, SLOT(onGet())); 
  18.  
  19.     m_resultView = new QTextEdit(); 
  20.     m_resultView->setReadOnly(true); 
  21.     layout->addWidget(m_resultView, 1); 
  22.     m_stateLabel = new QLabel(); 
  23.     layout->addWidget(m_stateLabel); 
  24.  
  25. Widget::~Widget() 
  26.  
  27.  
  28. bool Widget::event(QEvent *e) 
  29.     if(e->type() == SimpleCustomEvent::eventType()) 
  30.     { 
  31.         e->accept(); 
  32.         SimpleCustomEvent *sce = (SimpleCustomEvent*)e; 
  33.         switch(sce->m_arg1) 
  34.         { 
  35.         case 1: 
  36.             m_resultView->setText(sce->m_arg2); 
  37.             m_stateLabel->setText("Success!"); 
  38.             break
  39.         case 0: 
  40.             m_resultView->setText(sce->m_arg2); 
  41.             m_stateLabel->setText("Failed!"); 
  42.             break
  43.         case -1: 
  44.             m_stateLabel->setText(sce->m_arg2); 
  45.             break
  46.         } 
  47.         return true
  48.     } 
  49.     return QWidget::event(e); 
  50.  
  51. void Widget::onGet() 
  52. #ifdef WIN32 
  53.     m_resultView->setText("Sorry, Just for Android!"); 
  54. #elif defined(ANDROID) 
  55.     QString url = m_urlEdit->text(); 
  56.     QAndroidJniObject javaAction = QAndroidJniObject::fromString(url); 
  57.     QAndroidJniObject::callStaticMethod<void>("an/qt/useJar/ExtendsQtWithJava"
  58.                                        "downloadWebPage"
  59.                                        "(Ljava/lang/String;)V"
  60.                                        javaAction.object<jstring>()); 
  61.     m_stateLabel->setText("Downloading..."); 
  62. #endif 
#include "widget.h"
#include <QVBoxLayout>
#include <QPushButton>
#include "../simpleCustomEvent.h"
#include <QAndroidJniObject>
#include <QAndroidJniEnvironment>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    QVBoxLayout *layout = new QVBoxLayout(this);
    QHBoxLayout *getLayout = new QHBoxLayout();
    layout->addLayout(getLayout);
    m_urlEdit = new QLineEdit("http://blog.csdn.net/foruok");
    getLayout->addWidget(m_urlEdit, 1);
    QPushButton *getButton = new QPushButton("GET");
    getLayout->addWidget(getButton);
    connect(getButton, SIGNAL(clicked()), this, SLOT(onGet()));

    m_resultView = new QTextEdit();
    m_resultView->setReadOnly(true);
    layout->addWidget(m_resultView, 1);
    m_stateLabel = new QLabel();
    layout->addWidget(m_stateLabel);
}

Widget::~Widget()
{

}

bool Widget::event(QEvent *e)
{
    if(e->type() == SimpleCustomEvent::eventType())
    {
        e->accept();
        SimpleCustomEvent *sce = (SimpleCustomEvent*)e;
        switch(sce->m_arg1)
        {
        case 1:
            m_resultView->setText(sce->m_arg2);
            m_stateLabel->setText("Success!");
            break;
        case 0:
            m_resultView->setText(sce->m_arg2);
            m_stateLabel->setText("Failed!");
            break;
        case -1:
            m_stateLabel->setText(sce->m_arg2);
            break;
        }
        return true;
    }
    return QWidget::event(e);
}

void Widget::onGet()
{
#ifdef WIN32
    m_resultView->setText("Sorry, Just for Android!");
#elif defined(ANDROID)
    QString url = m_urlEdit->text();
    QAndroidJniObject javaAction = QAndroidJniObject::fromString(url);
    QAndroidJniObject::callStaticMethod<void>("an/qt/useJar/ExtendsQtWithJava",
                                       "downloadWebPage",
                                       "(Ljava/lang/String;)V",
                                       javaAction.object<jstring>());
    m_stateLabel->setText("Downloading...");
#endif
}

    调用Java的代码在onGet()槽中,很简单,不解释了。有疑问看Qt帮助手册有关QAndroidJniObject类的说明。


    OK,到此结束。

    版权所有:foruok。转载请注明出处:http://blog.csdn.net/foruok。

    我的Qt on Android系列文章:

  • Qt on Android:图文详解Hello World全过程
  • Windows下Qt 5.2 for Android开发入门
  • Qt for Android 部署流程分析
  • Qt on Android:将Qt调试信息输出到logcat中
  • Qt on Android: Qt 5.3.0 发布,针对 Android 改进说明
  • Qt on Android Episode 1(翻译)
  • Qt on Android Episode 2(翻译)
  • Qt on Android Episode 3(翻译)
  • Qt on Android Episode 4(翻译)
  • Qt for Android 编译纯C工程
  • Windows下Qt for Android 编译安卓C语言可执行程序
  • Qt on Android: Android SDK安装
  • Qt on Android: http下载与Json解析
  • Qt on Android 之设置应用名为中文
  • Qt on Android:让 Qt Widgets 和 Qt Quick 应用全屏显示
  • Qt on Android:怎样适应不同的屏幕尺寸

你可能感兴趣的:(Qt on Android:使用JNI与第三方jar包)