当前位置:首页 > 房产信息

toast Toast 不显示了?

链接:https://www.jianshu.com/p/1d64a5ccbc7c

作者注:没错,分析系统源代码,这个问题是原生安卓的Bug,也就是说只发现小米手机经过了特殊处理,而华为三星等手机并没有改变Toast API

1、概述

吐司就不出来了。解决方案:ToastUtils

https://github.com/getActivity/ToastUtils

接下来,让我们开始分析这个问题是如何出现的,解决它的过程,以及解决方案

先来看看大厂APP的吐司

问题

1.连吐司都不会玩的手机是什么梗?

2.是少数车型还是多数车型?

3.为什么通知栏关闭后不能播放?

4.为什么有的模特会玩,有的不会?

解释

1.自从我的ToastUtils框架发布以来,最常被问到的问题之一,您是否可以关闭toast的通知栏?所以我拿着我的小米8和红米Note5测试了一下。我发现没有这个问题,就统一回复了。这是兼容性问题,少数型号可能有问题。为了保证框架的稳定性,不要给出兼容性

2.于是有人陆续给我反馈了这个问题。反馈的人都是华为机型的问题,我就开始关注了。一个同事刚刚用了华为P9,我借了他一会手机。借一下午也没关系。估计同事的心都崩溃了,因为这个问题已经100%重现了,通知栏真的关了以后敬酒也打不出来

3.于是我搜索了Toast的源代码。底层的toast是由WindowManager实现的,但是这和通知栏的权限有什么关系呢?

就算和NotificationManager有关系,和WindowManager有什么关系?检查系统源代码后发现,toast是用WindowManager创建的,但是toast是用INotificationManager显示的,看类名肯定和NotificationManager有关,这也是通知栏权限关闭后不能显示toast的原因。

4.现在经过测试,大部分小米机型都不会因为通知栏权限关闭而播放原Toast,但华为荣耀、三星等都会关闭通知栏权限,导致原Toast显示。这可能是小米手机对这个吐司的显示做了特殊处理。这个问题会出现在Github上的top Toast框架中,一些大厂商的应用(除了QQ微信和美团)也会有这个问题。还没有人开始完美解决这个问题。

吐司打不出去的后果

吐司是我们日常开发中最常用的类。如果我们的APP在通知栏推送更多消息,用户会屏蔽我们的通知栏权限,但这样会引起联合反应,即应用中所有使用Toast的地方都不会显示,完全变成一个哑应用,比如下面的场景:

账户密码输入错误,吐司弹不出来用户网络支付失败,吐司弹不出来网络请求错误,吐司弹不出来双击退出应用,吐司弹不出来等等情况,只要用到原生 Toast 都显示不出来

其实这是一个系统Bug。谷歌使用与通知栏相关的应用编程接口,以便在其他应用程序上显示应用程序的吐司。但是由于用户屏蔽了通知栏,系统误以为你没有通知栏的权限,导致这个API变得不可用,间接导致系统拦截Toast的show请求。

2.Toast源代码分析

先看吐司的成分

让我们看看吐司里面的API

还有一个内部类,再看看内部API

由此不难推断Toast只是一个外观类,最终的实现还是由其内部类实现的。因为这个内部类太长了,我们把这个内部类的源代码放在这里,简单的过一遍。

privatesticclasstnextendsiltancientnotification。存根{

privatefinitilwindowmanager。layout params mParams = new window manager。layout params();

privatestaticfinaintshow = 0;

privateStaticFinalinted = 1;

privateStaticFinalintCancel = 2;

finalHandler mHandler

查看mView

查看mNextView

intmdduration;

WindowManager mWM

字符串mPackageName

staticfinallownshort _ DURATION _ time out = 4000;

statiFinallonglong _ DURATION _ time out = 7000;

TN(String packageName,@NullableLooper looper) {

finallywindowmanager。LayoutParams params

params.type = WindowManager。LayoutParams . TYPE _ TOAST

mPackageName = packageName

MH handler = new handler(looper,null) {

@覆盖

publicvoidhandleMessage(消息消息){

switch(msg.what) {

案例展示:{

IBinder token =(IBinder)msg . obj;

handleShow(令牌);

打破;

}

caseHIDE: {

handleHide();

mNextView = null

打破;

}

caseCANCEL: {

handleHide();

mNextView = null

尝试{

getService()。cancelToast(包装名称,TN。这个);

} catch(RemoteException e) {

}

打破;

}

}

}

};

}

@覆盖

public void show(IBinder WindowToken){

获取消息(显示,窗口标记)。sendToTarget();

}

@覆盖

publicvoidhide(){

获取消息(隐藏)。sendToTarget();

}

publicvoidcancel(){

获取消息(取消)。sendToTarget();

}

public void handleshow(IBinder WindowToken){

if(mView!= mNextView) {

handleHide();

mView = mNextView

context context = MView . GetContext()。getApplicationContext();

string package name = MView . GetContext()。getopbackagename();

if(context == null) {

context = MView . GetContext();

}

mWM =(window manager)Context . getsystemservice(Context。WINDOW _ SERVICE);

mParams.token = windowToken

mwm . addview(mv view,mParams);

trisendaccessibilityevent();

}

}

publicvoidhandleHide(){

if(mView!= null) {

if(mView.getParent()!= null) {

mwm . removeview immediate(mv view);

}

mView = null

}

}

}

您只需要简单地看一下,就可以理解Toast的底层是用这个内部类实现的。记住,这个内部类叫TN,字段名叫mTN。我们先来看看Toast中cancel方法的源代码。

Cancel最后调用了内部类TN中同名的方法,然后看Toast中show方法的源代码

仔细观察的同学会发现,这个show方法不仅仅是像cancel一样调用TN内部类中同名的方法,还调用了INotificationManager的API。事实上,不难发现这个创新经理是系统的AIDL。如果你不相信,让我们再来看看这个入侵管理器。

我相信学过AIDL的学生会明白,这里不再讲AIDL相关的知识。如果需要了解,请自行百度。

专注于INotificationManager,一个AIDL系统实现的类,不同系统对应AIDL的类是不一样的。这充分说明了为什么小米的机型仍然可以显示通知栏权限,而华为却不能。具体原因请参考源代码

因为APPlication的包名传递给了系统的通知栏,如果关闭了这个包名对应的app的通知栏的权限,吐司自然不会播放出来。

3.那么如何解决这个问题呢

首先想一个问题。吐司显示使用INotificationManager,与通知栏有关,而吐司创建使用WindowManager,与通知栏无关。那么我们可以通过WindowManager创建类似Toast的东西吗?答案是肯定的。

然而,在这个过程中会出现非常困难的问题。接下来我们来解决这些问题

首先,创建窗口管理器需要一个视图参数和窗口管理器。LayoutParams参数。在这里,我们来谈谈创建窗口管理器。布局并直接复制一些吐司代码:

WindowManager。LayoutParams params = newWindowManager。layout params();

params.height = WindowManager。LayoutParams . WRAP _ CONTENT

params.width = WindowManager。LayoutParams . WRAP _ CONTENT

params.format = PixelFormat。半透明的;

//找不到com . Android . internal . r . style . animation _ toast

//params . window animations = com . Android . internal . r . style . animation _ Toast;

params . window动画=-1;

params.type = WindowManager。LayoutParams . TYPE _ TOAST

params . settitle(" Toast ");

params.flags = WindowManager。标志_保持_屏幕_打开

| WindowManager。layout params . FLAG _ NOT _ FOCUSABLE

| WindowManager。layout params . FLAG _ NOT _ TOUCH able;

然后使用窗口管理器调用添加视图显示,然后报告一个错误

Android . view . Window manager $ BadTokenException:无法添加窗口令牌nullisnotvalid你的活动正在进行吗?

原因是我们使用了type,为什么不能添加TYPE_TOAST,因为在关闭通知权限后将显示类型设置为TOAST会报错,所以这里我们标注了这段代码,然后就可以显示了

// params.type = WindowManager。LayoutParams . TYPE _ TOAST

自建WindowManager没有吐司的显示效果

原因是我们复制了Toast的一些代码,其中有些引用了系统R文件中的资源,但是我不能直接用Java代码引用

params . window动画= com.android. internal。r . style . animation _ Toast;

Java代码不能引用这个Style,不代表XML就不行。在此创建一个样式,并继承原生的吐司样式。我们可以在这里定制或者直接使用。为了方便和风格统一,直接在这里使用。

& ltstyle name = " to astanimation " parent = " @ Android:style/Animation。吐司" >;

& lt!-& lt;item name = " Android:WindowEntAnimation " >;@ anim/toast _ enter & lt;/item>。->;

& lt!-& lt;item name = " Android:windowExitAnimation " >;@ anim/toast _ exit & lt;/item>。->;

& lt/style>。

然后再次指定params . window动画来解决问题

params . window animations = r . style . to stanimation;

自建窗口管理器不会消失

首先,WindowManager不能像Toast一样显示后自动消失。如果像Toast一样容易自动消失,显示后定期发送任务关闭,那么问题来了。如何定义显示的时间?系统Toast显示的时间是多少?

首先,我们需要看看Toast提供的两个常量值

我们没有从这张照片中发现任何有价值的东西。让我们继续往下看,看看这些常数被引用的地方

通过查看源代码继续学习

但是通过测试,短吐司的显示时间是2-3秒,而长吐司的显示时间是3-4秒,所以这两个值都不是吐司显示时间的毫秒数,那么如何得到正确的毫秒数呢?这个问题留给大家思考,这里没有答案。

只能用当前活动创建窗口管理器缺陷

我发现一个问题,Activity和Application也是Context的子类。如果可以创建由活动获得的窗口管理器对象,但是如果由应用程序获得的窗口管理器对象报告错误。

Android . view . Window manager $ BadTokenException:无法添加窗口令牌无效的应用程序

错误报告已经明确了不能用Application对象创建WindowManager,也就是说只能用Activity对象创建WindowManager。

那么问题来了。每次播放自建的吐司,都需要当前的Activity对象。这个问题对于常年使用框架的同学来说是致命的。

在这里,以我的ToastUtils框架为例,它表明敬酒是这样被调用的:

ToastUtils.show("我是吐司");

如果想解决关闭通知栏后再次弹出toast的问题,需要改为

吐司面包。show(主要活动。这,“我是烤面包”);

先说这个问题的影响。我是框架的作者。对我来说,我只需要在ToastUtils中的show方法中再添加一个Activity参数。但是对于使用框架的人来说,在更新框架之后,整个项目中所有使用这个ToastUtils.show()方法的用户都会报错,需要多传一个Activity参数。我相信他们的心都快崩溃了。有没有好的办法解决这个问题?

application . registereactivitylifecycleallbacks(activitylifecycleallback);

这个API是Android 4.0以后才有的,现在大部分设备已经在Android 5.0及以上,所以这个API还是很有前景的。我们来看看ActivityLifecycleCallbacks的界面:

publicationinterfacactivitylifecycleallbacks {

创建的无效活动(活动活动,捆绑保存实例属性);

活动开始(活动活动);

无效活动恢复(活动活动);

空隙活动暂停(活动活动);

无效活动停止(活动活动);

无效活动保存实例状态(活动活动,捆绑未完成);

无效活动已销毁(活动活动);

}

看到这里,相信你已经知道真相了。此方法用于监控应用程序活动中的生命周期方法。

然后我们就可以通过这个API得到与用户交互的Activity对象,从而完成当前Activity对象对WindowManager的创建。

使用窗口管理器实现Toast有一些限制

当然,用WindowManager创建的视图必然会受到Activity的限制,因为它只能在这个Activity上显示,而不能在其他界面上显示,而系统的原Toast可以出现在其他界面上。有什么解决办法吗?

当没有浮动窗口权限时,窗口管理器只能显示附加到调用的活动。授予浮动窗口权限后,可以通过更改类型参数来更改窗口管理器的显示范围,以便窗口管理器可以显示在其他界面上,这样Toast就不会因为活动不可见而变得不可见。

//确定是Android 6.0以上,有浮动窗口权限

如果(构建。VERSION.SDK_INT >=构建。版本_代码。并购。& ampsettings . candrawoverlays(mToast . GetView()。getContext())) {

//解决了WindowManager创建的Toast只能在当前Activity中显示的问题

如果(构建。VERSION.SDK_INT >=构建。版本_代码。O) {

params.type = WindowManager。layout params . TYPE _ APPLICATION _ OVERLAY;

} else{

params.type = WindowManager。LayoutParams . TYPE _ PHONE

}

}

如何在本机Toast和自建窗口管理器之间进行选择

所以我们比较了一组数据:

类型显示范围需要参数兼容性效率通知栏权限悬浮窗权限原生 Toast所有界面Context子类高一般需要不需要WindowManager当前ActivityActivity子类一般高不需要不需要

经过比较,原生Toast的优势还是大于WindowManager的。所以如果建议在通知栏有权限的前提下使用原生Toast,我们可以通过判断通知栏的权限是否关闭来判断是显示原生Toast还是自建WindowManager。方法代码如下:

/**

*检查通知栏权限是否打开

*/

publicationstationboolean is notification enabled(上下文上下文){

如果(构建。VERSION.SDK_INT >=构建。版本_代码。N) {

返回((通知管理器)上下文。NOTIFY _ ServiCe))。areNotificationsEnabled();

} elseif(Build。VERSION.SDK_INT >=构建。版本_代码。KITKAT) {

appOps manager appOps =(appOps manager)Context . GetSystemServiCe(上下文。APP _ OPS _ SERVICE);

application info appInfo = context . GetAPPlicationInfo();

string pkg = context . GetAPPlicationContext()。getPackageName();

intuid = appInfo.uid

尝试{

类别<。?>。appops class = class . for name(appops manager . class . GetName());

方法checkopnorthrowmethod = appopclass . getmethod(" checkopnorthrow "),整数。类型,整数。TYPE,string . class);

Field反对派NOTIFICATION value = appops class . getdeclaredfield(" OP _ POST _ NOTIFICATION ");

intvalue=(整数)反对派通知值。get(Integer . class);

return(Integer)CheckOpNorthRowMethod . invoke(appOps,value,uid,pkg)= 0;

} catch(NoSuchMethodException | NoSuchFieldException | InvocationTargetException | IllegalaccessException | RuntimeException | ClassNotFoundException被忽略){

returntrue

}

} else{

returntrue

}

}

详细的源地址请戳这里

https://github.com/getActivity/ToastUtils

作者技术讨论Q组:78797078

●编号390,直接输入该条的编号

1.《toast Toast 不显示了?》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《toast Toast 不显示了?》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/fangchan/1610969.html

上一篇

82年中泰斗法 中泰灵异大师斗法事件,风水灵异是真的存在吗?

下一篇

衣服图片女装 男人穿女装旗袍图,男人穿女装上街实拍

泰州市教育局 泰州市教育局发通知了!

为做好开学复课准备,台州市中小学以外的师生回泰计划如下 一个 人员分类 目前台州以外的中小学师生分为四类: 第一类:仍在湖北的师生; 第二类:目前湖北省的外部需求主要集中在疫区的师生; 第三类:江苏省以外非重点地区的师生; 第四类:仍在江苏省和台州市以外的师生。 二 医学观察原则 第一班师...

泰州市教育网 泰州市教育局发通知了!

为做好开学复课准备,台州市中小学以外的师生回泰计划如下 一个 人员分类 目前台州以外的中小学师生分为四类: 第一类:仍在湖北的师生; 第二类:目前湖北省的外部需求主要集中在疫区的师生; 第三类:江苏省以外非重点地区的师生; 第四类:仍在江苏省和台州市以外的师生。 二 医学观察原则 第一班师...

泰州教育局 泰州市教育局发通知了!

为做好开学复课准备,台州市中小学以外的师生回泰计划如下 一个 人员分类 目前台州以外的中小学师生分为四类: 第一类:仍在湖北的师生; 第二类:目前湖北省的外部需求主要集中在疫区的师生; 第三类:江苏省以外非重点地区的师生; 第四类:仍在江苏省和台州市以外的师生。 二 医学观察原则 第一班师...

中国建设监理协会官方网站 关于印发《中国建设监理协会会员自律公约》等三份文件的通知

各省、自治区、直辖市建设监理协会,相关行业建设监理专业委员会,中国工程咨询协会各分会、单位会员: 为适应“配送服务”改革和建筑市场发展的要求,加强监理行业自律管理,完善会员信用管理体系建设,促进监理行业健康发展,协会第六届三次理事会审议通过了《中国工程咨询协会会员自律公约》、《中国工程咨询...

催款通知书 2018欠债的,你不再是大爷了!一封催款通知书送给所有正在清账的你!

  • 催款通知书 2018欠债的,你不再是大爷了!一封催款通知书送给所有正在清账的你!
  • 催款通知书 2018欠债的,你不再是大爷了!一封催款通知书送给所有正在清账的你!
  • 催款通知书 2018欠债的,你不再是大爷了!一封催款通知书送给所有正在清账的你!

上海化科 关于转发市应急局印发《上海市危险化学品企业安全风险研判与承诺公告制度实施细则(试行)》的通知

上海市奉贤区应急管理局 关于转发市应急局发布的《上海市危险化学品 企业安全风险判断及承诺公告系统 实施细则通知(试行) 城镇、街道、社区、开发区安全监管部门及相关公司: 现将市应急局关于印发《上海市危险化学品企业安全风险判断和承诺公告制度实施细则(试行)》(沪应急规[2019]3号)的通知...

葫芦岛市教育局 葫芦岛市教育局通知,请互相转告

葫芦岛市教育局 葫芦岛市教育局通知,请互相转告

...................................................................................................................................................

葫芦岛市教育网 葫芦岛市教育局通知,请互相转告

葫芦岛市教育网 葫芦岛市教育局通知,请互相转告

...................................................................................................................................................