首页 > 其他分享 >Android 11 --关于Toast的异常

Android 11 --关于Toast的异常

时间:2023-11-13 19:13:29浏览次数:34  
标签:11 Toast 26 java -- 09 window 9306

WMS 服务关于Toast异常

Window和View的关系:
Window 是View的载体。每个view树都可以看成一个window。
view树中的每个view显示次序是固定,activity里面设置一个布局xml文件,最顶层的布局就是view树的根节点。
一个自定义布局的Dialog,Dialog的顶层布局就不属于activity的View树,这是2个View树,所以是2个Window。

Window 的一些重要属性:
Window的 type 属性决定了window的显示次序,大致分为1-1000 为低层,1001-2000为中层,2000以上为高层。

应用程序窗口:应用程序窗口一般位于最底层,z-order在1-99
子窗口:子窗口一般是显示在应用窗口之上,z-order在1000-1999
系统级窗口:系统级窗口一般位于最顶层,不会被其他的window遮住,如Toast,z-order在2000-2999

z-order越大,window显示越高,高度高的window会覆盖高度低的window,跟搭积木一样。

Window的flag属性 flag标志控制window的显示,Window的softInputMode属性,当软键盘弹出是,对当前window的控制。

Android 11 分屏(不同的displayId) 显示 Toast造成的SystemUI的ANR

bug报错日志:

11-09 11:26:15.814  9306  9306 W ToastUI : Attempt to hide non-current toast from package com.acloud.stub.localmusic
11-09 11:26:15.823  1392  5481 W WindowManager: Attempted to add window to a display for which the application does not have access: 0.  Aborting.
11-09 11:26:15.824  9306  9306 D AndroidRuntime: Shutting down VM
11-09 11:26:15.825  9306  9306 E AndroidRuntime: FATAL EXCEPTION: main
11-09 11:26:15.825  9306  9306 E AndroidRuntime: Process: com.android.systemui, PID: 9306
11-09 11:26:15.825  9306  9306 E AndroidRuntime: android.view.WindowManager$InvalidDisplayException: Unable to add window android.view.ViewRootImpl$W@6061d4d -- the specified display can not be found
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at android.view.ViewRootImpl.setView(ViewRootImpl.java:1101)
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:409)
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:110)
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at android.widget.ToastPresenter.show(ToastPresenter.java:210)
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at com.android.systemui.toast.ToastUI.showToast(ToastUI.java:96)
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at com.android.systemui.statusbar.CommandQueue$H.handleMessage(CommandQueue.java:1285)
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:106)
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:223)
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:7705)
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
11-09 11:26:15.825  9306  9306 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:952)

根据日志跟踪代码进行分析:
当时看到这个报错日志也是有点看不明白,不明白为什么Toast的显示,和system UI有什么关系。
但是报错全是标记到这里了,那就跟踪代码!

com.android.systemui.toast.ToastUI

    @Override
    @MainThread
    public void showToast(int uid, String packageName, IBinder token, CharSequence text,
            IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) {
        .........
        mPresenter = new ToastPresenter(context, mAccessibilityManager, mNotificationManager,
                packageName);
        mPresenter.show(view, token, windowToken, duration, mGravity, 0, mY, 0, 0, mCallback);
    }

    @Override
    @MainThread
    public void hideToast(String packageName, IBinder token) {
        if (mPresenter == null || !Objects.equals(mPresenter.getPackageName(), packageName)
                || !Objects.equals(mPresenter.getToken(), token)) {
            Log.w(TAG, "Attempt to hide non-current toast from package " + packageName);
            return;
        }
        hideCurrentToast();
    }


//systemUI中的toastUI 又重新new了一个ToastPresenter来用于toast的显示

ToastPresenter.java
public void show(View view, IBinder token, IBinder windowToken, int duration, int gravity,
            int xOffset, int yOffset, float horizontalMargin, float verticalMargin,
            @Nullable ITransientNotificationCallback callback) {
        ............
        if (mView.getParent() != null) {
            mWindowManager.removeView(mView);
        }
        try {
            mWindowManager.addView(mView, mParams);
        } catch (WindowManager.BadTokenException e) {
            // Since the notification manager service cancels the token right after it notifies us
            // to cancel the toast there is an inherent race and we may attempt to add a window
            // after the token has been invalidated. Let us hedge against that.
            Log.w(TAG, "Error while attempting to show toast from " + mPackageName, e);
            return;
        }
        ................
    }

//ToastPresenter 是通过 mWindowManager.addView(mView, mParams); 将toast 显示,这个具体实现实在WindowManagerService.java

public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
            int requestUserId) {
        ................
        synchronized (mGlobalLock) {
            if (!mDisplayReady) {
                throw new IllegalStateException("Display has not been initialialized");
            }

            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);

            if (displayContent == null) {
                ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "
                        + "not exist: %d. Aborting.", displayId);
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            //关键代码
            if (!displayContent.hasAccess(session.mUid)) {
                ProtoLog.w(WM_ERROR,
                        "Attempted to add window to a display for which the application "
                                + "does not have access: %d.  Aborting.", displayId);
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            //报错日志发源地....

            if (mWindowMap.containsKey(client.asBinder())) {
                ProtoLog.w(WM_ERROR, "Window %s is already added", client);
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }
            .................
    }


跳转到DisplayContent.java
    /**
     * Returns true if the specified UID has access to this display.
     * 如果指定的UID可以访问此显示,返回true,而bug中的toast不是默认的display显示,
     * 而是在另外的display,所以返回false, !false == true,就报错 return WindowManagerGlobal.ADD_INVALID_DISPLAY
     */
    boolean hasAccess(int uid) {
        return mDisplay.hasAccess(uid);
    }

最后代码执行到ViewRootImpl.java中的setView(xxx)
 /**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
            .......
            int res; /* = WindowManagerImpl.ADD_OKAY; */

            if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
                if (res < WindowManagerGlobal.ADD_OKAY) {
                    mAttachInfo.mRootView = null;
                    mAdded = false;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    switch (res) {
                        ..................
                        case WindowManagerGlobal.ADD_INVALID_DISPLAY:
                            throw new WindowManager.InvalidDisplayException("Unable to add window "
                                    + mWindow + " -- the specified display can not be found");
                        ..................
                    }
                    throw new RuntimeException(
                            "Unable to add window -- unknown error code " + res);
                }
            ..........

    }


    

看完整个代码流程,有两个方法可以尝试:
方法一:

在ToastPresenter.java 中的show()方法去再次try catch WindowManager.InvalidDisplayException 整个异常,
这样systemUI不会崩溃,但是分屏中的应用的toast不显示
    try {
            ...............
        } catch (WindowManager.BadTokenException e) {
            ............
            Log.w(TAG, "Error while attempting to show toast from " + mPackageName, e);
            return;
        }catch (WindowManager.InvalidDisplayException e){
            Log.w(TAG, "InvalidDisplayException->Error while attempting to show toast from" + mPackageName, e);
            return;
        }

方法二:

DisplayContent.java 中的hasAccess()方法,多添加一个判断,让它在mDisplayId不同的时候返回true

boolean hasAccess(int uid) {
        //add
            if(!isDefaultDisplay){
                return diffDisplay();
            }
         //end
        return mDisplay.hasAccess(uid);
    }
    
private boolean diffDisplay() {
        if (mDisplayId != DEFAULT_DISPLAY) {
            return true;
        }
        return false;
    }

参考链接
WindowState
WindowManagerService
分析方法

标签:11,Toast,26,java,--,09,window,9306
From: https://www.cnblogs.com/kato-T/p/17829876.html

相关文章

  • 分级测试
    这次测试主要是完成一个简单的选课系统,涉及到四个表的增删改查,主要是学生的信息表,老师的信息表,课程信息表,还有一个选课信息表。在刚开始拿到这道题的时候,我并没有看到最后一个表,所以我一开始就是使用前三个表在做这个系统。虽然基本功能都能实现,但是在最后选课的流程上,我并不能使......
  • oracle数据库 时间 TIMESTAMP(6)这是什么类型啊 怎么也插不进数据 ,是时间戳类型,参数6
    oracle数据库时间TIMESTAMP(6)这是什么类型啊怎么也插不进数据是时间戳类型,参数6指的是表示秒的数字的小数点右边可以存储6位数字是时间戳类型,参数6指的是表示秒的数字的小数点右边可以存储6位数字,最多9位。解决方法如下:1、时间戳的概念:它是一种时间表示方式,定义为从格林威......
  • mysql函数(二)之常见字符串函数
    1、CONCAT(str1,str2,…)函数CONCAT()将多个字符串连接成一个字符串,如果该函数中的任何参数为NULL,返回结果为NULL,使用示例如下: 2、SUBSTRING(str,pos,len)函数SUBSTRING()从字符串中提取子字符串,起始位置为pos,长度为len,使用示例如下: 3、REPLACE(str,from_str,to_str......
  • linux xfce 在文件管理器里点击运行shell脚本文件
    1.打开SettingsEditor2.点击左边的thunar3.点击右边的添加,在属性中输入/misc-exec-shell-scripts-by-default在类型中选择布尔类型在值中选择真保存4.给shell脚本文件添加运行权限命令行chmod+xyour_filename或者gui界面在右键脚本文件,点击属性,在权限里允许......
  • timestamp(6)详解 在MySQL中,timestamp是一种时间戳类型。timestamp(6)是timestamp类型
    timestamp(6)详解在MySQL中,timestamp是一种时间戳类型。timestamp(6)是timestamp类型的一个子类型,表示精确到秒后6位小数的时间戳。它占用8个字节存储空间一、什么是timestamp(6)在MySQL中,timestamp是一种时间戳类型。timestamp(6)是timestamp类型的一个子类型,表示精确到秒后6......
  • centos7 关闭swap分区
    目录1.查看swap分区信息2.关闭swap分区 1.查看swap分区信息通过free-h命令查看[root@k8s-master-02~]#free-htotalusedfreesharedbuff/cacheavailableMem:1.8G131M1.4G8.9M212M1.5GSwap:2.0G0B2.0G 2.关闭swap分区2.1临时关闭( swapoff-a )[r......
  • electron的axios用法
    当在Electron中使用axios库时,你需要在渲染进程的代码中使用它来发起HTTP请求。以下是一个完整的示例,展示了如何在Electron中导入并使用axios库: index.html: ```html<!DOCTYPEhtml><html><head> <title>ElectronAxiosExample</title></head><body> <......
  • Android C++ 打印(调用)堆栈
    C++Android12编译依赖库:libutilscallstack头文件:#include<utils/CallStack.h>代码:CallStackstack;stack.update();stack.log("TAG");打印callingpid#include<binder/IPCThreadState.h>IPCThreadState::self()->getCallingPid(); //占位用......
  • 如何减少项目的白屏时间,优化页面的卡顿
    1.如何减少项目的白屏时间,优化页面的卡顿问题背景在某些情况下,我们希望等待当前帧渲染完成后执行某个函数。这样可以确保在进行下一次操作之前,浏览器已经完成了渲染工作,以提供更流畅的用户体验。例如,当我们需要处理大量数据并进行渲染时,我们可以使用requestAnimationFrame在下......
  • SSH高级应用之远程端口转发并实现网关功能
    端口转发实现逻辑我们直接来看这图好,那么现在呢?你假设你是出差在外的用户,比方说有一个笔记本用户出差了。明白了好。现在是这样的,这个服务器呢?是在你企业内部的。那么,企业内部要想。让互联网的用户想访问,通常来讲是访问不了的,因为什么有防火墙?他会阻止你访问企业内部的服务器,所以......