浮點數(shù)引發(fā)的Canvas繪制血案

浮點數(shù)引發(fā)的Canvas繪制血案


今天在Android項目開發(fā)中遇到一個比較有趣的奔潰問題溪猿,感覺也好久沒有寫文章了,覺得可以跟大家分享一下的穴吹。這個問題涉及到浮點數(shù)計算盆昙、View繪制流程和機(jī)制羽历,理清楚后發(fā)現(xiàn)問題其實很簡單。

1.案發(fā)現(xiàn)場回顧

1.1 問題描述

某同學(xué)通過外部跳轉(zhuǎn)直接進(jìn)入WindowA(底部4個tab)的第4個tab的時候打開了WindowB淡喜,在WindowB中進(jìn)行了橫豎屏切換秕磷,此時返回了WindowA,切換到第1個tab后炼团,發(fā)現(xiàn)app卡死之后閃退澎嚣。

1.2 問題分析

1.2.1 下面簡單拆解一下其實現(xiàn):

  • WindowA中4個tab對應(yīng)的View通過設(shè)置visibility(GONE/VISIBLE)切換。
  • WindowA針對橫豎屏切換做了監(jiān)聽瘟芝,更改了Tab1中某些View的大小和位置并觸發(fā)重繪制易桃。
  • WindowA中初始時候四個Tab都是GONE,直接進(jìn)入Tab4的時候這時候只有Tab4是VISIBLE锌俱。
  • 從WindowB回來后只有點擊Tab1才會觸發(fā)奔潰晤郑。
  • 點擊Tab1之后只做了一個處理,那就是切換其Visibility為VISIBLE嚼鹉。

贩汉??锚赤?匹舞??线脚?為什么僅僅設(shè)置了一個View的Visibility就會導(dǎo)致閃退呢赐稽??浑侥?姊舵??寓落?
括丁??伶选?史飞??仰税?為什么閃退的時候看不到有奔潰日志构资??陨簇?吐绵???

1.2.2 部分關(guān)鍵代碼簡要回顧:

  • WindowA中Tab1針對橫豎的監(jiān)聽處理代碼如下(僅示例):
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的實現(xiàn)代碼如下(僅示例):
   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.問題分析和定位

或許很多人可能一看代碼就能很清楚明了發(fā)現(xiàn)問題了己单,不過下面還是容我分析一般唉窃。

2.1 首先,從onConfigurationChanged出發(fā)看代碼:

  • mRecycleViewPager.getWidth(); //Tab1初始化為GONE荷鼠,這里直接進(jìn)入Tab4句携,此處getWidth為0榔幸。

  • leftScrollX * 2f / width; //這里除以width允乐,0的時候拋異常?

那么削咆,問題是否是因為getWidth()==0導(dǎo)致除的時候拋異常能牍疏?
答案肯定是否定的,如果除的時候拋異常拨齐,那么橫豎屏切換的時候就奔潰了鳞陨,而不是等到Tab1的setVisibility才奔潰。

這里就牽扯出一個關(guān)于浮點數(shù)計算的問題了:浮點數(shù)計算的時候瞻惋,此處除以0厦滤,事實上得到的結(jié)果是一個正無窮或者負(fù)無窮。

所以歼狼,并不是除以0導(dǎo)致的異常掏导。(其實雖然不會異常但得到一個正無窮或者負(fù)無窮的值,之后在使用的時候肯定也會有問題)

2.2 接著羽峰,從mCurrentView.setScaleRate出發(fā)看代碼:

  • setScaleRate會觸發(fā)invalidata
  • dispatchDraw中canvas.scale(mScaleRate, mScaleRate, getWidth()/2f, getHeight()/2f);

其實趟咆,可以發(fā)現(xiàn),當(dāng)mScaleRate為無窮的時候梅屉,這個語句在canvas繪制的肯定會出問題值纱。
但是,為什么橫豎屏切換的時候明明就已經(jīng)觸發(fā)了invalidate但是并沒有卡死奔潰坯汤?

這里就牽扯出View繪制機(jī)制的問題了:當(dāng)視圖不可見(GONE)的時候調(diào)用invalidate是不會觸發(fā)Draw的虐唠。

所以,等到Tab1切回了可視(VISIBLE)重繪的時候才會跑到dispatchDraw惰聂,這個時候canvas.scale處理一個無窮大的值疆偿,你說會不會有問題?

3. 問題總結(jié)

1.橫豎屏切換的時候給View設(shè)置了一個非法數(shù)值(無窮大)庶近。
2.切tab觸發(fā)View的Draw的時候使用了這個非法數(shù)值進(jìn)行了canvas繪制翁脆。

4. 問題解決方法

  1. Tab1不可見的時候不監(jiān)聽處理onConfigurationChanged。
  2. 當(dāng)getWidth為0的時候不應(yīng)該做下一步處理鼻种。
  3. dispatchDraw中對mScaleRate做非法值校驗反番。

5. 總結(jié)

其實應(yīng)該也算是一個低級問題,不過這個低級問題下面也牽扯到一些高級知識。雖然這邊文章寫得云里霧里罢缸,不過總結(jié)一句話:問題都是可以解決篙贸,解決問題的同時要深究根源并從中總結(jié)知識。

最后枫疆,如果覺得我闡述的不夠詳細(xì)的爵川,歡迎補(bǔ)充。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末息楔,一起剝皮案震驚了整個濱河市寝贡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌值依,老刑警劉巖圃泡,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異愿险,居然都是意外死亡颇蜡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門辆亏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來风秤,“玉大人,你說我怎么就攤上這事扮叨$拖遥” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵甫匹,是天一觀的道長甸鸟。 經(jīng)常有香客問我,道長兵迅,這世上最難降的妖魔是什么抢韭? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮恍箭,結(jié)果婚禮上刻恭,老公的妹妹穿的比我還像新娘。我一直安慰自己扯夭,他們只是感情好鳍贾,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著交洗,像睡著了一般骑科。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上构拳,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天咆爽,我揣著相機(jī)與錄音梁棠,去河邊找鬼。 笑死斗埂,一個胖子當(dāng)著我的面吹牛符糊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播呛凶,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼男娄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了漾稀?” 一聲冷哼從身側(cè)響起模闲,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎县好,沒想到半個月后围橡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡缕贡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拣播。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晾咪。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖贮配,靈堂內(nèi)的尸體忽然破棺而出谍倦,到底是詐尸還是另有隱情,我是刑警寧澤泪勒,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布昼蛀,位于F島的核電站,受9級特大地震影響圆存,放射性物質(zhì)發(fā)生泄漏叼旋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一沦辙、第九天 我趴在偏房一處隱蔽的房頂上張望夫植。 院中可真熱鬧,春花似錦油讯、人聲如沸详民。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沈跨。三九已至,卻和暖如春兔综,著一層夾襖步出監(jiān)牢的瞬間饿凛,已是汗流浹背隅俘。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留笤喳,地道東北人为居。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像杀狡,于是被迫代替她去往敵國和親蒙畴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容