开源库地址:https://github.com/the-pig-of-jungle/smart-show

Toast工作原理依赖于通知,关闭应用通知权限后,Toast无法显示。在发布SmartShow1.0.0版的时候,我注意到了这个问题,立即用自己的手机(魅族pro 6 plus)对淘宝、优酷等知名app进行测试,发现关闭通知权限后,它们的“再按一次退出程序”的Toast无法显示。因为Toast的工作机制如此,我并没有把它当做一个问题看待。但是在前两篇文章发布时,关闭通知权限依然能够显示Toast的呼声之高,让我不得不着手解决这个问题。

有不少Toast开源库解决了该问题,采用独立悬浮窗,也就是另起炉灶,废弃了原生Toast,自行实现一套弹窗提示。我们知道,不同手机品牌设备的Toast外观及弹出动画不尽相同,直接强行统一成一种风格,并不一定符合开发者的意愿,我们应该把是否定制Toast的权利交给开发者。另外,独立悬浮窗本身也需要申请权限,用户会关闭通知权限,难道就没有可能关闭悬浮窗权限么?

完美的解决方案是什么?当通知权限关闭时,我们需要制造一个VirtualToast来代替,对开发者来说,在使用上和原生Toast毫无差别。对app用户来说,在体验上(弹窗的外观、动画)与原生一致。

乍一看,这个问题好像很棘手,换一个角度思考问题,则柳暗花明,哈哈。

先进行技术选型,基于上面提到的原因,我们采用Dialog而不是悬浮窗。

先分析一下,关闭通知权限后,Toast的工作流程卡在了哪里。Toast的工作流程是一个基于Binder的IPC(进程通信)过程,应用程序作为客户端仅仅发起Toast请求和被动接受回调。系统服务负责管理请求队列、Token等,并通过Toast的内部类Tn来实现与应用程序的交互,即通过回调Tn的show/hide方法来显示/隐藏Toast窗口。关闭了通知权限,导致无法与系统服务“通信”,最终无法添加Toast窗口。

不过当调用Toast的show方法时,此时添加窗口所需的View及各种窗口参数已全部准备就绪。

public class Toast{public void show() {...//创建tn,笔者注释TN tn = mTN;//窗口View,笔者注释tn.mNextView = mNextView;...}}
private static class TN extends ITransientNotification.Stub {...TN(String packageName, @Nullable Looper looper) {...final WindowManager.LayoutParams params = mParams;//窗口宽度,笔者注释params.height = WindowManager.LayoutParams.WRAP_CONTENT;//窗口高度,笔者注释params.width = WindowManager.LayoutParams.WRAP_CONTENT;params.format = PixelFormat.TRANSLUCENT;//窗口动画,笔者注释params.windowAnimations = com.android.internal.R.style.Animation_Toast;params.type = WindowManager.LayoutParams.TYPE_TOAST;params.setTitle("Toast");//不可获取焦点,不接收触碰事件,笔者注释params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;...}...
}

如此,当通知权限关闭,我们只需在调用Toast的show方法前用Dialog“截胡”,将其View作为Dialog的ContentView。

    public void showToast() {applySetting();//如果具有通知权限,正常显示Toastif (Utils.isNotificationPermitted()) {mToast.show();} else {//无通知权限,用Dialog代替VirtualToastManager.get().show(getToastType(), mToast, mWindowParams);}}

将Toast窗口的参数设置给Dialog

public void show(int toastType, Toast toast, WindowManager.LayoutParams windowParams) {//获取栈顶activityActivity activity = ActivityStack.getTop();//若activity生命周期不符合条件,则什么都不做if (!Utils.isUpdateActivityUIPermitted(activity)) {EasyLogger.d("activity is can not show virtual toast dialog ,so do nothing but return.");return;}...//取出Dialog窗口布局参数,原样复制Toast窗口的布局参数WindowManager.LayoutParams lp = virtualToastDialog.getWindow().getAttributes();//窗口宽度lp.width = windowParams.width;//窗口高度lp.height = windowParams.height;//窗口动画lp.windowAnimations = windowParams.windowAnimations;//窗口gravitylp.gravity = toast.getGravity();//窗口x,y坐标lp.x = toast.getXOffset();lp.y = toast.getYOffset();ViewGroup content = virtualToastDialog.findViewById(android.R.id.content);if (toast.getView().getParent() != content) {if (toast.getView().getParent() != null) {ViewGroup parent = (ViewGroup) toast.getView().getParent();parent.removeView(toast.getView());}content.removeAllViews();virtualToastDialog.setContentView(toast.getView());}try {virtualToastDialog.show();} catch (WindowManager.BadTokenException e) {EasyLogger.e("bad token has happened when show virtual toast!");mHostActivity = null;}...
}

去除Dialog显示时周围变暗的特性

virtualToastDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

Dialog不响应back键

      virtualToastDialog.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

如此这般VirtualToastDialog的外观及动画与目标设备的原生Toast完全一致了。就剩下间隔一段时间自动消失的控制了。

定义一个Handler,延时特定时间后,执行隐藏VirtualToastDialog的代码。

        Runnable dismissRunnable = toastType == BaseToastManager.PLAIN_TOAST ? mDismissPlainToastRunnable: mDismissTypeToastRunnable;mDismissHandler.removeCallbacks(dismissRunnable);mDismissHandler.postDelayed(dismissRunnable, toast.getDuration() == Toast.LENGTH_SHORT ?DURATION_SHORT : DURATION_LONG);

这里,始终复用同一个runnable任务即可,并且在发起一个新的延时隐藏时,我们要清除所有已存在的延时隐藏任务。假如,在上一个隐藏任务还差200毫秒执行的时候,主动调用了隐藏方法使Toast消失,那么再显示一个‘’新的‘’Toast时,200毫秒后就被自动隐藏了。所以mDismissHandler.removeCallbacks(dismissRunnable)起到归位的作用。

Toast系列(五):还在被关闭通知无法显示Toast所困扰?解决方案来了相关推荐

  1. 小小的Toast蕴含大道理(解决关闭通知时原生Toast不显示问题)

    目录 一.Toast成员变量 二. Toast显示流程 1. Toast makeText(@NonNull Context context, @Nullable Looper looper,@Non ...

  2. 如何在android程序使用toast,Android在退出应用程序时以及正在显示toast时...

    诀窍是跟踪显示的最后一个Toast,并取消那个. 我所做的是创建一个Toast包装器,它包含对显示的最后一个Toast的静态引用. 当我需要显示一个新的时,我首先取消静态引用,然后再显示新引用(并将其 ...

  3. android toast有焦点,android – 如何在显示Toast后进行edittext自动对焦?

    我有一个EditText,读取13位数的条形码.我想要做的是保持屏幕上显示的虚拟键盘和EditText始终具有焦点.下面的代码让我编写条形码并在按下回车键时搜索产品,并且效果很好.但是如果我输入的条形 ...

  4. 在Metro App中显示Toast notification

    Toast notification是向用户显示一些有关App的即时消息.具体Toast notification是什么可以参考  http://msdn.microsoft.com/en-us/li ...

  5. android toast通知关闭,屏蔽系统通知,Toast无法显示的解决方案 v2.0.0

    为了大家方便沟通和使用,建立了一个QQ群供大家交流,欢迎大家的加入 群名称:EToast交流群 群 号:547279762 更新日志: v2.2.1(2019年5月28日10:24:41) 在2.2. ...

  6. C#桌面办公应用-工资管理系统系列五

    C#桌面办公应用-工资管理系统系列五 接前文系列四,本文将讲解实现工资管理系统的代码的层次结构.主要采用的是MVCS模式的代码层次结构,视图层(V):是各种winform窗体:控制层(C):主要是wi ...

  7. 数学之美系列五 -- 简单之美:布尔代数和搜索引擎的索引

    数学之美系列五 -- 简单之美:布尔代数和搜索引擎的索引 [建立一个搜索引擎大致需要做这样几件事:自动下载尽可能多的网页:建立快速有效的索引:根据相关性对网页进行公平准确的排序.我们在介绍 Googl ...

  8. 智能路由器-OpenWRT 系列五 (NAS-SMB家庭共享)

    NAS是我觉得家里必不可少的一台设备,NAS 全称 Network Attached Storage,即网络附加存储服务器,它通过自身的操作系统和 SMB/NFS/CIFS/FTP 等多种通信协议来给 ...

  9. 深入剖析Redis系列(五) - Redis数据结构之字符串

    前言 字符串类型 是 Redis 最基础的数据结构.字符串类型 的值实际可以是 字符串(简单 和 复杂 的字符串,例如 JSON.XML).数字(整数.浮点数),甚至是 二进制(图片.音频.视频),但 ...

最新文章

  1. Redis 笔记(06)— set 类型(向集合添加元素、获取集合元素个数、判断集合中是否包含某个元素、删除给定元素、返回集合中所有元素、计算集合的交集、并集、差集)
  2. Prolog学习:数独和八皇后问题
  3. python基础一入门必备知识-Python从入门到精通要掌握哪些基础知识?
  4. 通过点击热区,有个href 跳到servlet,怎么把页面上的值传到servlet里面?
  5. CSS 强制换行和禁止换行强制换行 和禁止换行样式
  6. .net session 有效时间_Python中requests模拟登录的三种方式(携带cookie/session进行请求网站)...
  7. LncRNADisease database数据库使用方法
  8. C# html的Table导出到Excel中
  9. 前端学习(1397):项目包含的知识点cookie和session2
  10. js 获得radio的值
  11. Linux root密码丢失后的解决
  12. ffmpeg转换格式
  13. 技术不牛如何才拿到国内IT巨头的Offer(转)
  14. 软件测试——测试基础
  15. 深度学习之----各种学习策略
  16. 攻克python3-面向对象
  17. 计算机基础知识是几年级,[初中一年级]计算机基础知识ppt模版课件.ppt
  18. 个人电脑bt文件服务器,简单实用 教你轻松架设个人BT服务器
  19. hba卡在服务器什么位置,设置服务器通过HBA卡启动目前服务器主流使用的是Qlogic和Emulex.doc...
  20. 小米平板2怎么显示电脑连接服务器,小米平板2有什么接口?小米平板2有HDMI接口吗?...

热门文章

  1. 公司注册资本多少与交税有关系吗
  2. NSIS制作软件安装包
  3. 2020-02-25
  4. 绝了!小说建站项目完整开源
  5. 【新闻】Googel 发布 sugguest 服务
  6. 使用Namebench查找更快的DNS服务器
  7. 解决 Xshell6强制升级
  8. iphone计算机适配,适配iPhone用户!戴尔将支持iPhone投屏电脑
  9. java xstream_XStream 用法汇总
  10. 5个开源且简单实用的Code Review工具