Mobile Development 17 min read

Analyzing and Solving Android Toast Issues with Snackbar and View System

The article examines Android Toast crashes, explains the system‑level window and drawing lifecycle, and proposes safer alternatives by replacing Toast with a Snackbar or custom view hierarchy, using parent‑finding logic, weak‑reference management, and a protective handler wrapper to prevent exceptions.

Tencent Music Tech Team
Tencent Music Tech Team
Tencent Music Tech Team
Analyzing and Solving Android Toast Issues with Snackbar and View System

This article continues the deep dive into Android Toast problems introduced in the first part. It focuses on practical solutions, mainly by replacing the system‑level Toast window with a custom view implementation such as Snackbar or a direct View hierarchy.

Review : The previous article explained how Toast creates a window through NotificationManager and why timing mismatches cause crashes.

Solution Idea : Instead of using the system window, we can use a child window ( PopupWindow or Dialog ) or the View system. The article proposes using Snackbar as a façade that internally creates a SnackbarLayout attached to the activity’s android.R.id.content node.

The key method for finding a suitable parent is:

private static ViewGroup findSuitableParent(View view) {
    ViewGroup fallback = null;
    do {
        if (view instanceof CoordinatorLayout) {
            return (ViewGroup) view;
        } else if (view instanceof FrameLayout) {
            if (view.getId() == android.R.id.content) {
                return (ViewGroup) view;
            } else {
                fallback = (ViewGroup) view;
            }
        }
        // ...
    } while (view != null);
    return fallback;
}

When the parent is found, Snackbar inflates its layout:

private Snackbar(ViewGroup parent) {
    mTargetParent = parent;
    mContext = parent.getContext();
    LayoutInflater inflater = LayoutInflater.from(mContext);
    mView = (SnackbarLayout) inflater.inflate(R.layout.design_layout_snackbar, mTargetParent, false);
}

Android Drawing Lifecycle : The article details how an Activity creates a Window , which holds a PhoneWindow . The PhoneWindow installs a DecorView that contains the content FrameLayout . The drawing process is driven by a ~16 ms VSYNC signal and follows these steps:

Generate DecorView and content via installDecor() .

When WindowManager.addView is called, a layout request triggers a traversal.

ViewRootImpl.scheduleTraversals() posts a Choreographer.CALLBACK_TRAVERSAL runnable.

The traversal runs measure() , layout() , and draw() on the view hierarchy.

Key source snippets:

@Override
public void setContentView(int layoutResID) {
    if (mContentParent == null) {
        installDecor(); // creates DecorView
    } else {
        mContentParent.removeAllViews();
    }
    mLayoutInflater.inflate(layoutResID, mContentParent);
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}

To detect the exact moment drawing starts, the article suggests using Choreographer.getInstance().postFrameCallback() in onResume and logging the timestamp.

@Override
protected void onResume() {
    super.onResume();
    Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            start = SystemClock.uptimeMillis();
            Log.d("cdw", "Drawing start: height=" + view.getHeight());
        }
    });
}

Snackbar Internals : Snackbar.make() creates the object, sets text and duration, and the SnackbarManager (which uses weak references for callbacks) controls the show/hide timing, similar to NotificationManager . The manager adds the SnackbarLayout to the previously found parent.

Memory‑leak concerns are mitigated because SnackbarManager stores callbacks in a WeakReference :

private static class SnackbarRecord {
    final WeakReference
callback;
    // ...
}

Making Toast Safer : The article shows that crashes often happen inside the internal Handler of Toast.TN . By wrapping this handler with a safe decorator that catches all exceptions, the app can avoid crashes.

private static class SafelyHandlerWarpper extends Handler {
    private Handler impl;
    public SafelyHandlerWarpper(Handler impl) { this.impl = impl; }
    @Override
    public void dispatchMessage(Message msg) {
        try { super.dispatchMessage(msg); } catch (Exception e) {}
    }
    @Override
    public void handleMessage(Message msg) { impl.handleMessage(msg); }
}

Reflection is used to replace the original handler:

public static void showToast(Context context, CharSequence cs, int length) {
    Toast toast = Toast.makeText(context, cs, length);
    hook(toast);
    toast.show();
}

private static void hook(Toast toast) {
    try {
        Object tn = sField_TN.get(toast);
        Handler preHandler = (Handler) sField_TN_Handler.get(tn);
        sField_TN_Handler.set(tn, new SafelyHandlerWarpper(preHandler));
    } catch (Exception e) {}
}

A test method calling ToastUtils.showToast() and then sleeping for 10 seconds demonstrates that the process no longer crashes after the toast disappears.

Overall, the article provides a comprehensive analysis of the Android Toast lifecycle, the drawing pipeline, and practical code‑level solutions to replace or safeguard Toast with custom Snackbar implementations and safe handler wrappers.

uiAndroidlifecycleHandlerSnackbarToastview()
Tencent Music Tech Team
Written by

Tencent Music Tech Team

Public account of Tencent Music's development team, focusing on technology sharing and communication.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.