`
linuxstuding
  • 浏览: 1231842 次
文章分类
社区版块
存档分类
最新评论

我眼中的Qt for Android

 
阅读更多
我眼中的Qt for Android

引子

前几天,我分享了一下qt for android,从大家的反应和回馈,我看到两种极端的状态。一个是:“太好了!想做Android开发但是不想转java,这下不用了!” 另一个是:“不要在Qt上浪费时间了,它顶多在Android上跑个Hello world,别的什么也跑不了。”
我先说说我对Qt for Android的客观认识。首先,从现有阶段看,不得不承认TA并不是一个成熟的技术(工具)。在大型项目中,还是不建议使用qt for android开发的,因为资料太少,我们无法快速深入的在大脑建立起qt for android网络,在遇到问题的时候,解决起来就很棘手。但是,绝不是说仅仅就能跑个Hello world,如果真的这么一无是处,TA就没有存在的意义,也就不会吸引大批开发者深入研究和优化了。要知道,世上最简单的事情就是批评和怒斥。我再次强调一下,我只是分享我所看到的知道的,不带任何向导性。对于技术本身,仁者见仁,智者见智


品味与探究

当我看到这么一个技术工具,我的好奇心驱使我探究一下(1)TA到底是如何实现的,(2)程序在Android上执行效率和性能怎么样,(3)较常规的Android java开发和jni c++开发而言,两者之间有什么可以相互借鉴, (4)倘若Google真的开放纯c++开发,那么java和qt for android又是怎样的一番光景?

一个开发者分享他某一个程序的设计思路:在Qt下通过jni得到java Env,从而使用GPS等android API,并且已经实现:


也许因为我对Qt信号和槽的情有独钟,看到emit就感到很亲切,并且被深深的吸引了。那么从Qt for android 的qt工程源码看,到底是如何在android上成功启动并运行的呢?

启动流程分析

用qt-creator创建的每个应用程序中,src下的文件都是基本相同。因为启动程序,创建接口,链接库,这些操作是每个应用程序所必需的,最初的qt程序被编译成了lib**.so的动态库,当调用JNI接口startQtApp函数时真正启动了qt程序。
执行程序的入口在src/eu/licentia/necessitas/industrius/QtActivity.java中,onCreate调用startapp检查必要的库文件,扩展包,插件是否存在,并加载.链接ministro服务,得知ministro服务现在的状态,如果缺少qt库则需要借助ministro服务下载。

private void startApp(final boolean firstStart)
{
try
{
ActivityInfo ai=getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
if (!ai.metaData.containsKey("android.app.qt_libs_resource_id"))
{
// No required qt libs ?
// Probably this application was compiled using static qt libs
// or all qt libs are prebundled into the package
m_ministroCallback.libs(null, null, null, 0, null);
return;
}

int resourceId = ai.metaData.getInt("android.app.qt_libs_resource_id");
m_qtLibs=getResources().getStringArray(resourceId);

m_ministroCallback.libs(libs,"QT_IMPORT_PATH=/data/local/qt/imports\tQT_PLUGIN_PATH=/data/local/qt/plugins",
"-platform\tandroid", 0,null);
return;
}

try {
if(!bindService(new Intent(eu.licentia.necessitas.ministro.IMinistro.class.getCanonicalName()),
m_ministroConnection, Context.BIND_AUTO_CREATE))
throw new SecurityException("");
} catch (SecurityException e) { }

}


应用程序需要的库由AndroidManifest.xml中的qt_libs_resource_id 项指定,这一项来自于res/values/libs.xml中的qt_libs项。


qt应用程序的启动也是借助ministro服务,需要ministro提供相应的库支持,当得到相应的库后创建线程,启动startApplication。

private IMinistroCallback m_ministroCallback = new IMinistroCallback.Stub(){
@Override
public void libs(finalString[] libs, final String evnVars, final String params,
int errorCode, StringerrorMessage) throws RemoteException {
runOnUiThread(new Runnable() {
@Override
public void run() {
startApplication(libs,evnVars, params);
}
});
}
};


在src/eu/licentia/necessitas/industriusQtApplication.java中 startApplication函数,先启动android的Plugin然后调用一个JNI接口startQtApp启动qt程序。
QtActivity.java中的startApplication函数会调用QtApplication.java中的 startApplication。

public static void startApplication(String params, String environment)
{
if (params == null)
params ="-platform\tandroid";

synchronized (m_mainActivityMutex)
{
startQtAndroidPlugin();
setDisplayMetrics(m_displayMetricsScreenWidthPixels,
m_displayMetricsScreenHeightPixels,
m_displayMetricsDesktopWidthPixels,
m_displayMetricsDesktopHeightPixels,
m_displayMetricsXDpi,
m_displayMetricsYDpi);
if (params.length()>0)
params="\t"+params;
startQtApp("QtApp"+params,environment);
m_started=true;
}
}

startQtApp时会启动线程startMainMethod,在startMainMethod线程中会将lib**.so中main函数入口以库函数接口的形式再次执行,恢复了qt可执行程序的本来面目,在后台执行
这部分源码在android-lighthouse的源码中。

JNI的部分代码



extern "C" int main(int, char **); //use the standard mainmethod to start the application
staticvoid * startMainMethod(void * /*data*/)
{

char ** params;
params=(char**)malloc(sizeof(char*)*m_applicationParams.length());
for (inti=0;i<m_applicationParams.size();i++)
params[i]=(char*)m_applicationParams[i].constData();

int ret = main(m_applicationParams.length(),params);
......
}

重写onKeydown

通过上述调用过程可以知道,android程序是怎样通过JNI来调用qt库中的函数。其他相关android的的JNI接口在,plugins/platforms/android/下,比如 keydown。
在src/eu/licentia/necessitas/industriusQtActivity.java重写了onKeydown函数,调用JNI函数keyDown实现。

keyDown的实现在 plugins/platforms/android/mw/androidjnimain.cpp 中



static void keyDown(JNIEnv */*env*/, jobject /*thiz*/,jint key, jint unicode, jint modifier)
{
……
int mappedKey=mapAndroidKey(key);
if (mappedKey==Qt::Key_Close)
{
qDebug()<<"handleCloseEvent"<<mLastTLW;
QWindowSystemInterface::handleCloseEvent(mLastTLW);
}
else
QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, mappedKey,modifiers, QChar(unicode),true);
//通过JNI的帮助,转化成了Qt的实现
}



结束语



先写到这吧,不管怎么说,接触qt for android ,让我收获了很多很多,绝对不仅限于qt 和 android领域。






分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics