Blankj/AndroidUtilCode · error · IllegalArgumentException

The key is null.

Error message

The key is null.

What it means

DebouncingUtils.isValid(String, long) keys recent activity by a string and returns whether enough time has elapsed. It rejects an empty/null key with IllegalArgumentException because the internal KEY_MILLIS_MAP is a ConcurrentHashMap and TextUtils.isEmpty(key) covers both null and ""; an empty key would collide all debounced callers into one slot and silently suppress legitimate events.

Source

Thrown at lib/utilcode/src/main/java/com/blankj/utilcode/util/DebouncingUtils.java:61

     *
     * @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 7b4caf9e54)

Solutions

  1. Always derive a meaningful, non-empty key (e.g. a stable view ID, a tag, or a namespaced event name) before calling isValid.
  2. Guard with if (!TextUtils.isEmpty(key)) before invoking, and skip/no-op when the key is empty.
  3. Prefer the view overload isValid(View, long) when debouncing UI clicks — it derives the key internally.
  4. If the key comes from external input, validate and fall back to a deterministic default at the boundary.

Example fix

// before
boolean ok = DebouncingUtils.isValid(eventName, 1000); // eventName may be ""

// after
String key = TextUtils.isEmpty(eventName) ? "fallback:" + viewId : eventName;
boolean ok = DebouncingUtils.isValid(key, 1000);
Defensive patterns

Strategy: validation

Validate before calling

// Validate the key before debouncing
String key = eventName;
if (TextUtils.isEmpty(key)) {
    key = "fallback:" + viewId; // or skip the call
}
boolean ok = DebouncingUtils.isValid(key, duration);

Type guard

// Ensure a usable key
public static String nonEmptyDebounceKey(String raw, String fallback) {
    return (raw == null || raw.trim().isEmpty()) ? fallback : raw;
}

Try / catch

try {
    boolean ok = DebouncingUtils.isValid(key, duration);
} catch (IllegalArgumentException e) {
    // key was empty; substitute a stable key and retry
    ok = DebouncingUtils.isValid("fallback:" + viewId, duration);
}

Prevention

When it happens

Trigger: Calling isValid(key, duration) where key is null, "", or whitespace-only produced by String.valueOf of a null/empty source; building a key from an object whose hash source is unset; forwarding a user-entered identifier that was never validated.

Common situations: Using String.valueOf(view.hashCode()) on a view whose hashCode is 0 and then trimming; debouncing by an analytics/event name that came back empty from a config; a per-item key derived from a missing database column; copy-paste of the view-based overload with the wrong argument.

Related errors


AI-assisted analysis of Blankj/AndroidUtilCode@7b4caf9e54 (2026-08-14). Data as JSON: /api/errors/6b2881f76e64abea. Report an issue: GitHub.