• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

陈文管的博客

分享有价值的内容

  • Android
  • Affiliate
  • SEO
  • 前后端
  • 网站建设
  • 自动化
  • 开发资源
  • 关于

Toast 自定义布局重复添加异常分析

2019年7月18日发布 | 最近更新于 2023年8月28日

Toast Exception : java.lang.IllegalStateException: View has already been added to the window manager.

Crash堆栈如下:

Exception:java.lang.IllegalStateException: View com.autonavi.skin.view.SkinRelativeLayout{95730 V.E...... ......I. 0,0-0,0}
    has already been added to the window manager.
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:328)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
at android.widget.Toast$TN.handleShow(Toast.java:498)
at android.widget.Toast$TN$1.handleMessage(Toast.java:401)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

在项目中显示Toast用到了自定义布局mLayContent,而mLayContent是重复利用的。代码示例:

Toast toast = new Toast(this);
toast.setDuration(1000);
toast.setView(mLayContent);

纯粹从发生Crash的异常堆栈来分析,针对这种异常,常规处理是在设置Toast布局前先检测下mLayContent父布局是否为空,如果不为空则从父布局中移除掉mLayContent布局。然而,这个方法在这边并没什么用。下面对Toast显示的源码逻辑处理进行分析。

一、Toast.java类源码分析

public void handleShow(IBinder windowToken) {
    if (mView != mNextView) {
        // remove the old view if necessary
        handleHide();
        mView = mNextView;
        Context context = mView.getContext().getApplicationContext();
        String packageName = mView.getContext().getOpPackageName();
        if (context == null) {
            context = mView.getContext();
        }
        mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        
        if (mView.getParent() != null) {
            mWM.removeView(mView);
        }
       
        mWM.addView(mView, mParams);
        trySendAccessibilityEvent();
    }
}
 
public void handleHide() {
    if (mView != null) {
        if (mView.getParent() != null) {
            mWM.removeViewImmediate(mView);
        }
        mView = null;
    }
} 

上面是Toast显示和隐藏的简化代码,从代码里面可以看到,显示和隐藏的时候都有对当前添加的布局是否含有父布局进行检查,如果已经添加过布局,则从WindowManager中移除这个布局。

在Crash发生之前,查看Log可以看到Toast的显示和隐藏操作非常频繁,频繁操作导致时序上的问题,在上层添加是否含有父布局的判断是没用的。到framework层WindowManager显示Toast的时候一样会报异常。

二、WindowManagerGlobal.java类源码分析

Toast显示的布局是通过WindowManager接口去添加的,找到继承WindowManager接口的实现类WindowManagerImpl。可以看到WindowManagerImpl最终是通过WindowManagerGlobal单例对象来显示的。

WindowManager addView方法
WindowManager addView方法

接着看WindowManagerGlobal里面的addView方法,可以看到抛出

" has already been added to the window manager."

异常的地方。addView方法简化代码如下:

public void addView(View view, ViewGroup.LayoutParams params,
    Display display, Window parentWindow) {
    if (view == null) {
        throw new IllegalArgumentException(“view must not be null”);
    }
    if (display == null) {
        throw new IllegalArgumentException(“display must not be null”);
    }
    if (!(params instanceof WindowManager.LayoutParams)) {
        throw new IllegalArgumentException(“Params must be WindowManager.LayoutParams”);
    }

    synchronized (mLock) {
         int index = findViewLocked(view, false);
         if (index >= 0) {
             if (mDyingViews.contains(view)) {
                  // Don’t wait for MSG_DIE to make it’s way through root’s queue.
                  mRoots.get(index).doDie();
             } else {
                  throw new IllegalStateException(“View ” + view + ” has already been added to the window manager.”);
             }
        }
    }
}

三、异常解决方法

既然是时序上的问题导致布局重复添加异常,解决方法很简单,不进行布局复用,每次显示Toast之前都用新的Inflate出来的布局就行。

并不是每个Android平台或车机上都会出现这个问题,依赖出现异常的车机或系统做下特别的适配处理。

四、其他参考资料:

Toast源码深度分析

扩展阅读:

Android OOM问题分析

Android Context 源码解析

转载请注明出处:陈文管的博客 – Toast 自定义布局重复添加异常分析

扫码或搜索:文呓

博客公众号

微信公众号 扫一扫关注

文章目录

  • Toast Exception : java.lang.IllegalStateException: View has already been added to the window manager.
    • 一、Toast.java类源码分析
    • 二、WindowManagerGlobal.java类源码分析
    • 三、异常解决方法
    • 四、其他参考资料:
博客公众号

闽ICP备18001825号-1 · Copyright © 2025 · Powered by chenwenguan.com