Skip to content

fix: app hang a few seconds when launch app for the first time#769

Open
BLumia wants to merge 1 commit into
linuxdeepin:masterfrom
BLumia:launchpad-hang
Open

fix: app hang a few seconds when launch app for the first time#769
BLumia wants to merge 1 commit into
linuxdeepin:masterfrom
BLumia:launchpad-hang

Conversation

@BLumia
Copy link
Copy Markdown
Member

@BLumia BLumia commented Jun 2, 2026

修复启动器首次启动应用时,可能导致界面卡死几秒的问题.

@BLumia BLumia requested a review from 18202781743 June 2, 2026 08:53
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @BLumia, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: BLumia

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

修复启动器首次启动应用时,可能导致界面卡死几秒的问题.

Log:
@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

你好!我是CodeGeeX。我仔细审查了你提供的 git diff,本次修改的主要意图是将 DConfig 的初始化和信号监听逻辑提取到独立的 loadAppsLaunchedTimes() 函数中,并调整了触发时机(仅在 DBus 服务有效或注册时调用),同时优化了 QDBusServiceWatcher 的生命周期管理。

整体上,代码的逻辑变得更加清晰,职责划分更明确。但也引入了一些关于内存管理、对象生命周期和代码健壮性的问题。以下是我的详细审查意见:

1. 代码逻辑与内存管理

问题:QDBusServiceWatcher 的生命周期缩短与内存泄漏风险
在旧代码中,m_serviceWatcher 作为类成员,如果在构造函数执行完毕后 DBus 服务才上线,它依然能捕获信号。而在新代码中:

} else {
    auto serviceWatcher = new QDBusServiceWatcher(..., this);
    connect(serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, [this]() {
        fetchAppItems();
        loadAppsLaunchedTimes();
    });
}
  • 逻辑漏洞:如果 isValid() 为 false,创建了 serviceWatcher。当 DBus 服务注册时,触发了 serviceRegistered 信号,执行了 fetchAppItems()。但此时 serviceWatcher 的使命已经结束,它依然挂载在 DBus 总线上监听,造成不必要的资源开销。
  • 改进建议:在 lambda 的回调中,显式释放或删除 serviceWatcher;或者使用 QScopedPointer。由于它继承自 QObject 且指定了 this 作为 parent,最优雅的做法是在触发后直接 deleteLater()
auto serviceWatcher = new QDBusServiceWatcher(QStringLiteral("org.desktopspec.ApplicationManager1"),
                                            QDBusConnection::sessionBus(),
                                            QDBusServiceWatcher::WatchForRegistration,
                                            this);
connect(serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, [this, serviceWatcher]() {
    qCInfo(logDdeIntegration) << "AppManager1 service registered on bus, fetching app items";
    fetchAppItems();
    loadAppsLaunchedTimes();
    serviceWatcher->deleteLater(); // 触发后即销毁,避免重复监听和内存浪费
});

2. 代码性能

问题:loadAppsLaunchedTimes() 可能被重复调用导致重复连接
在新代码中,loadAppsLaunchedTimes() 内部每次调用时都会执行:

QObject::connect(config, &DConfig::valueChanged, this, [this, config](const QString &key) { ... });

如果在极端情况下(例如 DBus 服务掉线又重连,虽然当前代码未处理掉线情况),或者由于逻辑变更导致 loadAppsLaunchedTimes() 被多次调用,DConfig::valueChanged 信号将会被连接多次,导致 updateAppsLaunchedTimes() 被重复执行。

  • 改进建议:将 DConfig 指针提升为类的成员变量 m_config,在 loadAppsLaunchedTimes() 中先判断 m_config 是否已初始化,如果已初始化则直接刷新数据,不再重复创建和绑定信号。

3. 代码安全与健壮性

问题:DConfig::create 可能返回空指针
loadAppsLaunchedTimes() 函数中:

DConfig *config = DConfig::create("org.deepin.dde.application-manager", "org.deepin.dde.am", "", this);
if (!config->isValid()) {

DConfig::create 的返回值有可能是 nullptr(例如在某些极端的内存不足或配置缺失场景下)。直接调用 config->isValid() 会导致空指针解引用,引发程序崩溃。

  • 改进建议:先进行空指针检查,再检查 isValid()
DConfig *config = DConfig::create("org.deepin.dde.application-manager", "org.deepin.dde.am", "", this);
if (!config) {
    qCWarning(logDdeIntegration) << "Failed to create DConfig instance (nullptr).";
    return;
}
if (!config->isValid()) {
    qCWarning(logDdeIntegration) << "DConfig is invalid when getting launched times.";
    // 注意:如果 config 是 new 出来的且 parent 是 this,虽然最终会被析构,但尽早 delete 更好
    config->deleteLater(); 
    return;
}

4. 语法与代码风格

问题:Lambda 表达式中的变量遮蔽
loadAppsLaunchedTimes() 的 lambda 表达式中:

QObject::connect(config, &DConfig::valueChanged, this, [this, config](const QString &key) {
    // ...
    const auto &value = config->value(AppsLaunchedTimes).toMap(); // 这里的 value 遮蔽了外层作用域的 value
    updateAppsLaunchedTimes(value);
});

外层作用域有 const auto &value = config->value(AppsLaunchedTimes).toMap();,lambda 内部又定义了同名变量 value。虽然逻辑上不影响运行结果,但容易引起混淆,且编译器可能会发出警告。

  • 改进建议:将 lambda 内部的变量重命名,例如改为 newValuelaunchedTimesMap

综合改进后的代码建议

结合以上分析,我为你重构了 loadAppsLaunchedTimes() 和构造函数的相关部分,供你参考:

appmgr.cpp 构造函数相关部分:

    if (isValid()) {
        qCInfo(logDdeIntegration) << "AppManager1 service already ready, fetching app items right away";
        fetchAppItems();
        loadAppsLaunchedTimes();
    } else {
        auto serviceWatcher = new QDBusServiceWatcher(QStringLiteral("org.desktopspec.ApplicationManager1"),
                                                QDBusConnection::sessionBus(),
                                                QDBusServiceWatcher::WatchForRegistration,
                                                this);
        connect(serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, [this, serviceWatcher]() {
            qCInfo(logDdeIntegration) << "AppManager1 service registered on bus, fetching app items";
            fetchAppItems();
            loadAppsLaunchedTimes();
            serviceWatcher->deleteLater(); // 触发一次后不再需要监听
        });
    }

appmgr.cpp loadAppsLaunchedTimes() 函数:

void AppMgr::loadAppsLaunchedTimes()
{
    DConfig *config = DConfig::create("org.deepin.dde.application-manager", "org.deepin.dde.am", "", this);
    if (!config) {
        qCWarning(logDdeIntegration) << "Failed to create DConfig instance (nullptr).";
        return;
    }
    
    if (!config->isValid()) {
        qCWarning(logDdeIntegration) << "DConfig is invalid when getting launched times.";
        config->deleteLater(); // 及时清理无效对象
        return;
    } 

    static const QString AppsLaunchedTimes(u8"appsLaunchedTimes");
    const auto &value = config->value(AppsLaunchedTimes).toMap();
    updateAppsLaunchedTimes(value);
    
    QObject::connect(config, &DConfig::valueChanged, this, [this, config](const QString &key) {
        if (key != AppsLaunchedTimes) {
            qCDebug(logDdeIntegration) << "Ignoring non-appsLaunchedTimes key:" << key;
            return;
        }

        qCInfo(logDdeIntegration) << "appsLaunchedTimes of DConfig changed, updating";
        // 避免变量名遮蔽
        const auto &newValue = config->value(AppsLaunchedTimes).toMap();
        updateAppsLaunchedTimes(newValue);
    });
}

进一步优化建议(可选)
如果 AppMgr 类在业务逻辑上会频繁重建,或者需要多次调用 loadAppsLaunchedTimes(),强烈建议将 DConfig *config 提取为类成员变量 m_config。这样可以在第一次调用时初始化,后续调用只需 m_config->value(...) 即可,完全避免信号重复绑定和对象重复创建的问题。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants