AI summary
Xposed module that removes sponsored posts from the Threads feed before they render. Version 2.0.0 adds libxposed API 102 support, hot reload, and a hand-written Brainfuck policy core; requires Android 8.0 or newer and a compatible framework.
Generated by AI. May contain inaccuracies.
About this app
Threads Hide Ads
An Xposed module that removes sponsored posts from Threads, with its ad policy written in Brainfuck. Yes, really.
Ads are taken out of the feed before Threads draws them, not hidden after.
Why Brainfuck?
Surely nobody would write the decision logic of an ad blocker in Brainfuck.
Brainfuck was selected for its rich ecosystem, mature package manager, excellent Android SDK bindings, comprehensive type system, first-class coroutine support, and famously pleasant debugging experience.
Just joking.
The real idea is much less cursed than it sounds: keep every decision about Threads out of the Android code. Which list is the feed, what counts as an advertisement, how deep to look into a model object, when to retry, when to give up, which fallback to trust — that is the part that has to change when Threads changes, and it is the part that tends to leak into hook callbacks, reflection helpers and view listeners until nobody can say where the policy lives.
Brainfuck is very good at enforcing that boundary, because it literally cannot call Android, Xposed, DexKit or JNI. It can read bytes and write bytes. Anything that touches a Java object has to go through a small, documented bridge — "read this field", "remove the items I marked", "stream me this class name" — and the Java side only ever does what it is asked. The two layers cannot quietly melt together, because one of them has no way to reach the other.
It is not slow either. The six programs are written by hand in plain eight-command Brainfuck, compiled ahead of time to C and then to native code with the NDK. There is no interpreter in the APK.
Requirements Android 8.0 (API 26) or newer App Threads (com.instagram.barcelona) Framework Vector or LSPosed with libxposed API 102, or LSPatch (see Known limits) Root Required by Vector/LSPosed; the module itself asks for none Built against the modern libxposed API (io.github.libxposed:api), not the legacy de.robv.android.xposed bridge.
Install 1. Install the APK from Releases 2. Enable Threads Hide Ads in Vector or LSPosed 3. Force-stop Threads once, then open it The module declares a static scope — Threads only — so there is nothing to pick. Later updates are hot reloaded into a running Threads process. Vector reloads a module only when its versionCode changes; force-stop Threads if a reload is not offered.
The framework log shows what happened, in lines beginning with ThreadsHideAds::
Application.attach observed; starting hook resolution Hook initialization complete; installedHooks=38, pandoHooks=28 Removed 1 ad item(s) from argument 4 of ... [modelTotal=1, event=1] Without root: LSPatch
The module has no root-only calls, so LSPatch can embed it into Threads:
Install the LSPatch manager, or use the LSPatch command-line jar. Patch Threads with Threads Hide Ads as an embedded module. Patch the base APK together with all of its split APKs. Uninstall the store copy of Threads, then install the patched APKs as a set. The patched build is signed with LSPatch's key, so it cannot be installed over the store copy. Embedded mode needs no manager afterwards; local mode keeps the module managed by LSPatch and works alongside Shizuku. See Known limits for 2.0.0.
ADME GPL-3.0 license Threads Hide Ads Threads Hide Ads An Xposed module that removes sponsored posts from Threads, with its ad policy written in Brainfuck. Yes, really.
Ads are taken out of the feed before Threads draws them, not hidden after.
Release Downloads Android libxposed
Note
Threads renames its obfuscated classes with almost every update. The module finds its targets again by shape at each start, and when it cannot be sure it installs nothing rather than guess. See the compatibility table for what has actually been tested.
Why Brainfuck? Surely nobody would write the decision logic of an ad blocker in Brainfuck.
Brainfuck was selected for its rich ecosystem, mature package manager, excellent Android SDK bindings, comprehensive type system, first-class coroutine support, and famously pleasant debugging experience.
Just joking.
The real idea is much less cursed than it sounds: keep every decision about Threads out of the Android code. Which list is the feed, what counts as an advertisement, how deep to look into a model object, when to retry, when to give up, which fallback to trust — that is the part that has to change when Threads changes, and it is the part that tends to leak into hook callbacks, reflection helpers and view listeners until nobody can say where the policy lives.
Brainfuck is very good at enforcing that boundary, because it literally cannot call Android, Xposed, DexKit or JNI. It can read bytes and write bytes. Anything that touches a Java object has to go through a small, documented bridge — "read this field", "remove the items I marked", "stream me this class name" — and the Java side only ever does what it is asked. The two layers cannot quietly melt together, because one of them has no way to reach the other.
It is not slow either. The six programs are written by hand in plain eight-command Brainfuck, compiled ahead of time to C and then to native code with the NDK. There is no interpreter in the APK.
🧹 Removed, not hidden Sponsored items are taken out of the feed lists before Threads renders them, so there are no blank gaps and no flashes of an ad. 🔎 Survives renames DexKit finds the feed boundary by shape at each start. An ambiguous match installs nothing broad. 🧠 Policy in Brainfuck, compiled Every decision is hand-written Brainfuck, compiled ahead of time to native code. The APK contains no interpreter. 🛟 Fails open Anything the module cannot classify is left alone. A failed request keeps the list as it was. 🔁 No restart to update Vector hot reloads a new build into a running Threads process. 🧩 Layered Model hooks, a Pando response filter and a rendered-label fallback each work on their own; losing one does not lose the others. What it removes An advertisement is recognized by its feed unit type. Threads tags every feed item with a unit-type enum whose constants keep readable names even in an obfuscated build, and the advertisement values are matched whole:
AD, AD4AD, INTENT_AWARE_AD_PIVOT, STAND_ALONE_MULTI_AD_PIVOT, ADS_FEEDBACK_INTERFACE and its two variants Recommendation units, such as the NETEGO and SUGGESTED families, are deliberately left in place.
🧱 The three layers
Layer Where What it does Feed boundary the method that hands a batch of feed items to the cache, found by DexKit Removes ad items from the list argument before the call and again after it (late insertion), then cleans lists nested inside the returned wrapper, with bounded recursion. Pando responses list accessors on com.facebook.pando.TreeJNI, selected by shape Filters sponsored trees out of GraphQL response lists. The names come from the native binding, not the obfuscator, so this layer needs no DexKit and stays installed when discovery fails. Rendered label framework TextView updates If a localized "Sponsored" label still reaches the screen, collapses its card. The card is restored only when that same label view is rebound to an organic item. Lists are sanitized in place when they are mutable. A replacement list is used only where the caller, result, field, map entry, list slot or array slot can actually take it. A feed batch that would become empty is left alone; the item that owns it is dropped one level up instead.
Status v2.0.0. Moved to the modern Xposed API (libxposed 102) with hot reload, and every policy program rewritten by hand in plain Brainfuck. Each rewritten program was run side by side with the one it replaces over thousands of randomized requests, with identical answers and identical traffic to the host (docs/PARITY.md).
Compatibility Each row is one setup somebody has actually run. If you try another, please open a pull request adding a row.
Device Android Framework Threads Module Result Tester Date Pixel 8 Pro 17 Vector 2.2 (API 102) 448.0.0.54.85 2.0.0 38 model + 28 Pando hooks, 12 ads removed in 35 scrolls, no labels @MrxSiN 2026-09 Pixel 8 Pro 16 LSPosed 448.0.0.54.85 1.3.0 10–12 ads removed per session @MrxSiN 2026-09 — — LSPatch (embedded and local) — 1.x reported working in issue #1 @qradov 2026-8 Known limits LSPatch needs a build that loads libxposed API 102 modules. The reports above are for 1.x; 2.0.0 has not been tried under LSPatch. 32-bit and x86 devices ship the native library but have not been run. A Threads update can move the feed boundary. When it does, the Pando and label layers keep working and the log says what discovery found. Requirements Android 8.0 (API 26) or newer App Threads (com.instagram.barcelona) Framework Vector or LSPosed with libxposed API 102, or LSPatch (see Known limits) Root Required by Vector/LSPosed; the module itself asks for none Built against the modern libxposed API (io.github.libxposed:api), not the legacy de.robv.android.xposed bridge.
Install 1. Install the APK from Releases 2. Enable Threads Hide Ads in Vector or LSPosed 3. Force-stop Threads once, then open it The module declares a static scope — Threads only — so there is nothing to pick. Later updates are hot reloaded into a running Threads process. Vector reloads a module only when its versionCode changes; force-stop Threads if a reload is not offered.
The framework log shows what happened, in lines beginning with ThreadsHideAds::
Application.attach observed; starting hook resolution Hook initialization complete; installedHooks=38, pandoHooks=28 Removed 1 ad item(s) from argument 4 of ... [modelTotal=1, event=1] Without root: LSPatch
The module has no root-only calls, so LSPatch can embed it into Threads:
Install the LSPatch manager, or use the LSPatch command-line jar. Patch Threads with Threads Hide Ads as an embedded module. Patch the base APK together with all of its split APKs. Uninstall the store copy of Threads, then install the patched APKs as a set. The patched build is signed with LSPatch's key, so it cannot be installed over the store copy. Embedded mode needs no manager afterwards; local mode keeps the module managed by LSPatch and works alongside Shizuku. See Known limits for 2.0.0.
How it works Threads process ModuleMain ─────────────► Policy (bridge) ──JNI──► libthreadsbf.so ModelHookInstaller typed requests runtime + six AOT programs FeedBoundaryFinder ▲ │ FeedModelHooks │ capability frames │ PandoResponseHooks Host (bridge) ◄────────────┘ RenderedLabelHooks reflection, text streams, list mutation, view reads One rule decides where code goes: if it needs Android, Java, Xposed, DexKit or JNI, Java does it; if it decides what to do with what Java read, Brainfuck decides.
Starting at the right moment The Brainfuck core Staying fast Updating without restarting Threads Build python tools/bftool/gen.py # check brainfuck/src, regenerate C, ABI and memory map python -m unittest discover -s tests/compiler # optimizer, AOT and text parity tests ./gradlew :app:testDebugUnitTest # JVM parity against the frozen 1.2.1 policy ./gradlew :app:assembleRelease The generated files are committed; the Gradle build checks them with checkBrainfuck and never edits them. scripts/check-project.sh runs the static project checks.
You need JDK 17+, Android SDK 36 with NDK 28.2.13676358 and CMake 3.22.1, Python 3.10+, and for the JVM tests a host C compiler (MSVC Build Tools on Windows, gcc or clang elsewhere).
CI checks the generated files, runs the Python and JVM tests and builds a release APK on every push and pull request. Pushes and tags are signed when the four signing secrets are configured; pull-request builds stay unsigned. A v* tag publishes a GitHub Release with the signed APK.
Design brainfuck/src/ the six hand-written programs: every decision, word list and limit brainfuck/ programs.json, constants.txt (ABI numbers), generated memory map tools/bftool/ source checker, optimizer, C emitter, reference interpreter (tests only) app/src/main/cpp/ runtime, JNI glue and the generated C bridge/ Policy (typed requests), NativeCore, Host capabilities, handles, reflection ModuleMain libxposed entry: routes Threads to the layers, hot reload hook/ model and Pando hooks, the call-frame adapter discovery/ DexKit queries and class lookups the core asks for ui/ rendered-label listeners, view traversal, hide and restore core/ module runtime, logging, statistics wording The layering, the ABI and the migration from the Java policy are in docs/ARCHITECTURE.md and docs/MIGRATION.md; Threads-specific findings are in HOOK_NOTES.md.
Troubleshooting Problem Try Sponsored posts still appear Check the log for Hook initialization complete. An initialization attempt failed Keep Threads open for a few seconds while the retries run; if all of them fail, export the ThreadsHideAds: log lines. No module log lines Check that the module is enabled, force-stop Threads once, and check the framework status. Ambiguous method warning The Threads build has several possible targets and nothing broad was installed. Report the log lines. Broken after a Threads update Threads may have changed its internal classes. Report the ThreadsHideAds: log lines with the Threads version. Credits Project Contribution DexKit by LuckyPray Runtime DEX parsing and discovery of obfuscated classes and methods (Apache-2.0). libxposed API, Vector, LSPosed The hooking API and frameworks the module runs on. ReVanced-family Threads patches The method-signature reference used to update the sponsored-content hook. The module is derived from the GPL-3.0 Threads ad-removal concept and has been updated for the newer sponsored-content insertion method.
Disclaimer Not affiliated with, endorsed by or sponsored by Meta or Threads. Provided for educational and personal use. Threads updates may break the hooks without notice.
What's new
- Modern Xposed API, hot reload and a hand-written Brainfuck core This release changes how the module is loaded and how its policy is written, without changing what it removes.
- Changed
- Uses the modern Xposed API (libxposed 102). The module is described by META-INF/xposed/ (java_init.list, module.prop, scope.list) instead of xposed_init and manifest metadata. Requires Android 8.0 or newer and a framework that implements API 102 (Vector, current LSPosed). Supports hot reload: an update is applied to a running Threads process. The old module generation stops its native core and hands its hidden feed cards to the new one. New adaptive vector launcher icon with a themed (monochrome) layer. All six policy programs in libthreadsbf.so are now hand-written Brainfuck (brainfuck/src/*.bf). The previous macro assembler is removed. Each program was tested side by side with the one it replaces: identical responses and identical host traffic over thousands of randomized requests. The native library is smaller (arm64: 647 KB, was 729 KB); host benchmarks are unchanged within noise.
- Verified
- Parity with 1.2.1 on the JVM (text matchers, detector, list and nested sanitizers, boundary flow, core decisions). libxposed 102 loading and hot reload on a Pixel 8 Pro, Android 17, Vector 2.2. The hand-written core on the same device with Threads 448.0.0.54.85: initialization complete on the first attempt (38 model hooks, 28 Pando hooks), 12 advertisements removed over a cold launch and 35 scroll passes, no Ad or Sponsored label in uiautomator dumps, empty crash buffer.
- LSPatch modes, 32-bit and x86 devices were not re-run for this release.
About this version
- Version
- 2.0.0 (8)
- Size
- 4.64 MB
- Requires Android
- 8.0
- Target SDK
- 26
- Architecture
- x86, x86_64, arm64-v8a, armeabi-v7a
- Downloads
- 19
- Updated
- Oct 26, 2026
- Package
- io.github.mrxsin.threadshideads
Similar apps
Ratings & reviews
- 50
- 40
- 30
- 20
- 10