浮点数引发的Canvas绘制血案

浮点数引发的Canvas绘制血案


今天在Android项目开发中遇到一个比较有趣的奔溃问题,感觉也好久没有写文章了,觉得可以跟大家分享一下的。这个问题涉及到浮点数计算、View绘制流程和机制,理清楚后发现问题其实很简单。

1.案发现场回顾

1.1 问题描述

某同学通过外部跳转直接进入WindowA(底部4个tab)的第4个tab的时候打开了WindowB,在WindowB中进行了横竖屏切换,此时返回了WindowA,切换到第1个tab后,发现app卡死之后闪退。

1.2 问题分析

1.2.1 下面简单拆解一下其实现:

  • WindowA中4个tab对应的View通过设置visibility(GONE/VISIBLE)切换。
  • WindowA针对横竖屏切换做了监听,更改了Tab1中某些View的大小和位置并触发重绘制。
  • WindowA中初始时候四个Tab都是GONE,直接进入Tab4的时候这时候只有Tab4是VISIBLE。
  • 从WindowB回来后只有点击Tab1才会触发奔溃。
  • 点击Tab1之后只做了一个处理,那就是切换其Visibility为VISIBLE。

??????为什么仅仅设置了一个View的Visibility就会导致闪退呢??????
??????为什么闪退的时候看不到有奔溃日志??????

1.2.2 部分关键代码简要回顾:

  • WindowA中Tab1针对横竖的监听处理代码如下(仅示例):
protected void onConfigurationChanged(Configuration newConfig) {
    //...
    int width = mRecycleViewPager.getWidth();//mRecycleViewPager为Tab1中的View
    float scaleRateLeft = SCALE_RATE * (1.0f - Math.abs(leftScrollX * 2f / width));
    mCurrentView.setScaleRate(scaleRateCenter);//mCurrentView为Tab1中的View
    //...
}
  • Tab1中mCurrentView.setScaleRate的实现代码如下(仅示例):
   public void setScaleRate(float scaleRate) {
       mScaleRate = scaleRate;
       invalidate();
   }
   // ...
   @Override
   protected void dispatchDraw(Canvas canvas) {
       if (mIsNeedTranslate) {
           canvas.save();
           canvas.translate(mDeltaScrollX, 0);
           if (mScaleRate != 1.0f) {
               canvas.scale(mScaleRate, mScaleRate, getWidth()/2f, getHeight()/2f);
           }
           super.dispatchDraw(canvas);
           canvas.restore();
           if (mDeltaScrollX == 0) {
               mIsNeedTranslate = false;
           }
       } else {
           super.dispatchDraw(canvas);
       }
   }

2.问题分析和定位

或许很多人可能一看代码就能很清楚明了发现问题了,不过下面还是容我分析一般。

2.1 首先,从onConfigurationChanged出发看代码:

  • mRecycleViewPager.getWidth(); //Tab1初始化为GONE,这里直接进入Tab4,此处getWidth为0。

  • leftScrollX * 2f / width; //这里除以width,0的时候抛异常?

那么,问题是否是因为getWidth()==0导致除的时候抛异常能?
答案肯定是否定的,如果除的时候抛异常,那么横竖屏切换的时候就奔溃了,而不是等到Tab1的setVisibility才奔溃。

这里就牵扯出一个关于浮点数计算的问题了:浮点数计算的时候,此处除以0,事实上得到的结果是一个正无穷或者负无穷。

所以,并不是除以0导致的异常。(其实虽然不会异常但得到一个正无穷或者负无穷的值,之后在使用的时候肯定也会有问题)

2.2 接着,从mCurrentView.setScaleRate出发看代码:

  • setScaleRate会触发invalidata
  • dispatchDraw中canvas.scale(mScaleRate, mScaleRate, getWidth()/2f, getHeight()/2f);

其实,可以发现,当mScaleRate为无穷的时候,这个语句在canvas绘制的肯定会出问题。
但是,为什么横竖屏切换的时候明明就已经触发了invalidate但是并没有卡死奔溃?

这里就牵扯出View绘制机制的问题了:当视图不可见(GONE)的时候调用invalidate是不会触发Draw的。

所以,等到Tab1切回了可视(VISIBLE)重绘的时候才会跑到dispatchDraw,这个时候canvas.scale处理一个无穷大的值,你说会不会有问题?

3. 问题总结

1.横竖屏切换的时候给View设置了一个非法数值(无穷大)。
2.切tab触发View的Draw的时候使用了这个非法数值进行了canvas绘制。

4. 问题解决方法

  1. Tab1不可见的时候不监听处理onConfigurationChanged。
  2. 当getWidth为0的时候不应该做下一步处理。
  3. dispatchDraw中对mScaleRate做非法值校验。

5. 总结

其实应该也算是一个低级问题,不过这个低级问题下面也牵扯到一些高级知识。虽然这边文章写得云里雾里,不过总结一句话:问题都是可以解决,解决问题的同时要深究根源并从中总结知识。

最后,如果觉得我阐述的不够详细的,欢迎补充。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容