我眼中的Qt for Android

來源:互聯網
上載者:User
我眼中的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,並且已經實現:

JNIEnv *currEnv;currEnv = 0;if (currVM->AttachCurrentThread((void **)&currEnv, NULL)<0){     emit error("Cannot attach the current thread to the VM");}

       也許因為我對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項。

<?xmlversion='1.0' encoding='utf-8'?><resources>    <array name="qt_libs">        <item>QtCore</item>        <item>QtGui</item>    </array>    <arrayname="bundled_libs"/></resources> 

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領域。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.