技术记录---Toast频繁弹出问题及其流程分析
来源:互联网 发布:ios10屏幕录制软件 编辑:程序博客网 时间:2024/04/29 15:01
问题引入:当我们弹出toast的时候,一般会
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
可是当我们频繁点击按钮的时候,就会频繁触发上面代码。这样 toast 就会在很长的一段时间才能消亡。
解决方案:只要避免 toast 频繁创建即可。
1、我们可以加一个时间限制,比如在2s(LENGTH_SHORT)时间内,不响应事件,不重复执行代码。
2、定义一个全局 Toast的对象mToast,这样频繁点击时候,直接用同一个对象。
方法1,不要解释,不是本文重点。方法2,当你执行如下代码的时候,就会发现“没有任何现象”,toast 完全不弹出。
mToast.cancel();//取消上次mToast.setText("new message");//设置新内容mToast.show();// 再次呈现
但是当代码改成如下时,就可以正常弹出,而且频繁点击时只显示一个:
//mToast.cancel();mToast.setText("new message");mToast.show();不管,你怎么疯狂点击,当停止以后,2s内就会消失。那么肯定心中有疑问:
1、为什么全局的 mToast 可以重复使用(当其自然消失后,还可以show)?
2、为什么在show之前加上“cancel”,就不显示了呢?
深入研究:从源码层次下,逐步分析。
1、创建 toast的时候
public static Toast makeText(Context context, CharSequence text, int duration) { Toast result = new Toast(context);// 创建一个对象,并在构造函数里面实例化TN( 访问远程服务时候用到的callback函数) //加载view View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null); TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message); tv.setText(text); //保存view result.mNextView = v; result.mDuration = duration; return result; }2、在setText方法里面进行赋值
public void setText(CharSequence s) { if (mNextView == null) { throw new RuntimeException("This Toast was not created with Toast.makeText()"); } TextView tv = (TextView) mNextView.findViewById(com.android.internal.R.id.message); if (tv == null) { throw new RuntimeException("This Toast was not created with Toast.makeText()"); } // 把文本直接赋值到 <span style="font-family: Arial, Helvetica, sans-serif;">R.id.</span><span style="font-family: Arial, Helvetica, sans-serif;">message 对应的TextView上面。</span> tv.setText(s);}3、然后show方法源码
public void show() { if (mNextView == null) { throw new RuntimeException("setView must have been called"); } //NotificationManagerService 服务 INotificationManager service = getService(); //和应用相关联,这样消息队列方便管理,每个应用最多显示50个 toast String pkg = mContext.getPackageName(); //callback 用于处理服务器调度,比如 开始显示,隐藏等操作。 TN tn = mTN; tn.mNextView = mNextView; // 加入 toast队列,等待被调度 try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty }}4、下面跟踪到 NotificationManagerService 源码的 enqueueToast方法
public void enqueueToast(String pkg, ITransientNotification callback, int duration) { ..... synchronized (mToastQueue) { int callingPid = Binder.getCallingPid(); long callingId = Binder.clearCallingIdentity(); try { ToastRecord record; int index = indexOfToastLocked(pkg, callback);//如果队列中已经存在,那么就直接取出来,并更新数值。 if (index >= 0) { record = mToastQueue.get(index); record.update(duration); } else { //不存在,走创建流程,保存到队列中去。 ......... record = new ToastRecord(callingPid, pkg, callback, duration); mToastQueue.add(record); } if (index == 0) { //调度下一个 toast showNextToastLocked(); } } finally { Binder.restoreCallingIdentity(callingId); } } }这也就解释了,为什么一个全局的对象toast可以频繁使用,因为当队列中存在该 toast的时候,直接进行了 更新操作,从头开始计时;当不存在的时候,就会走创建流程,然后 通过方法 showNextToastLocked 进行调度,进行显示。
5、showNextToastLocked 方法
private void showNextToastLocked() { //从队列中取出第一个toast ToastRecord record = mToastQueue.get(0); while (record != null) { try { //调用toast的 TN对象 record.callback.show(); scheduleTimeoutLocked(record); return; } catch (RemoteException e) { ...... } } }6、然后我们回到 Toast 对象的TN内部类中的show方法,改方法最终会调用handleShow方法。
public void handleShow() { //mView 是TN类的内部对象,保留是最近一次引用的view,初始为null //mNextView是TN类的内部对象,引用的是Toast中的mNextView,在 toast的show方法中赋值。 if (mView != mNextView) { // remove the old view if necessary handleHide(); // 此处对mView进行赋值,表示该 toast 的view已经显示。 mView = mNextView; mWM = (WindowManager)mView.getContext().getApplicationContext() .getSystemService(Context.WINDOW_SERVICE); .............. // 先进行remove操作 if (mView.getParent() != null) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } //然后通过 WindowManager 添加到 系统中,进行toast的显示。 mWM.addView(mView, mParams); trySendAccessibilityEvent(); }}7、当时间到达,或者 调用 cancel方法的时候,会调用 TN的hide方法
handleHide();// 把 该对象 赋值为 nullmNextView = null;handleHide方法里面的源码如下:
public void handleHide() { if (mView != null) { // note: checking parent() just to make sure the view has // been added... i have seen cases where we get here when // the view isn't yet added, so let's try not to crash. if (mView.getParent() != null) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } //重置 mView。 mView = null; }}8、下面当用户调用Toast的cancel方法时候
public void cancel() { //调用TN的hide方法,如第7步中代码所示。 mTN.hide(); //调用 NotificationManagerService 服务移除toast队列 try { getService().cancelToast(mContext.getPackageName(), mTN); } catch (RemoteException e) { // Empty }}总结:
1、问题1的答案在于,当我们频繁对一个对象进行 show的时候,如果第一次创建就走add操作,之后的就走 udpate操作,这样就解释了,我们频繁点击的时候全局对象mToast只显示一次的原因,而且当 toast消失后,我们还可以通过 show方法再次重复调用起来。
2、问题2的解释为:当我们调用 cancel的时候,会把 mView 和 mNextView赋值为null,这样,当我们再次调用 Toast的show方法时候,由于不满足if (mView != mNextView) 条件,而不被执行,也就解释了先cancel后show, toast没有显示的根本原因。
0 0
- 技术记录---Toast频繁弹出问题及其流程分析
- 非UI主线程如何弹出Toast以及Handler.post()流程分析
- Toast连续弹出的问题
- zookeeper频繁异常问题分析
- 日志分析找到弹出toast的应用
- 频繁Toast的解决办法
- jvm频繁GC问题查找记录
- Android的Toast简介(频繁弹出时,推荐复用)
- lk及其流程分析
- Toast优化,解决频繁点击一个按钮,toast会一直显示,不能立即消失的问题
- 解决Android中Toast重复弹出问题
- recovery升级遇到的问题及其流程简单分析
- Toast执行流程分析与复用
- Toast工具,解决连续弹出toast以及toast不能全屏的问题
- 打造全局Dialog,Toast,解决Toast多次弹出以及小米无法弹出悬浮窗问题
- activity+fragment模式下,fragment中Toast频繁空指针异常的问题
- 技术记录---Handle收不到消息的问题分析
- QuickContact分析及其弹出窗口实现
- leetcode 16 3Sum Closest
- VS2013MFC对话框工程学习笔记十二 -
- 山东省第一届ACM省赛 H SDUT 2158 Hello World!(穷举)
- HDU 2544 最短路(最短路 Floyd)
- Linux中VIM的使用
- 技术记录---Toast频繁弹出问题及其流程分析
- Cmake Configuring error 61/ fatal error LNK1123
- 二维码的生成细节和原理
- Java集合框架复习之规则集Set-TreeSet(五)
- Linux/unix-Windows 查看wwn号
- MyEclipse2014安装Activiti插件
- 关于火狐和IE下href="javascript:void(0)"兼容性的问题
- Hibernate 检索方式
- hdu4722 Good Numbers 规律题