1. 程式人生 > >QtAndroid詳解(4):JNI呼叫Android系統功能(1)

QtAndroid詳解(4):JNI呼叫Android系統功能(1)

前面幾篇我們講解了 QtAndroid 名字空間的基本用法,這次我們使用前面講過的方法和類庫,展示一些簡單的小示例。我在《Qt on Android核心程式設計》一書中主要通過“繼承 QtActivity ,實現自己的 Activity 並新增 static 方法”這種形式來呼叫 Android 系統的一些功能。這一系列的文章,我們主要使用 Qt 5.3 裡引入的 QtAndroid 名字空間內的方法和 QAndroidJniObject 類來展示 Qt 中如何進行 JNI 呼叫,只在必要時才重寫 QtActivity 。

        Qt on Android 應用,根據你的需求,經常會呼叫到 Android 系統提供的一些功能,比如判斷網路連線、獲取外部儲存路徑,或者快取檔案目錄等等。這些經常被朋友問到,我會在這一系列文章中慢慢把 Qt on Android 開發中經常用到的功能點都演示一下。希望對大家有所幫助。

示例介紹

    示例很簡單,使用 Qt Widgets 來展示。下圖是效果:


    如上圖所示,介面非常簡陋,點下 Refresh 按鈕,就獲取一些 Android 系統資訊和當前應用的一些資訊,放在 QListWidget 中。包括下面的內容:

  • 手機的 Android 版本
  • 網路狀態和網路資訊
  • 手機的資料目錄
  • 手機外部儲存目錄
  • 手機的照片、音樂、視訊、鈴聲等目錄
  • 應用的路徑
  • 安裝後,系統保留的 APK 的位置
  • 應用的 files 目錄

原始碼分析

    程式碼沒什麼邏輯可講……都在下面了:

  1. #include "widget.h"
  2. #include <QVBoxLayout>
  3. #include <QListWidgetItem>
  4. #include <QtAndroid>
  5. #include <QAndroidJniEnvironment>
  6. #include <QAndroidJniObject>
  7. #include <QDebug>
  8. usingnamespace QtAndroid;  
  9. #define CHECK_EXCEPTION() \
  10.     if(env->ExceptionCheck())\  
  11.     {\  
  12.         qDebug() << "exception occured"
    ;\  
  13.         env->ExceptionClear();\  
  14.     }  
  15. Widget::Widget(QWidget *parent)  
  16.     : QWidget(parent)  
  17. {  
  18.     QVBoxLayout *layout = new QVBoxLayout(this);  
  19.     m_refresh = new QPushButton("Refresh");  
  20.     connect(m_refresh, SIGNAL(clicked()), this, SLOT(onRefresh()));  
  21.     layout->addWidget(m_refresh);  
  22.     m_list = new QListWidget();  
  23.     layout->addWidget(m_list, 1);  
  24. }  
  25. Widget::~Widget()  
  26. {  
  27. }  
  28. void Widget::onRefresh()  
  29. {  
  30.     m_list->clear();  
  31.     QAndroidJniEnvironment env;  
  32.     //get Android SDK version
  33.     m_list->addItem(QString("SDK版本:%1").arg(androidSdkVersion()));  
  34.     QAndroidJniObject activity = androidActivity();  
  35.     //get network state
  36.     QAndroidJniObject connectivity = QAndroidJniObject::getStaticObjectField(  
  37.                 "android/content/Context",  
  38.                 "CONNECTIVITY_SERVICE",  
  39.                 "Ljava/lang/String;");  
  40.     if(connectivity.isValid()){  
  41.         qDebug() << "connectivity id - " << connectivity.toString();  
  42.         CHECK_EXCEPTION()  
  43.         QAndroidJniObject connectivityService = activity.callObjectMethod(  
  44.                     "getSystemService",  
  45.                     "(Ljava/lang/String;)Ljava/lang/Object;",  
  46.                     connectivity.object<jstring>());  
  47.         CHECK_EXCEPTION()  
  48.         qDebug() << "got connectivity service - " << connectivityService.isValid();  
  49.         if(connectivityService.isValid())  
  50.         {  
  51.             QAndroidJniObject networkInfo = connectivityService.callObjectMethod(  
  52.                         "getActiveNetworkInfo",  
  53.                         "()Landroid/net/NetworkInfo;");  
  54.             CHECK_EXCEPTION()  
  55.                     qDebug() << "got NetworkInfo - " << networkInfo.isValid();  
  56.             if(networkInfo.isValid())  
  57.             {  
  58.                 m_list->addItem(QString("網路狀態:已連線(%1)").arg(networkInfo.toString()));  
  59.             }  
  60.             else
  61.             {  
  62.                 m_list->addItem("網路狀態:未連線");  
  63.             }  
  64.         }  
  65.     }  
  66.     //get variable directories of Android System
  67.     QAndroidJniObject externalStorageDir = QAndroidJniObject::callStaticObjectMethod(  
  68.                 "android/os/Environment",  
  69.                 "getExternalStorageDirectory",  
  70.                 "()Ljava/io/File;"
  71.                 );  
  72.     CHECK_EXCEPTION()  
  73.     m_list->addItem(QString("外部儲存目錄:%1").arg(externalStorageDir.toString()));  
  74.     QAndroidJniObject dataDir = QAndroidJniObject::callStaticObjectMethod(  
  75.                 "android/os/Environment",  
  76.                 "getDataDirectory",  
  77.                 "()Ljava/io/File;"
  78.                 );  
  79.     CHECK_EXCEPTION()  
  80.     m_list->addItem(QString("資料目錄:%1").arg(dataDir.toString()));  
  81.     QAndroidJniObject dcim = QAndroidJniObject::getStaticObjectField(  
  82.                 "android/os/Environment",  
  83.                 "DIRECTORY_DCIM",  
  84.                 "Ljava/lang/String;"
  85.                 );  
  86.     CHECK_EXCEPTION()  
  87.     QAndroidJniObject dcimDir = QAndroidJniObject::callStaticObjectMethod(  
  88.                 "android/os/Environment",  
  89.                 "getExternalStoragePublicDirectory",  
  90.                 "(Ljava/lang/String;)Ljava/io/File;",  
  91.                 dcim.object<jstring>()  
  92.                 );  
  93.     CHECK_EXCEPTION()  
  94.     m_list->addItem(QString("照片目錄:%1").arg(dcimDir.toString()));  
  95.     QAndroidJniObject music = QAndroidJniObject::getStaticObjectField(  
  96.                 "android/os/Environment",  
  97.                 "DIRECTORY_MUSIC",  
  98.                 "Ljava/lang/String;"
  99.                 );  
  100.     CHECK_EXCEPTION()  
  101.     QAndroidJniObject musicDir = QAndroidJniObject::callStaticObjectMethod(  
  102.                 "android/os/Environment",  
  103.                 "getExternalStoragePublicDirectory",  
  104.                 "(Ljava/lang/String;)Ljava/io/File;",  
  105.                 music.object<jstring>()  
  106.                 );  
  107.     CHECK_EXCEPTION()  
  108.     m_list->addItem(QString("音樂目錄:%1").arg(musicDir.toString()));  
  109.     QAndroidJniObject movie = QAndroidJniObject::getStaticObjectField(  
  110.                 "android/os/Environment",  
  111.                 "DIRECTORY_MOVIES",  
  112.                 "Ljava/lang/String;"
  113.                 );  
  114.     CHECK_EXCEPTION()  
  115.     QAndroidJniObject movieDir = QAndroidJniObject::callStaticObjectMethod(  
  116.                 "android/os/Environment",  
  117.                 "getExternalStoragePublicDirectory",  
  118.                 "(Ljava/lang/String;)Ljava/io/File;",  
  119.                 movie.object<jstring>()  
  120.                 );  
  121.     CHECK_EXCEPTION()  
  122.     m_list->addItem(QString("視訊目錄:%1").arg(movieDir.toString()));  
  123.     QAndroidJniObject ringtones = QAndroidJniObject::getStaticObjectField(  
  124.                 "android/os/Environment",  
  125.                 "DIRECTORY_RINGTONES",  
  126.                 "Ljava/lang/String;"
  127.                 );  
  128.     CHECK_EXCEPTION()  
  129.     QAndroidJniObject ringtonesDir = QAndroidJniObject::callStaticObjectMethod(  
  130.                 "android/os/Environment",  
  131.                 "getExternalStoragePublicDirectory",  
  132.                 "(Ljava/lang/String;)Ljava/io/File;",  
  133.                 ringtones.object<jstring>()  
  134.                 );  
  135.     CHECK_EXCEPTION()  
  136.     m_list->addItem(QString("鈴聲目錄:%1").arg(ringtonesDir.toString()));  
  137.     //app's infomation
  138.     QAndroidJniObject filesDir = activity.callObjectMethod(  
  139.                 "getFilesDir",  
  140.                 "()Ljava/io/File;");  
  141.     CHECK_EXCEPTION()  
  142.     m_list->addItem(QString("應用檔案目錄:%1").arg(filesDir.toString()));  
  143.     QAndroidJniObject packageName = activity.callObjectMethod<jstring>("getPackageName");  
  144.     CHECK_EXCEPTION()  
  145.     m_list->addItem(QString("應用包名:%1").arg(packageName.toString()));  
  146.     QAndroidJniObject appCacheDir = activity.callObjectMethod(  
  147.                 "getCacheDir",  
  148.                 "()Ljava/io/File;");  
  149.     CHECK_EXCEPTION()  
  150.     m_list->addItem(QString("應用快取目錄:%1").arg(appCacheDir.toString()));  
  151.     QAndroidJniObject appInfo = activity.callObjectMethod(  
  152.                 "getApplicationInfo",  
  153.                 "()Landroid/content/pm/ApplicationInfo;");  
  154.     CHECK_EXCEPTION()  
  155.     QAndroidJniObject appClassName = appInfo.getObjectField<jstring>("className");  
  156.     CHECK_EXCEPTION()  
  157.     m_list->addItem(QString("應用類名:%1").arg(appClassName.toString()));  
  158.     QAndroidJniObject appLocation = appInfo.getObjectField(  
  159.                 "sourceDir""Ljava/lang/String;");  
  160.     CHECK_EXCEPTION()  
  161.     m_list->addItem(QString("APK位置:%1").arg(appLocation.toString()));  
  162. }  

    最恐怖的就是 onRefresh() 這個槽了,將近一百五十行程式碼,這不是好的程式設計實踐,實際開發中儘量別這麼幹。

    其實在 Qt 中通過 JNI 呼叫 Android 功能,關鍵的就是兩點:

  1. Qt提供的API怎麼用
  2. Android類庫怎麼用

    Android 類庫這方面,我們搞 C++ 開發的朋友,可能不熟悉。不過沒關係,可以通過 Android 線上 SDK 來學習,另外我這裡提供的 Qt JNI 程式碼,都是實測可用的,裡面演示一些功能的程式碼,如果需要,可以直接在專案中使用。

    好了,我們開始慢慢介紹吧。

Android版本獲取

    這個很貼心,QtAndroid名字空間直接提供了一個方法: androidSdkVersion() 。它返回一個整數值,表示 Android SDK 版本號。

網路狀態

    在 Android 中,有一個 ConnectivityManager 類,可以查詢系統的網路狀態。

    ConnectivityManager 類的 getActiveNetworkInfo() 方法可以獲取到當前活躍的網路連線資訊,它返回一個 NetworkInfo 類的例項。如果未聯網,這個方法返回 null 。

    ConnectivityManager 在 Android 系統裡以一個服務存在,需要通過 Context 的 getSystemService() 方