Open-source project
badoo/android-weak-handler avatar
badoo/android-weak-handler

WeakHandler holds your delayed Runnable weakly, at the cost of one hard reference

Memory safer implementation of android.os.Handler

1,533 stars282 forksJavaMIT

At a glance

What is it?
android-weak-handler is a Badoo library that replaces android.os.Handler with a version storing WeakReferences to posted Runnables and Messages, so a five second postDelayed no longer pins an Activity until the timeout expires. Version 1.3 dates from 2022 and is installed from JitPack.
Who is it for?
Reach for WeakHandler when you have a legacy Java codebase that posts delayed callbacks against an Activity or Fragment and cannot yet move to lifecycle-scoped work, since it is a small drop-in rather than a migration.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 57 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why a five second postDelayed keeps an Activity alive

The problem statement in the README is more precise than the usual warning about Handler leaks, and the precision is what makes the library make sense.

The original implementation of Handler always keeps a hard reference to the handler in its queue of execution. Any object in a Message or Runnable posted to `android.os.Handler` is therefore hard referenced for some period. If you create an anonymous Runnable and call `postDelayed` with a large timeout, that Runnable is held in memory until the timeout passes.

The expensive part is the second sentence of consequence: even if your Runnable seems small, it indirectly references its owner class, which is usually something as big as an Activity or Fragment. An anonymous inner class captures its enclosing instance, so a thirty second delayed callback holds an entire Activity, its view tree and its context through that timeout, regardless of how little work the callback does.

That failure mode is hard to see in a profiler because it resolves. The object graph closes over an ordinary Activity reference and nothing in the dump looks unusual. What you observe instead is a retained Activity after `onDestroy`, or a configuration change that never releases. Badoo wrote up the investigation on its engineering blog, and the README links that post as the longer explanation.

The distinction that matters for choosing a fix is when the reference is held. A leak that happens because a callback runs forever is a bug in your code. A leak that happens because the callback is scheduled five seconds out is a property of the platform class, and that is the case this library addresses.

WeakReferences, and the one hard reference you still need

The mechanism is a substitution rather than a repair. `WeakHandler` is described as trickier than `android.os.Handler`, and it keeps `WeakReferences` to runnables and messages, so the garbage collector can collect them once the `WeakHandler` instance is no longer referenced.

The usage example makes the requirement explicit in a comment on the field declaration: we still need at least one hard reference to WeakHandler. The Activity holds the handler in a field, so the handler lives as long as the Activity, and the weak references it holds can be cleared once nothing else keeps the Runnable alive. That is exactly the lifetime you want.

The interface is otherwise the one you already know. The example constructs it with no arguments in `onCreate` and calls `postDelayed` with an anonymous Runnable and a five second delay, the same shape as the platform call, which is why the library describes itself as something to use as you would normally use Handler.

Adding it to a Gradle build from JitPack

There is one documented installation route, and it goes through JitPack rather than a package repository you control. Add the repository and the dependency coordinate:

groovy
repositories {
    maven { url 'https://jitpack.io' }
}

dependencies {
    implementation 'com.github.badoo:android-weak-handler:1.3'
}

Two details in that block deserve attention before you copy it.

First, the artifact is `com.github.badoo:android-weak-handler:1.3`, so it is built from a GitHub repository on request rather than published as a release by Badoo. That has a practical consequence for reproducible builds: a repository dependency resolves through a third-party build service, and if that service or the upstream tag is unavailable, your build fails in a way a normal dependency would not. Pin the exact version, which the example does, and avoid a dynamic or snapshot reference.

Second, it is on JitPack at all, which is the same point. In an organisation with a strict allowlist of artifact repositories, adding a build-time repository is a change to your supply chain policy, not just to a build file.

Beyond the Gradle coordinate, the repository root shows a Gradle project with a wrapper (`gradlew`, `gradlew.bat`) and a `gradle/` directory, a `consumer-rules.pro` file, `gradle.properties`, and an `mvn-local-support.gradle` script. The consumer rules file is the ProGuard or R8 configuration shipped to consuming apps, which for a library this small is likely about keeping the class and its members from being stripped or renamed; the README does not describe its contents, so check it directly if you are debugging a release build. The `mvn-local-support.gradle` script suggests the library can also be staged locally for testing rather than consumed from the remote repository.

When this is the wrong tool in 2026

The library is small and the problem it solves has a better answer in current Android code, which is the main reason to be deliberate about adopting it.

The clearest case is a new Kotlin project. Kotlin coroutines with a lifecycle scope give you a cancellable job tied to the Activity or Fragment lifecycle, so a pending delay is cancelled when the lifecycle ends rather than merely becoming collectable. That is not a mitigation, it is the absence of the queue. Choosing WeakHandler there means adding a third-party library to solve a problem the language and the framework now solve together.

A second case is a delay you can simply avoid. If the callback only matters while the screen is visible, cancelling it in `onStop` or `onDestroy` with `removeCallbacks` fixes the leak without changing anything about how the callback is stored. That is a few lines and no new dependency.

A third case is a callback that fires once and posts nothing further. The leak window is the timeout, which is bounded. If your timeout is short, the retained memory may not matter; if it is thirty seconds on a low-memory device, it does. Decide by measuring your actual retention rather than by principle.

Two limits of the library itself are worth stating. It is a Handler wrapper, so it inherits Handler's main-thread requirement without adding anything, and it does not solve leaks caused by other sources, such as a listener registered and never removed, or a static reference held elsewhere in your class. Treat it as a fix for one specific mechanism, not as a leak strategy.

The README does not document Kotlin support, coroutine integration, or API surface beyond the example, so if you need those, you are choosing based on the absence of information rather than on a documented capability.

Maintenance signals and what the release history says

The repository is not archived, and the last push was on 2026-08-27, under two months before today.

The release history is older than that date suggests. The tags are 1.3 on 2022-02-24, 1.2 on 2020-12-09 and release-1.1 on 2015-06-16. Read together, those show a long release history: the last published release is several years old while commits continue on the default branch, which is `master`.

What that means practically depends on what you are consuming. The 1.3 coordinate on JitPack is built from the 1.3 tag, so you get 2022 code regardless of later commits. If a recent commit addressed a specific issue you care about, the documented dependency will not include it.

Licensing is MIT, credited to Badoo Trading Limited, with the LICENSE file at the repository root and the terms stated inline in the README. That permits use inside a closed-source commercial application with the notice retained, which is the normal expectation for an internal utility library of this kind.

The README also points at Badoo's other open source projects and their engineering blog. For a library this small there is no API surface to learn beyond the example, so the only real adoption risk is not understanding the weak reference semantics, which is worth reading once before you rely on it across an app.

Editorial conclusion

Reach for WeakHandler when you have a legacy Java codebase that posts delayed callbacks against an Activity or Fragment and cannot yet move to lifecycle-scoped work, since it is a small drop-in rather than a migration. Do not adopt it in a new Kotlin project, where structured concurrency solves the same leak without a wrapper, and do not adopt it expecting a general Handler replacement, because it is documented as trickier than the platform class and needs one hard reference to itself to stay alive. Verify first whether your build can reach JitPack at `com.github.badoo:android-weak-handler:1.3`, since that coordinate is the only documented install path.

Frequently asked questions

What memory leak does WeakHandler fix?

android.os.Handler keeps hard references to posted Runnables and Messages in its execution queue, so an anonymous Runnable posted with a long delay holds its enclosing Activity or Fragment until the timeout passes. WeakHandler stores those references weakly so the garbage collector can reclaim them.

How do I add android-weak-handler to my Android project?

Add the JitPack repository and the dependency in your build.gradle: `maven { url 'https://jitpack.io' }` in repositories and `implementation 'com.github.badoo:android-weak-handler:1.3'` in dependencies. The artifact is built from GitHub rather than published to a repository you control.

Do I still need a strong reference to WeakHandler?

Yes. The README's example comments the field explicitly: we still need at least one hard reference to WeakHandler. The handler should live exactly as long as the object it belongs to, otherwise its queue outlives the screen it was created for.

Is WeakHandler still maintained?

The repository is not archived and the last push was on 2026-08-27. However, the latest published release is 1.3 from 2022-02-24, before versions 1.2 in 2020 and 1.1 in 2015, so consuming the 1.3 coordinate gives you 2022 code.

Should I use WeakHandler in a new Kotlin project?

Probably not. Lifecycle-scoped coroutines give you cancellation tied to the Activity or Fragment lifecycle, so the pending delay never reaches the handler queue at all. The README documents no Kotlin or coroutine support.

What license is android-weak-handler released under?

MIT, credited to Badoo Trading Limited, with the LICENSE file at the repository root and the terms stated in the README. That permits use in a closed-source commercial application provided the notice is retained.

Official sources

  1. badoo/android-weak-handler on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/badoo-android-weak-handler.svg)](https://hysenlabs.com/projects/badoo-android-weak-handler)

Was this useful?