「一本正經地胡說八道」用日語怎麼說?大概是「真面目にふざけている」吧。這篇日誌大概就是這麼一個意思?算法
一直以來都想對Android APP開發的性能調優作一下總結,其實性能調優涉及到多方面的工做,每次有一些心得我都會記錄下來,零零散散記錄了不少,最近發現許多地方重複了,感受仍是得作一下整理的,知識就是這麼牢固起來的。數據庫
「APP卡頓」是一個問題,咱們既須要知道怎麼查找出哪裏形成卡頓,也須要掌握規避這些卡頓的技巧,因此這個話題能夠分爲「如何定位APP中的性能問題」和「提升性能須要注意哪些點」這兩部分,後續在陸續對這兩點展開討論吧,今天先從總體分析下問題存在的緣由。segmentfault
開始正題以前先讓我吐一吐苦水吧。設計模式
我我的喜歡日語,因此學了好久的日語了,一樣我也是由於喜歡Android,纔開始跳進了Android開發這個坑。不過很是遺憾的是,就和「當初我在日語班裏,周圍大部分人只是由於日語專業比較輕鬆才選了它」同樣,我周圍的Android研發同事大部分用的是IOS手機,甚至有人問過我「我說你工資也不至於那麼低吧,怎麼天天都拿着安卓手機」。產品則是每次都說「你這個交互和IOS的不同,我需求文檔裏寫得清清楚楚,要保持一致的用戶體驗」。設計的話,我歷來就沒有碰見過拿安卓手機的。緩存
我嘗試說過Android不比IOS差,可是沒人站在我這邊。性能優化
自從Android系統誕生以來一直都有一個疑問,「爲何Android手機這麼卡?」,不管Android設備的硬件再怎麼升級,版本再怎麼迭代,「Android比IOS卡頓」彷佛成爲一個板上釘釘的事實。Android手機真的卡麼?服務器
至少在Android Kitkat以前,許多Android開發者都會選擇迴避這個問題;Kitkat以後,有一部分開發者已經有底氣回答這個問題了;隨着Android Lollipop以及Marshmallow前後的出現,我以爲Android開發者均可以自信地回答說「Android不卡」了。網絡
在一個公司內部分享會中,國內Android領域的大神羅昇陽在分享他對ART模式的研究中說到,繼Kitkat採用了ART模式後,Lollipop中,除了UI線程以外又提供了一條專門用於執行動畫的線程,這樣一來UI線程就能夠專一於展現數據和響應用戶的輸入,這讓Android的流暢度又上了一個檔次。IOS是閉源的因此咱們無從得知,可是有人分析說IOS之因此這麼流暢很大一個緣由就是它大概也採用了相似動畫線程的機制。老羅也相信「Android系統不會比IOS卡,甚至已經比它還流暢」。框架
這可不是隨便說說,個人Nexus5升級到Lollipop以後我以爲它已經能夠匹敵同事的IPhone6了,前不久升級到Marshmallow內測版,我更是以爲它已經拉開IPhone6一個檔次了。異步
那爲何許多人以爲Android用起來仍是比IOS卡?注意老羅說「Android系統」比IOS流暢,而不是「Android應用」,言下之意就是Android系統自己不卡,卡的是設備開發商開發的ROM以及開發者開發的第三方APP。我我的以爲Android卡頓問題大體有如下的緣由。
如今Lollipop甚至Kitkat的普及率還不算很高,更別談Marshmallow了,這是Android碎片化的問題決定的,具體緣由有興趣的能夠本身Google,網上一堆比我在這裏吹的靠譜多的分析。
固然這裏也有很大一部分是設備開發商的鍋,Google開源的AOSP項目只能兼容Nexus系列手機的硬件,若是第三方設備開發商須要使用AOSP的話,最起碼也要把AOSP裏面的驅動部分改爲能兼容本身的設備的,此外,他們還喜歡把系統UI風格作成本身家的,這起碼也要本身寫一個Launcher應用和自定義主題(這也是許多Android ROM被吐槽成換皮膚的緣由);此外有一些特點功能,好比指紋設備,Android Marshmallow以前AOSP並無這個功能,因此開發商就得本身出解決方案了。這一系列的工做,形成了開發商沒法在Google發佈Android的新版本以後迅速升級自家Android設備的系統。
Android4.0版本相比以前的版本性能上優化了許多,無奈我接觸的Android產品都要求最低支持Android2.3,有一款SDK甚至要求支持到Android1.6並且不能使用Support庫(今年但是2015年!你能想象一個手動寫Thread去控制一個複雜的屬性動畫的效果有多糟糕嗎?),因此Android開發者須要作一大堆向下兼容的工做,有時候爲了保證在一些奇葩機型的兼容性,選擇了保守的實現方式而不採用Android的新特性。向下兼容的邏輯使得APP即便在高級版本的Android系統上也要跑一堆沒用的判斷邏輯,若是這些邏輯出如今循環體內則更糟糕;採用保守的實現方式,使得APP沒法發揮新版本Android的性能,即便用戶手機升級了Android系統版本也享♂受新版本帶來的體驗。此外,即便許多新的APP產品都選擇最低支持Android4.0,這使得許多新特性都不用Support庫支持就能直接使用,可是許多手機設備仍是沒法升級到Android4.4版本的系統,即便有,不少ROM仍是把ART模式給嚴格了,沒法體驗其脫胎換骨版的順暢。
最近支付寶被Google Play下架了,緣由就是其「從Play市場之外的服務器下載可執行代碼」,意思就是它使用了動態加載技術。Android的動態加載也不是新鮮的事物了,個人項目中也採用過,簡單來講原理就是Android APP採用「APK空殼+可執行代碼」的開發方式,APK只是一個空殼,用戶安裝過一次後就不用從新安裝了,若是須要升級APP,APK空殼會從服務器下載新的可執行代碼,更換本地的便可完成升級(具體實現方案如今網上一堆教程,也能夠參考我Github上的相關項目)。採用這種開發方式,開發者能夠迅速完成用戶安裝好的APP的升級,在某種意義上提升了用戶的體驗,可是開發者的開發方式也會變得比較「繞」,開發成本增長了很多,爲了保證兼容性也會放棄使用Android的一些沒法在動態加載框架上使用的功能,因此體驗每每比不上「正統」的Android開發方式。
缺乏了Google Play的約束,一些Android APP就變得肆意妄爲了,這一點在BAT系中顯得格外明顯。常駐內存就接受到服務器的推送信息的成功率就比較高了,用戶明明關了一些APP,可是它們就不想退出,就算咱們手動關閉了它們,一旦從新啓動、網絡變化等,它們就又從新啓動了,佔着原本就珍貴的手機內存,很快就不夠用了,這也是Android卡頓的一大緣由。「百度全家桶」你怕不怕?,一旦安裝了百度家族啓動的一個APP,就會偷偷幫你安裝上全家族的APP,就算是我這種作Android研發的都常常中招,不用說普通的用戶了。更可恨的是這些APP還會互相喚醒,Andriod Lollipop以後你能夠完全關閉一個APP,除非你手動啓動它不然它沒法自啓動,可是家族APP之間互相喚醒使得這成爲了可能,「綠色守護」等後臺清理神器在「互相喚醒」大法之下也沒轍了。
Android原生與H5界面交互的框架已經很成熟了,許多中大型的APP的有H5的界面,特別是淘寶、天貓、京東這種購物類的,這些APP有大量和時間相關的活動,在節日以前開發一個新版本的APP再發布到應用市場顯得太蠢了,H5網頁這種隨時能夠更新在線內容的技術最適合這種活動了。然而H5界面的性能在仍是肛不過原生界面,特別是各類黑科技附身的Lollipop以後的時代,特別是當一個界面有H5的東西也有原生的東西那就更加糟糕了。此外,國內設計師都喜歡把Android的美術圖設計得和IOS同樣,這裏不討論這樣作的緣由,然而結果就是Android的開發須要使用大量的自定義View,這樣一來,許多系統自帶控件的加速效果(特別是當用到系統公共資源的時候)就沒有什麼卵用了,並且每每這些自定義View都有或多或少的Bug,可能設計得不合理,也可能有內存泄露,這些都會影響APP的性能。
上面說的客觀緣由除了因爲Android的碎片化問題以外,其實有很大部分是開發者有意爲之,可是我相信這並非Android開發的責任,咱們是都是清白的,都是無奈的,錯的不是咱們,是世界啊!╮( ̄▽ ̄")╭。也不是產品經理的鍋,而是大環境造就的。國內互聯網的競爭堪比電商,不這麼作,不搶佔用戶的手機的話根本就沒法生存,就算百度不去作,阿里也會去……因此勉強能夠把這些歸類爲客觀因素。可是瞎BB了這麼多,都不是我想說的主要內容,我想許多人都不感興趣,我只是一時來興致了敲了這麼多字,感受若是不貼出來的話不就虧了嘛。
…………
……
…
既然你都看到這了,順便把主觀緣由部分也看完吧_(-ω-`_)⌒)_。
主觀緣由主要是因爲開發過程當中的過錯致使的,整體上來講大體有如下幾方面的緣由
APP要流暢的話就好讓它保持高度相應,CPU要能及時響應UI線程的操做。不過不可能把全部非UI的工做通通扔到異步任務裏面去,有時候一些工做直接在UI線程搞會很是方便,並且太多後臺線程也會形成大量的性能開銷。這是一個矛盾,這時候就須要成熟的異步任務框架咯,若是這個框架沒寫好的話,就可能致使後臺任務混亂,產生多餘的線程開銷,UI線程得不到及時的響應,更甚,若是有異步任務形成內存泄露,內存不夠用很快就卡頓了,甚至直接OOM嗝屁了。
內存是很是寶貴的,再多也不嫌多。因此當一個對象離開他的做用域後,咱們必定馬上回收它佔有的內存,最理想的狀態是對每個對象都能作到這樣,若是有一個對象作不到,就說明他泄露了。Android中,Activity對象以及Bitmap的像素數據每每佔用很是大的內存,若是這二者發生泄漏會致使可用內存急劇減小,那卡頓則就沒法避免。此外,即便沒有嚴重的內存泄露,可是頻繁建立對象和回收對象的話,會引起虛擬機頻繁的GC,GC佔用比較大的資源開銷,一樣也可能會致使卡頓。總的來講,開發過程當中要對內存的使用保持敏感,知根知底。
設計師給出的同一張美術圖能夠有多張的佈局實現,不一樣方案之間的性能可能相差很遠。ListView等列表控件的Adapter也有許多優化點,許多人容易疏忽。
這就不僅是Android領域的問題啦,某些特定的業務應該採用最優算法,把時間複雜度降到最低,儘可能避免指數級別的時間複雜度,儘可能以空間換時間,必要的時候使用Native庫來提升算法。
一開始我也提到了,其實性能調優涉及到多方面的工做,好比一些靜態的網絡資源要作好緩存不要重複請求;頻繁數據庫操做的話最好使用異步任務,SQLite默認實在UI線程直接操做數據庫的;使用反射也會比較性能,不過反射有時候確實挺方便的,特別是項目龐大的時候,這個看取捨;過分使用「設計模式」也會有額外的開銷,設計意味着更多接口和多態,業務跑起來須要額外的空間和時間;儘可能不要開多進程,進程之間的通信比同一進程之間的互調消耗的性能很是多,通常項目只要一個進程就夠了,有推送的能夠多一個推送進程;此外,不限制與Android客戶端開發,H五、服務器的優化也能提升APP的性能。
這個在後面的具體分析中,再結合實際遇到的問題一塊兒介紹吧。
關於性能優化的技術點,歡迎你們到這個日誌裏補充:[收集向] Android 性能調優的技術點