前面几篇我们讲解了 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 来展示。下图是效果:

QtAndroid详解(4):JNI调用Android系统功能(1)_第1张图片


如上图所示,界面非常简陋,点下 Refresh 按钮,就获取一些 Android 系统信息和当前应用的一些信息,放在 QListWidget 中。包括下面的内容:

  • 手机的 Android 版本
  • 网络状态和网络信息
  • 手机的数据目录
  • 手机外部存储目录
  • 手机的照片、音乐、视频、铃声等目录
  • 应用的路径
  • 安装后,系统保留的 APK 的位置
  • 应用的 files 目录

源码分析

代码没什么逻辑可讲……都在下面了:

#include "widget.h"#include <QVBoxLayout>#include <QListWidgetItem>#include <QtAndroid>#include <QAndroidJniEnvironment>#include <QAndroidJniObject>#include <QDebug>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<jstring>());        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<jstring>()                );    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<jstring>()                );    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<jstring>()                );    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<jstring>()                );    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<jstring>("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<jstring>("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 功能,关键的就是两点:

  1. Qt提供的API怎么用
  2. 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<jstring>());

这行代码调用 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 获取这些目录的步骤是:

  1. 获取目录类型名
  2. 调用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<jstring>()                );

我们使用 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() 方法能得到同样的结果。


--------

好啦,这次就到这里吧。下一次我们会展示更有意思的一些实用功能,如让手机震动、让屏幕常亮、动态切换横屏竖屏等。再再往后可能还会介绍调节音量、调整屏幕亮度、使用推送、通知栏、选取联系人等等,不过要看我的时间哈。

回顾一下:

  • QtAndroid详解(3):startActivity实战Android拍照功能
  • QtAndroid详解(2):startActivity和它的小伙伴们
  • QtAndroid详解(1):QAndroidJniObject
  • Qt on Android专栏

更多相关文章

  1. Android网络编程之通过Get方法实现
  2. 研究开源OpenWnn Android输入法源代码
  3. Android assets 目录作用
  4. android控件的监听绑定方法
  5. 全面的Android文件目录解析和获取方法(包含对6.0系统的说明)
  6. Android资源目录 /res/xml /res/raw 和 /assets介绍
  7. Android官方的文档中提到了模拟器中设置代理服务器的方法,即在命
  8. 如何向Android的framework里添加新类 &&& android修改开放类方法
  9. Android swap分区作用及swapper软件设置方法

随机推荐

  1. Android打开关闭触摸提示音
  2. Qt for Android(安卓)部署流程分析
  3. android Canvas清屏只需三句话
  4. 网上找了些Android数据库操作的代码
  5. Android ListView滑动时出现黑屏解决方法
  6. android camera调试命令
  7. Android Toast无效的问题
  8. 编译Android 4.0时遇到的问题汇总
  9. 学习笔记 Android 使用AIDL实现进程间通
  10. Android Banner 的简单使用步骤