Qt on Android:使用JNI與第三方j(luò)ar包
很多朋友在論壇和QQ群里問到這個(gè),今天有時(shí)間寫了個(gè)簡單的示例。
功能很簡單,允許你輸入一個(gè)web頁面地址,使用Java的下載類庫下載后用QTextEdit顯示出來。
效果展示
初始效果如圖1所示:
圖1 useJar示例初始效果
圖2為點(diǎn)擊GET按鈕后下載到對(duì)應(yīng)頁面的效果:
圖2 下載頁面成功
下載部分,為了顯示如何使用jar包,我用了asynchttpclient,參考我的博文:Android開源框架AsyncHttpClient (android-async-http)使用。
項(xiàng)目創(chuàng)建
參考《Qt on Android:圖文詳解Hello World全過程》吧,沒什么特別可說的。
pro文件內(nèi)添加“QT += androidextras”。
創(chuàng)建一個(gè)AndroidManifest,package命名為an.qt.useJar。
版權(quán)所有:foruok。轉(zhuǎn)載請注明出處:http://blog.csdn.net/foruok。
添加Java源碼
你可以任意的文本編輯器中編輯java源碼,然后通過Qt Creator項(xiàng)目視圖加到項(xiàng)目里,在其它文件那里鼠標(biāo)右鍵點(diǎn)擊,選擇添加現(xiàn)有文件即可。參考下面幾張圖吧。
圖3 添加Java源碼之右鍵菜單
圖4 添加Java源文件之選擇Java源文件
圖5 添加Java源文件OK
修改AndroidManifest,把a(bǔ)ctivity標(biāo)簽的android:name屬性值修改為an.qt.useJar.ExtendsQtWithJava。這是必須的,因?yàn)槲覀兊腅xtendsQtWithJava.java實(shí)現(xiàn)的Activity就是這個(gè)名字。
好了,Java代碼添加結(jié)束。
添加第三方j(luò)ar包
這個(gè)沒什么好說的,放在android/libs目錄下即可??磮D:
圖6 放jar包
只要放好位置,Qt Creator編譯項(xiàng)目時(shí)就會(huì)把這個(gè)jar包打包到APK里。
Java源碼使用jar包
這是java編程的內(nèi)容了,import包名,然后使用即可。
源碼分析
咱先看Java側(cè)的代碼吧。
Java代碼
ExtendsQtWithJava.java:
81package 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){[!--empirenews.page--]
if(m_nativeNotify == null){
m_nativeNotify = new ExtendsQtNative();
}
m_nativeNotify.OnDownloaded(result, uri, data);
}
}
ExtendsQtNative.java:
? 1
2
3
4
5
6
7package an.qt.useJar;
import java.lang.String;
public class ExtendsQtNative
{
public native void OnDownloaded(int result, String url, String content);
}
基本思路是醬紫的:
Qt調(diào)用java的downloadWebPage,Java代碼使用asynchttpclient下載一個(gè)網(wǎng)頁,然后調(diào)用ExtendsQtNative通知Qt C++代碼。
C++代碼
分兩部分,一部分是實(shí)現(xiàn)JNI方法。另一部分是調(diào)用Java類的方法。
實(shí)現(xiàn)JNI方法并注冊
先看與ExtendsQtNative對(duì)應(yīng)的JNI實(shí)現(xiàn),在main.cpp中,都列出吧:
61#include widget.h
#include
#include
#include
#include
#include ../simpleCustomEvent.h
#include
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
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
w.show();
return a.exec();
}
注冊JNI方法,設(shè)置一個(gè)全局的對(duì)象接收通知。具體的,參考Qt幫助來理解。
調(diào)用Java方法
對(duì)Java方法的調(diào)用在Widget.cpp中。直接看代碼吧。
widget.h:
? 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
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
都是界面相關(guān)的,沒什么好說的??磜idget.cpp:
? 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71#include widget.h
#include
#include
#include ../simpleCustomEvent.h
#include
#include
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);[!--empirenews.page--]
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
downloadWebPage,
(Ljava/lang/String;)V,
javaAction.object
m_stateLabel->setText(Downloading...);
#endif
}
調(diào)用Java的代碼在onGet()槽中,很簡單,不解釋了。有疑問看Qt幫助手冊有關(guān)QAndroidJniObject類的說明。
OK,到此結(jié)束。