如何修复无法与“android_openssl”一起使用的 HTTPS 请求?

问题描述 投票:0回答:1

我正在使用 Qt 开发 Android 应用程序。我需要从 HTTPS URL 下载文件。应用程序开发人员现在必须发布自己的 OpenSSL 副本。我发现最简单的方法是使用 android_openssl 项目。理论上,这为 Qt 提供了一个可用的 TLS 后端,用于从 HTTPS 服务器下载文件。

克隆最小的可重现示例:

  • git clone https://github.com/ftab/QuickDownloadTest --recursive

CMakeLists.txt(根据 Qt Creator 新项目向导生成的内容进行修改,并添加必要的行

android_openssl
):

cmake_minimum_required(VERSION 3.16)

project(QuickDownloadTest VERSION 0.1 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 6.5 REQUIRED COMPONENTS Quick Network)

qt_standard_project_setup(REQUIRES 6.5)

include(android_openssl/android_openssl.cmake)

qt_add_executable(appQuickDownloadTest
    main.cpp
)

add_android_openssl_libraries(appQuickDownloadTest)

qt_add_qml_module(appQuickDownloadTest
    URI QuickDownloadTest
    VERSION 1.0
    QML_FILES
        Main.qml
)

# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
set_target_properties(appQuickDownloadTest PROPERTIES
#    MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appQuickDownloadTest
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

target_link_libraries(appQuickDownloadTest
    PRIVATE Qt6::Quick Qt6::Network
)

include(GNUInstallDirs)
install(TARGETS appQuickDownloadTest
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

main.cpp:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QObject>
#include <QNetworkReply>

class Downloader : public QObject
{
    Q_OBJECT
public:
    explicit Downloader(QObject* parent = nullptr) : QObject(parent)
    {

    };
    Q_INVOKABLE void downloadFile(const QString &url)
    {
        QNetworkAccessManager manager;
        QNetworkRequest request((QUrl(url)));
        m_reply = manager.get(request);

        QObject::connect(m_reply, &QNetworkReply::finished, this, &Downloader::on_fileDownloaded);
        QObject::connect(m_reply, &QNetworkReply::errorOccurred, this, &Downloader::on_errorOccurred);
    };
    Q_INVOKABLE QByteArray data()
    {
        return m_data;
    };
    Q_INVOKABLE QString toString()
    {
        return QString(m_data);
    };
public slots:
    void on_fileDownloaded()
    {
        if (m_reply->error() == QNetworkReply::NoError)
        {
            m_data = m_reply->readAll();
            qDebug() << "Download success";
            emit success();
        }
        else
        {
            qDebug() << "Download error:" << m_reply->errorString();
            emit error();
        }
        m_reply->deleteLater();
        m_reply = nullptr;
    };
    void on_errorOccurred(QNetworkReply::NetworkError error)
    {
        qDebug() << "Download error occurred:" << error;
    };
signals:
    void error();
    void success();
private:
    QByteArray m_data;
    QNetworkReply *m_reply{nullptr};
};

#include "main.moc"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    auto context = engine.rootContext();
    auto downloader = new Downloader();
    context->setContextProperty("Downloader", downloader);
    engine.loadFromModule("QuickDownloadTest", "Main");

    return app.exec();
}

主要.qml:

import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Window

ApplicationWindow {
    visible: true
    flags: Qt.Window | Qt.FramelessWindowHint

    ColumnLayout {
        Button {
            text: "Download File"
            onClicked: Downloader.downloadFile("https://gist.githubusercontent.com/ftab/818fbe5080a18ecb7332c7fe4542eeb3/raw/82a9750622a5a4db4c5250068cf2f6bba117ff51/test.txt");
        }
        Label {
            id: label
            text: "Result"
        }
    }
    Connections {
        target: Downloader
        function onSuccess()
        {
            label.text = Downloader.toString();
        }
        function onError()
        {
            label.text = "Error";
        }
    }
}

(配置 Qt 项目以在您所需的测试设备上构建 Android)

我希望下载能够正常工作,并且标签将使用我的

test.txt
文件(“v1.0.1”)的内容进行更新。

实际发生的事情是……什么也没有。显然。一旦我安装了

android_openssl
,我在尝试下载时不会遇到任何错误,但该请求似乎从未真正执行任何操作。我没有收到任何完成信号、错误信号或任何实际发生任何事情的指示。

  • 如果我稍后尝试查询 QNetworkReply 对象(例如
    m_reply->errorString()
    ),我会收到段错误。
  • 我尝试更改正在构建的 Qt 版本,仅在 6.6.3 和 6.7.2 上成功构建,但两者都出现了相同的问题。
  • 我尝试更改 NDK 以匹配构建 SSL 库的版本 (25.2.*),但没有成功。
  • 我尝试打开 Qt 的日志记录工具来查看它挂起的位置,但在加载库后似乎没有任何反应(或者至少日志中没有出现任何相关内容)。

我该如何找出问题所在?是 android_openssl 损坏还是我做错了什么?

android qt openssl
1个回答
0
投票

原因可能是你在回复完成之前就破坏了

QNetworkAccessManager
。它必须保持活力,直到答案到来。引用规格:

QNetworkAccessManager::~QNetworkAccessManager()

销毁 QNetworkAccessManager 对象并释放所有资源。请注意,从此类返回的 QNetworkReply 对象将此对象设置为其父对象,这意味着如果您不对它们调用 QObject::setParent() ,它们将随之被删除。

我使用 android_openssl,它对我来说工作正常。

© www.soinside.com 2019 - 2024. All rights reserved.