Skip to content

ADFA-3160 | Fix IllegalStateException by forcing main thread updates on ListView adapter#1462

Open
jatezzz wants to merge 3 commits into
stagefrom
fix/ADFA-3160-listview-background-thread-crash
Open

ADFA-3160 | Fix IllegalStateException by forcing main thread updates on ListView adapter#1462
jatezzz wants to merge 3 commits into
stagefrom
fix/ADFA-3160-listview-background-thread-crash

Conversation

@jatezzz

@jatezzz jatezzz commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Description

This PR resolves a recurrent crash (java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification) in TermuxActivity.

The crash happens because underlying session data changes are being processed on a background thread while the UI thread attempts a layout pass on ListView before notifyDataSetChanged() executes on the main thread. By adding explicit safety scaffolding with Looper.myLooper() == Looper.getMainLooper(), we guarantee that layout dataset notifications dispatch correctly and sync immediately with the UI thread scheduler.

Details

java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread.
    at android.widget.ListView.layoutChildren(ListView.java:1715)
    at android.widget.AbsListView.onLayout(AbsListView.java:2524)

Screen.Recording.2026-06-29.at.12.55.52.PM.mov

Ticket

ADFA-3160

Observation

The addition of the Looper verification guarantees thread compliance, eliminating race conditions between internal data collection steps and the Android layout rendering passes.

Note that this is a defensive fix, as the issue could not be reproduced locally.

…ifications

Fix crash caused by modifying or notifying ListView adapter changes from background threads.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ce4aefad-9f3f-407a-b001-6580fb7ef5c9

📥 Commits

Reviewing files that changed from the base of the PR and between 06c0ec6 and 5cc2c9a.

📒 Files selected for processing (2)
  • termux/termux-app/src/main/java/com/termux/app/TermuxActivity.java
  • termux/termux-app/src/main/java/com/termux/app/TermuxService.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • termux/termux-app/src/main/java/com/termux/app/TermuxService.java
  • termux/termux-app/src/main/java/com/termux/app/TermuxActivity.java

📝 Walkthrough
  • Fixed a recurring ListView adapter crash in TermuxActivity (IllegalStateException: The content of the adapter has changed but ListView did not receive a notification) by enforcing main-thread-only adapter refreshes using Looper checks.
  • Switched session list lookups/initialization to use immutable snapshot copies (instead of the live mutable list) to reduce concurrent modification and timing issues.
  • Added a thread-safe TermuxService.getTermuxSessionsListSnapshot() helper to provide consistent session data across threads.
  • Updated TermuxSessionsListViewController to own an ArrayList copy of the provided sessions and added updateSessions(...) to refresh the adapter with a single notifyDataSetChanged() while minimizing intermediate change signals.
  • Added defensive null checks and looper-aware refresh logic so UI updates run immediately on the UI thread or are posted to it when triggered from background work.
  • Risk: using snapshots can cause the UI to temporarily lag behind service/session mutations until the next refresh.
  • Best-practice note: the fix is intentionally defensive at the UI boundary—ensuring adapter updates are synchronized with the main thread avoids Recycler/ListView notification order violations.

Walkthrough

TermuxActivity, TermuxService, and TermuxSessionsListViewController now use copied session-list snapshots for initialization and refresh. Refreshes are dispatched on the main looper, and the adapter replaces its contents from a fresh snapshot.

Changes

Thread-safe session list refresh

Layer / File(s) Summary
Service session snapshot
termux/termux-app/src/main/java/com/termux/app/TermuxService.java
Adds a synchronized getTermuxSessionsListSnapshot() helper and synchronizes session-exit handling.
Controller snapshot update
termux/termux-app/src/main/java/com/termux/app/terminal/TermuxSessionsListViewController.java
Copies the initial session list into the adapter and adds updateSessions(@nonnull List<TermuxSession>) to replace adapter contents and trigger one refresh.
Activity refresh path
termux/termux-app/src/main/java/com/termux/app/TermuxActivity.java
Switches controller setup and session lookup to snapshots, adds looper-aware refresh dispatch, and refreshes the controller from a fresh service snapshot with null checks.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • Daniel-ADFA

Poem

Hop, hop—fresh snapshots in the air,
A tidy session list with rabbit care.
On the main thread, the UI gleams,
Clear little updates, like carrot dreams.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main fix: preventing ListView adapter crashes by ensuring main-thread updates.
Description check ✅ Passed The description is directly related to the crash fix and thread-safety changes in the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ADFA-3160-listview-background-thread-crash

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.23.0)
termux/termux-app/src/main/java/com/termux/app/TermuxActivity.java

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

[00.11][ERROR]: unable to find a config; path .coderabbit-opengrep-fallback.yml does not exist

termux/termux-app/src/main/java/com/termux/app/TermuxService.java

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

[00.11][ERROR]: unable to find a config; path .coderabbit-opengrep-fallback.yml does not exist


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@termux/termux-app/src/main/java/com/termux/app/TermuxActivity.java`:
- Around line 976-982: Serialize the session-list update with the UI refresh by
ensuring the mutation in addNewSession() and the refresh in
termuxSessionListNotifyUpdated() happen on the main thread together, or by
having TermuxSessionListViewController work from an immutable snapshot instead
of the live mTermuxSessions ArrayList. Update the code paths around
addNewSession(), mTermuxSessions, and termuxSessionListNotifyUpdated() so
ListView never observes the list while a background executor is modifying it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55a7dfa3-6e3e-4712-8b8a-e218b0b46635

📥 Commits

Reviewing files that changed from the base of the PR and between fc5b6f9 and 31705c9.

📒 Files selected for processing (1)
  • termux/termux-app/src/main/java/com/termux/app/TermuxActivity.java

Comment thread termux/termux-app/src/main/java/com/termux/app/TermuxActivity.java
…thread-only snapshot instead of sharing TermuxService's live session list mutated off the UI thread
@jatezzz

jatezzz commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

@claude review once

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, comment @claude review on this pull request to trigger a review.

@jatezzz jatezzz requested review from a team, Daniel-ADFA and itsaky-adfa June 29, 2026 18:13

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@termux/termux-app/src/main/java/com/termux/app/TermuxActivity.java`:
- Line 631: `onServiceConnected()` still uses the live session list for the
working-directory lookup, which can race with concurrent session updates. Update
the lookup in `TermuxActivity.onServiceConnected()` to iterate over
`mTermuxService.getTermuxSessionsListSnapshot()` instead of
`getTermuxSessions()`, matching the snapshot-based approach already used for
`TermuxSessionsListViewController` and avoiding concurrent modification issues.

In `@termux/termux-app/src/main/java/com/termux/app/TermuxService.java`:
- Around line 899-907: `getTermuxSessionsListSnapshot()` assumes
`mShellManager.mTermuxSessions` is always mutated under the service monitor, but
`onTermuxSessionExited()` still removes sessions without taking that same lock.
Update `onTermuxSessionExited()` in `TermuxService` to synchronize on the same
monitor used by `getTermuxSessionsListSnapshot()` before touching
`mShellManager.mTermuxSessions`, so all session add/remove paths share the same
lock and the snapshot guarantee stays consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9643ae6a-a8fc-46a1-a7fe-0c2d8bb548d3

📥 Commits

Reviewing files that changed from the base of the PR and between 31705c9 and 06c0ec6.

📒 Files selected for processing (3)
  • termux/termux-app/src/main/java/com/termux/app/TermuxActivity.java
  • termux/termux-app/src/main/java/com/termux/app/TermuxService.java
  • termux/termux-app/src/main/java/com/termux/app/terminal/TermuxSessionsListViewController.java

Comment thread termux/termux-app/src/main/java/com/termux/app/TermuxActivity.java
Comment thread termux/termux-app/src/main/java/com/termux/app/TermuxService.java
…working-dir lookup to keep getTermuxSessionsListSnapshot consistent and avoid concurrent-modification races
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