Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> QtAndroid詳解(4):JNI調用Android系統功能(1)

QtAndroid詳解(4):JNI調用Android系統功能(1)

編輯:關於Android編程

前面幾篇我們講解了 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 目錄

 

源碼分析

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

 

#include widget.h
#include 
#include 
#include 
#include 
#include 
#include 

using namespace QtAndroid;

#define CHECK_EXCEPTION() 
    if(env->ExceptionCheck())
    {
        qDebug() << exception occured;
        env->ExceptionClear();
    }

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    QVBoxLayout *layout = new QVBoxLayout(this);
    m_refresh = new QPushButton(Refresh);
    connect(m_refresh, SIGNAL(clicked()), this, SLOT(onRefresh()));
    layout->addWidget(m_refresh);
    m_list = new QListWidget();
    layout->addWidget(m_list, 1);
}

Widget::~Widget()
{

}

void Widget::onRefresh()
{
    m_list->clear();
    QAndroidJniEnvironment env;

    //get Android SDK version
    m_list->addItem(QString(SDK版本:%1).arg(androidSdkVersion()));

    QAndroidJniObject activity = androidActivity();
    //get network state
    QAndroidJniObject connectivity = QAndroidJniObject::getStaticObjectField(
                android/content/Context,
                CONNECTIVITY_SERVICE,
                Ljava/lang/String;);
    if(connectivity.isValid()){
        qDebug() << connectivity id -  << connectivity.toString();
        CHECK_EXCEPTION()
        QAndroidJniObject connectivityService = activity.callObjectMethod(
                    getSystemService,
                    (Ljava/lang/String;)Ljava/lang/Object;,
                    connectivity.object());
        CHECK_EXCEPTION()
        qDebug() << got connectivity service -  << connectivityService.isValid();
        if(connectivityService.isValid())
        {
            QAndroidJniObject networkInfo = connectivityService.callObjectMethod(
                        getActiveNetworkInfo,
                        ()Landroid/net/NetworkInfo;);
            CHECK_EXCEPTION()
                    qDebug() << got NetworkInfo -  << networkInfo.isValid();
            if(networkInfo.isValid())
            {
                m_list->addItem(QString(網絡狀態:已連接(%1)).arg(networkInfo.toString()));
            }
            else
            {
                m_list->addItem(網絡狀態:未連接);
            }
        }
    }

    //get variable directories of Android System
    QAndroidJniObject externalStorageDir = QAndroidJniObject::callStaticObjectMethod(
                android/os/Environment,
                getExternalStorageDirectory,
                ()Ljava/io/File;
                );
    CHECK_EXCEPTION()
    m_list->addItem(QString(外部存儲目錄:%1).arg(externalStorageDir.toString()));

    QAndroidJniObject dataDir = QAndroidJniObject::callStaticObjectMethod(
                android/os/Environment,
                getDataDirectory,
                ()Ljava/io/File;
                );
    CHECK_EXCEPTION()
    m_list->addItem(QString(數據目錄:%1).arg(dataDir.toString()));

    QAndroidJniObject dcim = QAndroidJniObject::getStaticObjectField(
                android/os/Environment,
                DIRECTORY_DCIM,
                Ljava/lang/String;
                );
    CHECK_EXCEPTION()
    QAndroidJniObject dcimDir = QAndroidJniObject::callStaticObjectMethod(
                android/os/Environment,
                getExternalStoragePublicDirectory,
                (Ljava/lang/String;)Ljava/io/File;,
                dcim.object()
                );
    CHECK_EXCEPTION()
    m_list->addItem(QString(照片目錄:%1).arg(dcimDir.toString()));

    QAndroidJniObject music = QAndroidJniObject::getStaticObjectField(
                android/os/Environment,
                DIRECTORY_MUSIC,
                Ljava/lang/String;
                );
    CHECK_EXCEPTION()
    QAndroidJniObject musicDir = QAndroidJniObject::callStaticObjectMethod(
                android/os/Environment,
                getExternalStoragePublicDirectory,
                (Ljava/lang/String;)Ljava/io/File;,
                music.object()
                );
    CHECK_EXCEPTION()
    m_list->addItem(QString(音樂目錄:%1).arg(musicDir.toString()));

    QAndroidJniObject movie = QAndroidJniObject::getStaticObjectField(
                android/os/Environment,
                DIRECTORY_MOVIES,
                Ljava/lang/String;
                );
    CHECK_EXCEPTION()
    QAndroidJniObject movieDir = QAndroidJniObject::callStaticObjectMethod(
                android/os/Environment,
                getExternalStoragePublicDirectory,
                (Ljava/lang/String;)Ljava/io/File;,
                movie.object()
                );
    CHECK_EXCEPTION()
    m_list->addItem(QString(視頻目錄:%1).arg(movieDir.toString()));

    QAndroidJniObject ringtones = QAndroidJniObject::getStaticObjectField(
                android/os/Environment,
                DIRECTORY_RINGTONES,
                Ljava/lang/String;
                );
    CHECK_EXCEPTION()
    QAndroidJniObject ringtonesDir = QAndroidJniObject::callStaticObjectMethod(
                android/os/Environment,
                getExternalStoragePublicDirectory,
                (Ljava/lang/String;)Ljava/io/File;,
                ringtones.object()
                );
    CHECK_EXCEPTION()
    m_list->addItem(QString(鈴聲目錄:%1).arg(ringtonesDir.toString()));

    //app's infomation
    QAndroidJniObject filesDir = activity.callObjectMethod(
                getFilesDir,
                ()Ljava/io/File;);
    CHECK_EXCEPTION()
    m_list->addItem(QString(應用文件目錄:%1).arg(filesDir.toString()));

    QAndroidJniObject packageName = activity.callObjectMethod(getPackageName);
    CHECK_EXCEPTION()
    m_list->addItem(QString(應用包名:%1).arg(packageName.toString()));

    QAndroidJniObject appCacheDir = activity.callObjectMethod(
                getCacheDir,
                ()Ljava/io/File;);
    CHECK_EXCEPTION()
    m_list->addItem(QString(應用緩存目錄:%1).arg(appCacheDir.toString()));

    QAndroidJniObject appInfo = activity.callObjectMethod(
                getApplicationInfo,
                ()Landroid/content/pm/ApplicationInfo;);
    CHECK_EXCEPTION()

    QAndroidJniObject appClassName = appInfo.getObjectField(className);
    CHECK_EXCEPTION()
    m_list->addItem(QString(應用類名:%1).arg(appClassName.toString()));

    QAndroidJniObject appLocation = appInfo.getObjectField(
                sourceDir, Ljava/lang/String;);
    CHECK_EXCEPTION()
    m_list->addItem(QString(APK位置:%1).arg(appLocation.toString()));
}

最恐怖的就是 onRefresh() 這個槽了,將近一百五十行代碼,這不是好的編程實踐,實際開發中盡量別這麼干。

 

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

 

Qt提供的API怎麼用Android類庫怎麼用

 

Qt 提供的 API ,在“QtAndroid詳解(1):QAndroidJniObject”、QtAndroid詳解(2):startActivity和它的小伙伴們、QtAndroid詳解(3):startActivity實戰Android拍照功能這三篇文章中已有詳細講解,不再贅述了。

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

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

Android版本獲取

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

網絡狀態

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

ConnectivityManager 類的 getActiveNetworkInfo() 方法可以獲取到當前活躍的網絡連接信息,它返回一個 NetworkInfo 類的實例。如果未聯網,這個方法返回 null 。

ConnectivityManager 在 Android 系統裡以一個服務存在,需要通過 Context 的 getSystemService() 方法來獲取到這個服務。 getSystemService() 接受一個代表服務名字的字符串作為參數。對於網絡連接管理服務,名字是 CONNECTIVITY_SERVICE ,它是 Context 類的靜態成員變量。

獲取網絡連接管理服務的 Java 代碼如下:

 

Context.getSystemService(Context.CONNECTIVITY_SERVICE);

這些都是 Android Java 背景知識,現在來看 Qt JNI 代碼。一行一行過。

 

 

    QAndroidJniObject connectivity = QAndroidJniObject::getStaticObjectField(
                android/content/Context,
                CONNECTIVITY_SERVICE,
                Ljava/lang/String;);

這行代碼使用獲取 Context 類的靜態成員 CONNECTIVITY_SERVICE ,保存在 connectivity 對象裡,我們在獲取 ConnectivityManager 時需要它。

 

 

        QAndroidJniObject connectivityService = activity.callObjectMethod(
                    getSystemService,
                    (Ljava/lang/String;)Ljava/lang/Object;,
                    connectivity.object());

這行代碼調用 Context 的 getSystemService 方法來獲取 ConnectivityManager 實例。我們需要一個 Context 實例,剛好 QtAndroid::androidActivity() 方法能返回一個給我們。

 

拿到了 ConnectivityManager 實例,就該調用它的 getActiveNetworkInfo() 方法來獲取當前的活動連接了。下面是代碼:

 

    QAndroidJniObject networkInfo = connectivityService.callObjectMethod(
                getActiveNetworkInfo,
                ()Landroid/net/NetworkInfo;);

QAndroidJniObject 有個方法叫 isValid() ,當它返回 true 時代表它拿的 JNI 對象正常可用, false 就代表沒拿到可用的 JNI 對象,一般也就是 Java 裡的 null 。所以,我認為networkInfo.isValid() 為 true 說明網絡連接正常。

 

其它的都是輔助性代碼,看最前面的源碼好了。

Android系統的各種目錄

示例裡的各種系統級的目錄,都是通過 android.os.Enviroment 這個類獲取的。

我們先說圖片、視頻、鈴聲這些吧,他們通過 getExternalStoragePublicDirectory(String) 方法獲取。Android給每個公共存儲目錄提供了一個字符串類型的名字,定義為 Enviroment 類的靜態成員變量。所以,我們使用 Qt JNI 獲取這些目錄的步驟是:

 

獲取目錄類型名調用getExternalStoragePublicDirectory

 

按照這個邏輯來看獲取圖片目錄的代碼,關鍵的就下面兩行:

 

    QAndroidJniObject dcim = QAndroidJniObject::getStaticObjectField(
                android/os/Environment,
                DIRECTORY_DCIM,
                Ljava/lang/String;
                );

    QAndroidJniObject dcimDir = QAndroidJniObject::callStaticObjectMethod(
                android/os/Environment,
                getExternalStoragePublicDirectory,
                (Ljava/lang/String;)Ljava/io/File;,
                dcim.object()
                );

我們使用 QAndroidJniObject::getStaticObjectField() 方法來獲取 Java 類 Enviroment 的靜態成員變量,然後使用 callStaticObjectMethod 調用 getExternalStoragePublicDirectory 方法。

 

當前應用信息

當前應用的一些信息,可以通過 Android 裡的 Activity 類獲取。

我們需要一個 Activity 對象,在 Qt on Android 應用裡,對應的類是 QtActivity ,之前在“QtAndroid詳解(3):startActivity實戰Android拍照功能”中我們已經介紹過了。不多說了。

獲取當前應用 files 目錄的代碼如下:

 

    QAndroidJniObject filesDir = activity.callObjectMethod(
                getFilesDir,
                ()Ljava/io/File;);

它的返回結果就是 /data/data/an.qt.SystemInfo/files ,實際上使用 Qt 的 QDir::currentPath() 方法能得到同樣的結果。

 

 

--------

好啦,這次就到這裡吧。下一次我們會展示更有意思的一些實用功能,如讓手機震動、讓屏幕常亮、動態切換橫屏豎屏等。再再往後可能還會介紹調節音量、調整屏幕亮度、使用推送、通知欄、選取聯系人等等,不過要看我的時間哈。

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved