Skip to content

fix: launchpad might be blank when starting a new treeland dde session#768

Merged
BLumia merged 1 commit into
linuxdeepin:masterfrom
BLumia:treeland-blank-app
May 29, 2026
Merged

fix: launchpad might be blank when starting a new treeland dde session#768
BLumia merged 1 commit into
linuxdeepin:masterfrom
BLumia:treeland-blank-app

Conversation

@BLumia
Copy link
Copy Markdown
Member

@BLumia BLumia commented May 29, 2026

修复treeland dde会话下,启动器应用列表可能完全空白的问题。

Log:

Summary by Sourcery

Ensure application items and launch statistics are initialized and updated when the DDE application manager service becomes available, preventing empty launchpad app lists in new sessions.

Bug Fixes:

  • Prevent the apps model from remaining empty by fetching app items once the ApplicationManager1 D-Bus service registers instead of only during initial construction.
  • Avoid missing app updates by always wiring AppsModel signals to AppMgr, regardless of initial AppMgr validity, so late initialization still propagates changes.

Enhancements:

  • Refactor AppMgr initialization by inlining object manager and DConfig setup into the constructor and introducing a QDBusServiceWatcher to reactively reload app items when the D-Bus service appears.

修复treeland dde会话下,启动器应用列表可能完全空白的问题。

Log:
@BLumia BLumia requested a review from 18202781743 May 29, 2026 05:09
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 29, 2026

Reviewer's Guide

Refactors AppMgr initialization to always wire up DBus object manager, DConfig, and app fetching (with service-watcher retry), and removes validity gating in AppsModel so launchpad updates correctly even when the application manager service appears late in a new treeland DDE session.

Sequence diagram for app list initialization with late ApplicationManager1 service

sequenceDiagram
    actor User
    participant AppsModel as AppsModel
    participant AppMgr as AppMgr
    participant DConfig as DConfig
    participant ServiceWatcher as QDBusServiceWatcher
    participant AMService as ApplicationManager1Service

    User ->> AppsModel: AppsModel::AppsModel()
    AppsModel ->> AppMgr: AppMgr::instance()
    activate AppMgr
    AppMgr ->> AppMgr: AppMgr::AppMgr()
    AppMgr ->> DConfig: DConfig::create()
    DConfig -->> AppMgr: isValid()
    alt DConfig is valid
        AppMgr ->> DConfig: DConfig::value(AppsLaunchedTimes)
        AppMgr ->> AppMgr: updateAppsLaunchedTimes()
        DConfig ->> AppMgr: valueChanged(AppsLaunchedTimes)
        AppMgr ->> AppMgr: updateAppsLaunchedTimes()
    end

    AppMgr ->> AppMgr: isValid()
    alt AppMgr::isValid() true at startup
        AppMgr ->> AMService: fetchAppItems()
        AMService -->> AppMgr: app items
        AppMgr ->> AppsModel: changed
    end

    AppMgr ->> ServiceWatcher: QDBusServiceWatcher::QDBusServiceWatcher()
    ServiceWatcher ->> AppMgr: serviceRegistered
    AppMgr ->> AMService: fetchAppItems()
    AMService -->> AppMgr: app items
    AppMgr ->> AppsModel: changed

    AppsModel ->> AppsModel: connect(AppMgr::changed, QTimer::start)
    AppsModel ->> AppsModel: connect(AppMgr::itemDataChanged, updateAppItemFromAM)
    AppMgr ->> AppsModel: itemDataChanged(id)
    AppsModel ->> AppsModel: itemFromDesktopId(id)
    AppsModel ->> AppsModel: updateAppItemFromAM()
Loading

File-Level Changes

Change Details Files
Inline and extend AppMgr initialization so DBus object manager signals, DConfig tracking, and initial app fetch are set up in the constructor with a DBus service watcher to refetch when the ApplicationManager service registers.
  • Move the InterfacesAdded/InterfacesRemoved connections from a separate initObjectManager() method directly into AppMgr::AppMgr, ensuring they are always established once the object exists
  • Reset m_checkCount when a new app DBus interface appears and delegate to parseDBus2AppItem and watchingAppItemAdded/Removed as before
  • Create and validate a DConfig instance in the constructor, initialize appsLaunchedTimes from config, and connect to DConfig::valueChanged to keep launch counts in sync
  • Conditionally call fetchAppItems() if isValid() returns true, instead of unconditionally calling it from the removed initObjectManager()
  • Introduce a QDBusServiceWatcher on org.desktopspec.ApplicationManager1 to trigger fetchAppItems() when the service registers on the session bus, improving robustness when the service appears late
  • Remove the now-unused initObjectManager() declaration and definition, and add a QDBusServiceWatcher* member initialized to nullptr
src/ddeintegration/appmgr.cpp
src/ddeintegration/appmgr.h
Ensure AppsModel always listens to AppMgr signals regardless of initial validity so the UI updates when the application manager becomes available later.
  • Remove the isValid() guard around connections to AppMgr::changed and AppMgr::itemDataChanged in AppsModel::AppsModel
  • Keep the itemDataChanged handler logic the same, updating an existing app item from the application manager or logging a warning if not found
src/models/appsmodel.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

你好!我是CodeGeeX。我已仔细审查了你提供的Git Diff。本次代码变更主要将原本的 initObjectManager() 函数逻辑拆解并移入构造函数,同时引入了 QDBusServiceWatcher 来监听DBus服务的注册事件,并在 AppsModel 中移除了对 isValid() 的前置检查。

整体来看,代码的重构方向是好的,增强了动态监听能力,解决了服务延迟启动的痛点。但代码在逻辑严谨性、内存/生命周期管理、代码性能和安全性方面存在一些需要改进的地方。以下是详细的审查意见:

1. 逻辑缺陷

问题:isValid() 检查被移除导致的逻辑漏洞
在旧代码中,initObjectManager() 内有 if (!isValid()) return; 的逻辑,只有当 DBus 代理有效时才会连接信号和调用 fetchAppItems()。重构后,构造函数中直接连接了 m_objectManager 的信号,并且只在 isValid() 为真时才调用 fetchAppItems()

  • 风险:如果构造时 isValid() 为 false,InterfacesAddedInterfacesRemoved 信号的连接依然建立了。虽然 QDBusServiceWatcher 监听了服务上线,但当服务上线时,只调用了 fetchAppItems(),并没有重新连接 m_objectManager 的信号。如果 m_objectManager 在服务上线后没有自动重建底层连接,或者底层代理在无效时连接信号会导致潜在崩溃或内存越界,这将是一个严重的逻辑漏洞。
  • 改进建议:确认 m_objectManager 是否能在服务重启后自动恢复连接。如果不能,建议在 serviceRegistered 的回调中重新绑定信号,或者确保 DBus 代理本身具备自动重连机制。

2. 内存与生命周期管理

问题:DConfig 在无效时成为悬空/无用对象

DConfig *config = DConfig::create("org.deepin.dde.application-manager", "org.deepin.dde.am", "", this);
if (!config->isValid()) {
    qCWarning(logDdeIntegration) << "DConfig is invalid when getting launched times.";
    // 这里没有 return,也没有 delete config
} else {
    // ...
}
  • 风险:当 config->isValid() 为 false 时,代码仅输出了警告,但 config 对象已经被 DConfig::create 创建并指定了 this 作为父对象。虽然指定了父对象意味着它最终会被回收,但如果它在无效状态下一直存在,不仅浪费内存,还可能在某些Dtk内部机制下引发意外行为。
  • 改进建议:如果 DConfig 无效,应直接 delete config; 防止内存泄漏(直到父对象析构前都泄漏),或者将其置空。

3. 代码性能

问题:构造函数中的阻塞/耗时操作

  • 风险:将 initObjectManager() 的逻辑移入构造函数后,构造函数中包含了 DBus 信号的连接、DConfig 的读取(可能涉及磁盘I/O或DBus IPC通信)、以及 fetchAppItems() 的调用(通常是同步的DBus调用)。在构造函数中进行这些耗时操作,会导致对象实例化变慢,如果此对象在主线程创建,可能会导致UI卡顿。
  • 改进建议:考虑使用延迟初始化(例如利用 QTimer::singleShot(0, ...) 将耗时操作移到事件循环的下一个周期),或者保留单独的 init() 函数,在对象构造完成后再调用。

4. 代码安全与健壮性

问题:m_serviceWatcher 的初始化缺乏保护

m_serviceWatcher = new QDBusServiceWatcher(QStringLiteral("org.desktopspec.ApplicationManager1"),
                                           QDBusConnection::sessionBus(),
                                           QDBusServiceWatcher::WatchForRegistration,
                                           this);
  • 风险:如果 QDBusConnection::sessionBus() 返回无效的连接(例如DBus守护进程未运行),QDBusServiceWatcher 将无法正常工作,虽然不会崩溃,但后续逻辑将永远无法触发。
  • 改进建议:增加对 Session Bus 是否有效的判断,如果无效应给出明确的错误提示。

5. 代码质量

问题:Lambda 表达式参数按值传递

[this](const QDBusObjectPath &objPath, ObjectInterfaceMap interfacesAndProperties)
  • 风险ObjectInterfaceMap 通常是一个包含较多数据的 QMap 或 QHash,在 Lambda 表达式中按值传递会导致不必要的深拷贝,消耗额外的 CPU 和内存。
  • 改进建议:对于只读的 Lambda 参数,应使用 const & 传递:
    [this](const QDBusObjectPath &objPath, const ObjectInterfaceMap &interfacesAndProperties)

改进后的代码建议

结合上述意见,我为你重构了构造函数的相关代码:

AppMgr::AppMgr(QObject *parent)
{
    m_checkTimer->setInterval(3000); // 3 second interval
    connect(m_checkTimer, &QTimer::timeout, this, &AppMgr::checkPendingAppItems);

    // 1. 优化:使用 const & 避免不必要的拷贝
    connect(m_objectManager, &AppManager1ApplicationObjectManager::InterfacesAdded, this,
            [this](const QDBusObjectPath &objPath, const ObjectInterfaceMap &interfacesAndProperties) {
                const QString key(objPath.path());
                qCDebug(logDdeIntegration) << "InterfacesAdded by AM, path:" << key;
                if (m_appItems.contains(key)) { // 优化:直接使用 key,避免重复调用 objPath.path()
                    qWarning() << "App already exists for the path:" << key;
                    return;
                }
                m_checkCount = 0;
                if (auto appItem = parseDBus2AppItem(interfacesAndProperties)) {
                    qCDebug(logDdeIntegration) << "App item added, desktopId" << appItem->id;
                    watchingAppItemAdded(key, appItem);
                }
            });

    connect(m_objectManager, &AppManager1ApplicationObjectManager::InterfacesRemoved, this,
            [this](const QDBusObjectPath &objPath, const QStringList &interfaces) {
                Q_UNUSED(interfaces)
                const QString key(objPath.path());
                qCDebug(logDdeIntegration) << "InterfacesRemoved by AM, path:" << key;
                watchingAppItemRemoved(key);
            });

    // 2. 改进:处理 DConfig 无效时的内存释放
    DConfig *config = DConfig::create("org.deepin.dde.application-manager", "org.deepin.dde.am", "", this);
    if (!config || !config->isValid()) {
        qCWarning(logDdeIntegration) << "DConfig is invalid when getting launched times.";
        delete config; // 防止无效对象常驻内存
    } else {
        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 &value = config->value(AppsLaunchedTimes).toMap();
            updateAppsLaunchedTimes(value);
        });
    }

    if (isValid()) {
        fetchAppItems();
    }

    // 3. 改进:增加 DBus 连接有效性检查
    QDBusConnection bus = QDBusConnection::sessionBus();
    if (!bus.isConnected()) {
        qCWarning(logDdeIntegration) << "Session bus is not connected, service watcher disabled.";
    } else {
        m_serviceWatcher = new QDBusServiceWatcher(QStringLiteral("org.desktopspec.ApplicationManager1"),
                                                   bus,
                                                   QDBusServiceWatcher::WatchForRegistration,
                                                   this);
        connect(m_serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, [this]() {
            qCInfo(logDdeIntegration) << "AppManager1 service registered on bus, fetching app items";
            // 注意:如果 m_objectManager 在服务重启后需要重新生成,应在此处处理
            fetchAppItems();
        });
    }
}

关于 AppsModel 的变更:
移除 if (AppMgr::instance()->isValid()) 是合理的,因为现在有了 QDBusServiceWatcher 动态监听服务上线,信号可以在任何时候触发,前置检查反而会导致服务后启动时模型无法更新。这部分改动逻辑正确,保持即可。

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.

Hey - I've left some high level feedback:

  • In the AppMgr constructor you now connect to m_objectManager signals unconditionally, but previously this was guarded by isValid() in initObjectManager(); if m_objectManager can be null or otherwise invalid, these new connects risk a crash and should be protected by the same validity check.
  • AppsModel now connects to AppMgr::changed and itemDataChanged without checking AppMgr::instance()->isValid(); if those signals are only meaningful when the manager is valid, you may want to keep a similar guard or at least document that the model can handle a temporarily invalid manager.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the AppMgr constructor you now connect to m_objectManager signals unconditionally, but previously this was guarded by isValid() in initObjectManager(); if m_objectManager can be null or otherwise invalid, these new connects risk a crash and should be protected by the same validity check.
- AppsModel now connects to AppMgr::changed and itemDataChanged without checking AppMgr::instance()->isValid(); if those signals are only meaningful when the manager is valid, you may want to keep a similar guard or at least document that the model can handle a temporarily invalid manager.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743, 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

@BLumia BLumia merged commit f4feb31 into linuxdeepin:master May 29, 2026
10 of 11 checks passed
@BLumia BLumia deleted the treeland-blank-app branch May 29, 2026 07:35
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.

3 participants