華為手機為何在攝影技術占據領先?今天為你呈現碼農如何優化適配

菊廠工程師 發佈 2019-12-29T07:50:44+00:00

加入終端,緣來如此美妙2005年的一個夜晚,老爸因為工作需要買回了家裡第一台電腦。初中生的我,第一次望著顯示屏上的windows大草原,體驗著滑鼠在系統頁面自由跳轉的快感,被這個「價值不菲」的產品勾起了興趣。

加入終端,緣來如此美妙



2005年的一個夜晚,老爸因為工作需要買回了家裡第一台電腦。初中生的我,第一次望著顯示屏上的windows大草原,體驗著滑鼠在系統頁面自由跳轉的快感,被這個「價值不菲」的產品勾起了興趣。這之後,我常常採用各種「遊戲隱藏秘笈」跟老爸鬥智鬥勇,以此獲得更多與電腦的「約會」時間。


步入高中,我的興趣再次延伸,偶然間學習到通過U盤boot文件自啟動批處理,反編譯破解了網管軟體,甚至成了黑客論壇版主。這一切引爆了我成為一名軟體工程師的好奇心,於是高考報志願時我毫不猶豫選擇了計算機專業。


大學期間,正值Android崛起,我也隨之開啟了Android的探索之路。新奇於第一次在自己手機里安裝了自己的應用,學著獨立開發了一個遊戲輔助工具,並添加廣告盈利,親身體驗了一次移動網際網路紅利,還被導師推薦參加了一些和學校合作的有關Android的創新項目……
用編碼改變世界,成了我最想實現的夢。


2015年7月,22歲的我順利畢業,如願加入正在智慧型手機領域開疆闢土的華為終端,期待再續Coding之緣,做自己喜歡的事,在技術領域探索更多的美妙。



萌新預研,提前熟悉相機基礎框架


入職軟體基礎ROM部兩周時,PL安排我去做預研(預備研發)工作,我成為預研團隊唯一的新員工。PL鼓勵我:「往上跳一跳,能『夠』到就大膽上!」這句話讓我受用至今。


那時候,團隊正在做下一代相機預研項目。新的相機應用要從功能較少的一代接口切換到能力和拓展性更強的二代接口,並最終落地到華為旗艦機的雙攝產品中。這涉及40萬行代碼量和數百個功能的重新開發,而預研要做的是搭起新相機應用的基礎插件化框架,將內部各個功能解耦成多個相互獨立的插件,彼此能自由組合,想要哪個模式,把文件加進來就行,供後續功能業務的快速拓展。


這對萌新的我來說,無疑是一個挑戰,當時插件化在業內還沒有太成熟的方案,終端內部也沒有做過。但我也知道,這更是一個機遇,職場起步就能接觸和學習比較新的技術,還有什麼比這更美好的事呢?
別看我現在說得好像很容易,當時特別艱難。我在大學期間的學習偏技術原理,原來做開發是用的「野路子」,寫個幾千行的代碼做個小項目還湊合,而現在參與的是需要整個團隊維護、數十萬代碼量級的大工程,預研更複雜的是,要搞清楚基礎框架,「野路子」一下子暴露出短板,寫出來的代碼有各種毛病,比如只考慮功能的實現,從未考慮代碼要分層分模塊,沒考慮代碼的可讀性。我不得不逼著自己從零開始理解新架構。


幸運的是,我抓住了當時的架構設計師陳國棟的「援手」。國棟哥當時在華為已十年,是相機的模塊設計師,在相機領域造詣頗深。他給我推薦Java書籍,講解新框架的設計思想,再手把手教我設計接口草稿,職場新人喝著前輩的這杯「咖啡」吸收宇宙的能量。


前輩的思考方式和工作習慣也深深影響了我,他會從全局考慮設計是否合理,是否適合後續業務演進,接口設計是否穩定,後續會不會頻繁修改。他還有一個習慣,和別人溝通時,一邊溝通一邊寫下設計好的接口代碼,等溝通完,設計草稿已經出來了。「除了完成任務,更重要的是要想如何高效完成。」他常常對我這樣說。


從前輩的身上,我學到了太多太多。我知道了什麼是代碼的好壞味道,代碼要講究可讀性、可拓展性、可維護性,我知道了代碼的設計原則是為了更快更好地業務交付,我知道了怎麼寫代碼,相機業務是什麼,更學習了什麼是插件化的技術和解耦……就這樣一邊工作一邊學習,做得多,學得也多,不僅幫助我提前熟悉了新架構,也為後面獨立承擔基礎模塊開發打好了基礎。


毫不謙虛地說,我自此建立了一種在熟悉的領域不管多難都能搞定的自信。也正是這種自信,支撐我後來不管碰到什麼難題,都會選擇「跳一跳」,萬一「夠」到了呢!


落地雙攝,我「夠」到了


完成新相機基礎插件化框架預研後,為趕上3個月後的旗艦機發布,預研組十餘人展開了與時間賽跑的三個月封閉開發。


因為對基礎框架非常熟悉,主管將相機應用的基礎流程管理,這一最基礎也最容易出現問題的部分交給了還是新員工的我,其他人負責人像、夜景、大光圈等各個模式的開發。基礎流程管理涉及基礎的相機啟動模塊,3A算法交互模塊、插件管理模塊、基礎拍攝流程等,用作各個模式的實現基礎。那時候,我每天都要面對任務板上幾十個密密麻麻的問題單和需求,有的能復現還好,有的是小機率偶現,還有一些是歷史遺留問題,問題的繁雜常常讓我措手不及。


好在「敲代碼」這件事讓人受益又讓人快樂。我在代碼的海洋里奮力探索,從團隊空間到公司論壇,再到外部開源社區、搜尋引擎,想盡辦法不斷汲取知識,找到解題思路,把一個個問題作為練手的工具,每晚到深夜仍然樂此不疲。當時相機的啟動涉及二代接口和第三方應用,能參考的資料非常少,我查閱各種文檔、手冊和網上的問答,幾乎搜遍全網終於在公司Andriod源碼里找到了參考工程,有全量幾十萬行的代碼。我從中搜索與實現功能有關的代碼,仔細分析研讀,找到了解題的鑰匙,並從此掌握了一套知識搜索和學習技巧,獨立解決問題更遊刃有餘,問題單的閉環速度也越來越快,從一開始一天一兩個到後來平均五六個,甚至在組內有了「解單高手」的稱號,算是漸漸站穩了腳跟。


忙碌的日子總是過得飛快,搭載了全組人心血的全新相機應用火熱出爐,我們成功按期交付版本,落地P9系列雙攝產品中。


現在回想起來,當時的艱難困苦都煙消雲散,留下的只有滿滿的收穫與感激。每當遇到艱難的階段,我就會想起這段經歷,對自己說:不怕跳一跳,就怕不敢跳;每一次起跳,都是向下一次發起衝擊。


優化十來天,相機啟動性能達業界第一


2018年,離EMUI9.0版本拉出商用分支只有十幾天,我們突然接到一個新的優化訴求——Connect老化性能評測,相機啟動性能要從1.2秒優化至0.6秒。


這個突如其來的極端要求把我搞懵了,我非常排斥,心想怎麼可能?往常這種需求要投入兩三人用一個月的時間實現,是不是流程出了問題?一般一個指標提升10%已經是非常大的變化了,這次要提升100%,幅度這麼大,合理嗎?業界其他競品當時最快就是0.6幾秒,我們在十幾天內能做到超越嗎?
儘管帶著諸多疑問,我還是一邊和測試部確認需求,一邊著手分析歷史代碼。三天時間,通過排查發現,原來是歷史代碼隨意增加功能搗的鬼。我開始對代碼大膽「開刀」,對「壞味道」的歷史代碼逐一優化,並且調整相機全局資源加載流程的優先級,想盡辦法減少每一毫秒的開銷,一下子優化到了0.9秒,但是離最終目標還有不小的差距。


之後,考慮多個線程相互等待問題,我把一些線程的串行改為並行,把首介面不會出現的東西晚一點加載,相機啟動性能優化到0.7秒。我感覺自己已經絞盡腦汁,可離要求的還有0.1秒!


還能再優化嗎?我想來想去,只能採用比較極端的辦法了。相機啟動涉及幾十個類幾百個變量的初始化,我會去摳每個變量的初始化時間消耗,有些不需要在相機啟動時出現在介面的變量,要麼挪到後面,要麼放到比較靠後的時間段執行,這樣一個一個細摳,終於達到了0.6秒。


最終,我們的相機啟動性能實現了Connect老化海外評測業界第一的目標。


完成任務後,我對代碼的理解更深了一層。隨意添加功能導致性能惡化,這些就是我們開發人員自己挖的坑,而指標優化,不過是開發人員必要的自我填坑罷了。有的人會及時填坑,目的是解決短期矛盾;而更優者則是前期「不留坑」,和壞代碼說「NO」,目的是解決長遠問題。


2019年,做相機1+N 組件化方案時,在完成需求規定特性的基礎上,我依然思考著如何避免以前的問題重犯。組件化後會帶來加載的高負載,我想了一個辦法,按需延遲加載各個拍攝模式組件,減少初始內存消耗20M,整體內存占用下降10%,高負載下性能優化15%以上。


有人說,你走過的路每一步都算數。其實你每改一次壞代碼,積累的好代碼就越多;你每一次寫代碼「不留坑」,持續用好代碼要求自己,就是在構建好代碼的萬里長城。


主動重構介面,效率提升60%


  相機作為新手機發布會賣點的「常客」,介面設計一直以來受到用戶的廣泛關注。


  在我來公司的這幾年,相機介面從4.1版本升級到10.0版本,存在多次大的用戶介面調整。2017年,我和同事十餘人日夜奮戰3個月,修改了十萬行代碼,成功交付新版相機介面。但改完之後回過頭看,我們發現人力投入還是比較大,我心裡不斷冒出另一個想法:能不能讓相機應用的介面調整變得更簡單?能不能做到在不影響功能邏輯的前提下調整介面?


  當時相機應用的介面和功能耦合,介面就是用戶看到的圖標、按鈕,功能是用戶點擊了圖標或按鈕後的響應。調整介面就會影響到功能,修改起來複雜,容易引入問題。


  痛定思痛,我決定選擇重構,也減少以後介面調整的工作量。把這個想法和團隊一說,大家也非常支持。


  然而,面對歷史的功能接口設計易與介面耦合的問題,新接口不僅要解決歷史痛點,又必須適配相機所有的功能特性,這意味著很多需求都要推倒重來。而要推倒重來的前提,是對原來的功能業務代碼了解透徹,並有很好的重構思維。


  比如,相機的美膚功能,重構時存在一代簡單美膚、二代加了虛化效果,後來又加了光滑、美白、瘦臉等美膚能力,每一個功能對應不同的歷史產品,由不同的人開發的不同版本,規格不一,重構後的代碼必須能保證以往的功能規格都能使用,且不會遺漏。為了了解原來的代碼,我找到當時做這些功能需求的人,一個一個去交流,但有些功能已經實現很久了,開發的人不一定記得自己寫過的代碼,也只能靠自己一行一行看代碼,自己去理解掌握。


  說實話,這期間,我曾經有過放棄的念頭。團隊並沒有給我定重構的目標,能實現多少是自己擬定的,我曾考慮放棄比較複雜的模塊,但一想到下一次交付還會產生問題,還不如一次就把事情搞定,只得硬著頭皮咬牙堅持下來。


  幸運的是,重構之後介面服務化管理模塊成功上庫,介面和功能在EMUI9.0版本中解耦成功,開發變得更簡單,更穩定。緊接著在2019年最新的EMUI10.0相機介面中,我基於重構的代碼,用了2個月便快速完成介面替換,效率提升60%。


  這讓我把餘下的精力放到與介面設計師一起持續打磨極致體驗上。原先相機在華為手機、平板、摺疊屏的布局上並不統一,帶來了介面適配和維護較大的工作量,我們一起優化了設備的差異性,進行了統一,在面向用戶的EMUI10.0設計風格調研中,相機的整體滿意度再上一個台階,讓我再次嘗到重構的美妙。


  不經意間,我養成了每次修改問題單主動識別壞代碼、重構問題頻發區的習慣。2019年,我重構的代碼也驚喜獲得了軟體部首屆鴻蒙金碼獎。


  重構應該是自我驅動的,每一位工程師都應該嘗試著獨立看護某些模塊,發現代碼壞味道,再大膽優化。在編程界,流傳著一句話——10萬行代碼鑄就編程高手,它也激勵著我主動重構,寫出更多好代碼。這種量變到質變的過程,本身就是代碼修煉之道。


年輕,就要一碼當先


  「殺」猿祭天、畢業2年擁有4年工作經驗、全員格子衫……每當我看到網友們對程式設計師調侃時,也會會心一笑。慢慢地,我也會注意到這些標籤背後增加了新內容,比如小學開通python課程,比如非計算機專業的大學生跨界編碼,比如很多名人紛紛學習編程……這無一不在印證賈伯斯「人人都應該學習編程」的話嗎?而作為年輕一代的程式設計師,我們更要一碼當先。


  不管程式設計師的標籤是什麼,我們都需要用代碼為自己代言。轉眼進入華為已經4年有餘,我很幸運,伴隨著終端的每一步發展和崛起,我也從職場菜鳥成為能獨當一面的模塊設計師。工作之餘,我很喜歡去視頻網站看手機拆解視頻、前沿科技評測視頻,了解主流廠商攝像頭規格及安裝方式,了解未來趨勢。我喜歡我正在做的事,我也慶幸我能一直做自己喜歡的事。未來面臨全場景的應用設計的新挑戰,這將又是新的學習之旅,但我始終相信,只要拼搏向前,再大的困難總會結束,我們終有所獲。


  作為26歲編碼人,我常常會幻想這樣一個場景:


  夕陽西下,年邁古稀,家裡的小輩問我最會什麼,我能自豪地說:「我最會敲代碼!「

關鍵字: