didi/DoKit · error · IllegalArgumentException
The key is null.
Error message
The key is null.
What it means
DebouncingUtils.isValid(key, duration) implements click-debouncing keyed on a string (typically a View hash) stored in a static map with an expiry timestamp. An empty or null key cannot identify a debounce entry, so it fails fast with IllegalArgumentException('The key is null.') despite the @NonNull annotation — the annotation is not enforced at runtime in Android unless Kotlin null-checks are in play.
Source
Thrown at Android/dokit-util/src/main/java/com/didichuxing/doraemonkit/util/DebouncingUtils.java:62
*
* @param view The view.
* @param duration The duration.
* @return {@code true}: yes<br>{@code false}: no
*/
public static boolean isValid(@NonNull final View view, final long duration) {
return isValid(String.valueOf(view.hashCode()), duration);
}
/**
* Return whether the key is not in a jitter state.
*
* @param key The key.
* @param duration The duration.
* @return {@code true}: yes<br>{@code false}: no
*/
public static boolean isValid(@NonNull String key, final long duration) {
if (TextUtils.isEmpty(key)) {
throw new IllegalArgumentException("The key is null.");
}
if (duration < 0) {
throw new IllegalArgumentException("The duration is less than 0.");
}
long curTime = SystemClock.elapsedRealtime();
clearIfNecessary(curTime);
Long validTime = KEY_MILLIS_MAP.get(key);
if (validTime == null || curTime >= validTime) {
KEY_MILLIS_MAP.put(key, curTime + duration);
return true;
}
return false;
}
private static void clearIfNecessary(long curTime) {
if (KEY_MILLIS_MAP.size() < CACHE_SIZE) return;
for (Iterator<Map.Entry<String, Long>> it = KEY_MILLIS_MAP.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Long> entry = it.next();View on GitHub (pinned to 626827cddb)
Solutions
- Always pass a non-empty key; for views use the provided isValid(view, duration) overload which derives the key from hashCode().
- If the key is dynamic, fall back to a constant: key = TextUtils.isEmpty(key) ? "default" : key.
- Add Objects.requireNonNull(key) at your call site to fail with a clearer stack trace.
Example fix
// before DebouncingUtils.isValid(tag, 500); // tag == "" -> throws // after String safeTag = TextUtils.isEmpty(tag) ? "global" : tag; DebouncingUtils.isValid(safeTag, 500);
Defensive patterns
Strategy: validation
Validate before calling
String safeKey = TextUtils.isEmpty(key) ? "fallback_key" : key; boolean valid = DebouncingUtils.isValid(safeKey, duration);
Prevention
- Prefer the isValid(View, duration) overload which generates the key for you.
- Default dynamic keys to a constant rather than allowing empty strings.
When it happens
Trigger: Calling isValid with an empty String key: isValid("", 500); or from Java with a null key that slips past @NonNull; building the key from a nullable field via String.valueOf(null) producing 'null' is fine, but concatenation of nulls or trimming can produce empty strings.
Common situations: Debouncing helper wrappers that accept an optional tag and default it to null/empty; dynamic keys built from view identifiers that are unset in some layout variants; Java callers ignoring the @NonNull contract.
Related errors
- precision shouldn't be less than zero!
- The duration is less than 0.
- maxStoredHeapDumps must be at least 1
- Not an array: " + array.getClass()
- Array has incompatible type: " + array.getClass()
AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14).
Data as JSON: /api/errors/e2bab2bff3a21410.
Report an issue: GitHub.