From a77e1ad35d612a08cfb0484ef16bf5abdbe3ff88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Android=20=E8=BD=AE=E5=AD=90=E5=93=A5?= Date: Tue, 25 Jan 2022 20:26:33 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20Toast=20=E5=9C=A8=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E6=97=B6=E6=8C=89=E4=B8=8B=E8=BF=94=E5=9B=9E=E9=94=AE?= =?UTF-8?q?=E5=87=BA=E7=8E=B0=20WindowLeaked=20=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 ++++---- app/build.gradle | 8 +++++--- library/build.gradle | 4 ++-- .../src/main/java/com/hjq/toast/WindowLifecycle.java | 10 +++------- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 838d147..0b9a4b6 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ * 博客地址:[只需体验三分钟,你就会跟我一样,爱上这款 Toast](https://www.jianshu.com/p/9b174ee2c571) -* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/ToastUtils/releases/download/10.2/ToastUtils.apk) +* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/ToastUtils/releases/download/10.3/ToastUtils.apk) ![](picture/demo_code.png) @@ -47,7 +47,7 @@ android { dependencies { // 吐司框架:https://github.com/getActivity/ToastUtils - implementation 'com.github.getActivity:ToastUtils:10.2' + implementation 'com.github.getActivity:ToastUtils:10.3' } ``` @@ -140,7 +140,7 @@ ToastUtils.init(this, new ToastStrategy() { | 功能或细节 | [ToastUtils](https://github.com/getActivity/ToastUtils) | [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode) | [Toasty](https://github.com/GrenderG/Toasty) | | :----: | :------: | :-----: | :-----: | -| 对应版本 | 10.2 | 1.30.6 | 1.5.0 | +| 对应版本 | 10.3 | 1.30.6 | 1.5.0 | | issues 数 | [![](https://img.shields.io/github/issues/getActivity/ToastUtils.svg)](https://github.com/getActivity/ToastUtils/issues) | [![](https://img.shields.io/github/issues/Blankj/AndroidUtilCode.svg)](https://github.com/Blankj/AndroidUtilCode/issues) | [![](https://img.shields.io/github/issues/GrenderG/Toasty.svg)](https://github.com/GrenderG/Toasty/issues) | | **aar 包大小** | 28 KB | 500 KB | 50 KB | | **调用代码定位** | ✅ | ❌ | ❌ | @@ -168,7 +168,7 @@ ToastUtils.init(this, new ToastStrategy() { * 这个问题的出现是因为原生 Toast 的显示要通过 NMS(NotificationManagerService) 才会 addView 到 Window 上面,而在 NMS 中有一个 `static final boolean ENABLE_BLOCKED_TOASTS = true` 的字段,当这个常量值为 true 时,会触发 NMS 对应用通知栏权限的检查,如果没有通知栏权限,那么这个 Toast 将会被 NMS 所拦截,并输出 `Suppressing toast from package` 日志信息,而小米手机没有这个问题是因为它是将 `ENABLE_BLOCKED_TOASTS` 字段值修改成 `false`,所以就不会触发对通知栏权限的检查,另外我为什么会知道有这个事情?因为我曾经和一名 MIUI 工程师一起确认过这个事情。 -* 框架处理这个问题的方式有两种,先判断当前应用是否处于前台状态,如果是则使用自定义的 WindowManager 代替 Toast 来显示,如果当前应用处于后台状态,则会通过 Hook Toast 中的 INotificationManager 接口,将 enqueueToast 方法传递的包名参数修改成 `android` 来欺骗 NotificationManagerService,因为 NotificationManagerService 已经将 `android` 包名的应用纳入白名单,会自动放行,需要注意的是,这种方式在 Android 10 上面已经失效了,已经被系统纳入反射黑名单,但是好消息是,通过查看和对比 NotificationManagerService 源码发现,这个问题(关闭通知栏权限后无法在前台弹 Toast 的问题)已经在 Android 10.0 的版本上面被修复了,所以框架只在 Android 9.0 及以下版本并且在关闭了通知栏权限的情况下才去 Hook INotificationManager,另外我还找到了官方关于这块的代码提交记录:[Always allow toasts from foreground apps](https://cs.android.com/android/_/android/platform/frameworks/base/+/58b2453ed69197d765c7254241d9966ee49a3efb),大家可以感兴趣可以看看,还有一个问题,如果你想在 Android 10 之后仍然能在后台显示 Toast,请保证应用的通知栏权限处于开启的状态。 +* 框架处理这个问题的方式有两种,先判断当前应用是否处于前台状态,如果是则使用自定义的 WindowManager 代替 Toast 来显示,如果当前应用处于后台状态,则会通过 Hook Toast 中的 INotificationManager 接口,将 enqueueToast 方法传递的包名参数修改成 `android` 来欺骗 NotificationManagerService,因为 NotificationManagerService 已经将 `android` 包名的应用纳入白名单,会自动放行,需要注意的是,这种方式在 Android 10 上面已经失效了,已经被系统纳入反射黑名单,但是好消息是,通过查看和对比 NotificationManagerService 源码发现,这个问题(关闭通知栏权限后无法在前台弹 Toast 的问题)已经在 Android 10.0 的版本上面被修复了,所以框架只在 Android 9.0 及以下版本并且在关闭了通知栏权限的情况下才去 Hook INotificationManager,另外我还找到了官方关于这块的代码提交记录:[Always allow toasts from foreground apps](https://cs.android.com/android/_/android/platform/frameworks/base/+/58b2453ed69197d765c7254241d9966ee49a3efb),大家可以感兴趣可以看看,还有一个问题,如果你想在 Android 10 之后仍然能在后台显示 Toast,请保证应用的通知栏权限或者悬浮窗权限处于开启的状态。 #### Android 11 不能在后台显示 Toast 的问题介绍 diff --git a/app/build.gradle b/app/build.gradle index 171cb1b..a57d691 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "com.hjq.toast.demo" minSdkVersion 16 targetSdkVersion 31 - versionCode 1020 - versionName "10.2" + versionCode 1030 + versionName "10.3" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } @@ -51,7 +51,9 @@ android { } dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') + // 依赖 libs 目录下所有的 jar 和 aar 包 + implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') + implementation project(':library') // AndroidX 库:https://github.com/androidx/androidx diff --git a/library/build.gradle b/library/build.gradle index 6af2fdf..7ad9dd1 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -5,8 +5,8 @@ android { defaultConfig { minSdkVersion 14 - versionCode 1020 - versionName "10.2" + versionCode 1030 + versionName "10.3" } // 使用 JDK 1.8 diff --git a/library/src/main/java/com/hjq/toast/WindowLifecycle.java b/library/src/main/java/com/hjq/toast/WindowLifecycle.java index 02874ea..caed32a 100644 --- a/library/src/main/java/com/hjq/toast/WindowLifecycle.java +++ b/library/src/main/java/com/hjq/toast/WindowLifecycle.java @@ -38,21 +38,17 @@ final class WindowLifecycle implements Application.ActivityLifecycleCallbacks { public WindowManager getWindowManager() { if (mActivity != null) { - if (mActivity.isFinishing()) { - return null; - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && mActivity.isDestroyed()) { return null; } - return mActivity.getWindowManager(); } else if (mApplication != null) { + return (WindowManager) mApplication.getSystemService(Context.WINDOW_SERVICE); - } else { - return null; } + + return null; } /**