Android開發:除了編碼以外,這個技能也很重要

作者:qing的世界

週末?不存在的。

在大廠,寫得一手好文檔是一個非常吃香的技能。這可不只是一個錦上添花的東西,而是很多工程師晉升,打造自己話語權的武器。

我這兩年在組內的深刻體會就是,大部分厲害的高級工程師(不包括那些純混日子靠資歷晉升的人),寫文檔的能力一點也不含糊,很能抓住上級和項目的G點。

Android開發:除了編碼以外,這個技能也很重要

可能有人會覺得,我技術牛逼就行了,為啥還要提高寫文檔的能力,有這功夫我還不如多看看源碼分析?

這是一些初級或者剛入門的工程師的普遍的困惑。

這是因為大部分剛剛入行的朋友有一個很深的誤區,就是他們以為做軟件工程是一個和計算機打交道的工作,其實不然。軟件工程不只是和代碼打交道,更重要的是和人打交道,是一份社會性質很強的工作。

在大部分公司裡面,尤其是大廠,牽涉到的人,組,都是非常非常多的。在小廠,人與人之間交流意見和設計可以口口相傳,心領神會,但是一旦人開始多了,就只能靠文檔了。除非你可以厲害到一個人把所有代碼擼完,不然還是最好老老實實的夯實自己寫文檔的能力。

如果你有寫技術博客的習慣,那麼恭喜你,相信你已經對如何抓住文檔受眾的技巧有所瞭解了。這對你在大廠生存有很大的幫助。如果沒有也不要傷心,這篇文章就是為你精心設計的。

在這篇文章裡,我會大致的把一份安卓的項目設計文檔的骨架,和一些我工作中實際遇到的正反例都列出來,方便大家以後在工作中實踐。

一、設計文檔的結構

一個好的項目設計文檔,其實有一定的模板可以參考的,不過不管模板怎麼變,大致都需要有以下幾個大框架

  1. 項目背景
  2. 項目術語
  3. 技術挑戰
  4. 完成要求 (App性能要求 (可選),App Size 要求 (可選))
  5. 現有架構(可選)
  6. 建議架構(引入的第三方框架/SDK的簡介 (可選))
  7. 開發時間線
  8. 其他可選架構(可選)
  9. 參考文獻

咱先從項目背景開始聊。

二、項目背景

如果大家面試次數夠多,應該會有聽過一個叫STAR原則的東西,就是介紹自己項目的時候要遵循Situation(背景)->Target(目標)->Action(行動/做法)->Result(結果)這樣的順序,儘量做到簡潔。

同樣的,項目背景的介紹就是對應了這個STAR原則的S,也可以說是項目的動機,為什麼要做這個項目。

這個背景和動機可以是一個產品產生的動機。

比如說“抖鷹”的產品經理發現競品“快腳”提供了一個新的視頻濾鏡,而且這個濾鏡在競品“快腳”中迅速攀升到用戶熱度的第一位了,基於我們在產品的數據分析中blalala。。。於是我們也要做這個濾鏡。這就是一個簡潔明瞭的項目背景。

當然這個背景也可以是一個純技術方面的問題,比如架構的升級等等,當然如果是架構的升級,那需要在背景裡面簡單的介紹現有架構的大概的一些侷限性(我們下文會提到)。

本人閱讀過的一些經典反例就是,背景介紹的第一句話上來就開始直接飆產品/公司內部的一些黑話,比如某個sqlite 的 database的某一個col有問題啊,或者是公司內部的一個SDK的限制等等。

這些都是技術細節,不是項目大背景。提前把這些細節說出來是沒法在第一段就抓住讀者的眼球的,這會讓讀者失去仔細觀看全文的熱情,導致最後你的設計文檔可能收不到任何有意義的反饋。

三、項目術語

這一部分就更重要了。項目術語這個部分必須要儘可能的把設計中涉及到的:

  1. 新引用的SDK/框架
  2. 項目之前沒用過的語言
  3. 項目/公司內部工具,服務
  4. 產品本身的組件Component.

都過一遍,尤其是對一些剛剛進組的朋友,這對他們會有很大的幫助。很多剛剛入職的朋友初來乍到,可能也不太敢在研討會上問問題,閱讀沒有項目術語的文檔對他們可以說是直接勸退的。

作為一個往高級工程師方向努力的朋友們,擴大自己在組內影響力也是一個至關重要的點,如果你的設計文檔可以對初級工程師/剛剛進組的朋友更友好,那麼你已經成功了一半了。

很多在組裡面待了很久的老鳥會懶得在產品本身的組件Component 解釋太多,因為他們想當然的會覺得這是一個他自己每天都接觸的組件沒有必要解釋。這其實是不太好的,因為你的文檔不是給自己看的,而是給其他組員,甚至老闆(老闆很多情況下是不瞭解產品的技術細節的)。

比如你在新的項目中打算使用GraphQL這個查詢語言和相應的框架。那麼最好的做法是先在術語環節介紹一下:

Android開發:除了編碼以外,這個技能也很重要

GraphQL -> 是一種針對圖狀數據進行查詢特別有優勢的查詢語言

GraphQL Query-> 一種類似於HTTP GET的GraphQL 請求,用來查詢後端數據

GraphQL Mutation-> 一種類似於HTTP POST 的GraphQL請求,用來修改後端數據

GraphQL Subscription-> 一種建立在客戶端和後端之間的長鏈接,用來監聽後端數據變化請求,大部分GraphQL框架用websocket來實現

有了這上面的介紹,相信你在接下來設計細節說到Query/Mutation的時候就不會有人懵逼了。

四、技術挑戰

這個環節就比較簡單了,把該項目的技術難點都列舉出來,但是有一個問題要切記:

不要貼源碼!不要貼源碼!不要貼源碼!

很多朋友,包括在寫博客的時候都是一言不合直接複製粘貼源碼,這樣的做法是非常讓人討厭的,說白了就是偷懶,連精煉一下源碼,哪怕做一份偽代碼加comment的功夫都不肯下。還是那句話,文檔是寫給別人看的,不是寫給自己的。

這裡我用KunMinX大哥的博客裡面的偽代碼做正面 例子,大家如果看到這一份安卓事件分發的源代碼 (KunMinX 大哥如果你看到了覺得不想自己的例子被放進我的文章,請聯繫我,會及時刪掉並替換,在這裡先感謝你):

<code>@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
    mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
    ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
    final int action = ev.getAction();
    final int actionMasked = action & MotionEvent.ACTION_MASK;
    // Handle an initial down.
    if (actionMasked == MotionEvent.ACTION_DOWN) {
        // Throw away all previous state when starting a new touch gesture.
        // The framework may have dropped the up or cancel event for the previous gesture
        // due to an app switch, ANR, or some other state change.
        cancelAndClearTouchTargets(ev);
        resetTouchState();
    }
    // Check for interception.
    final boolean intercepted;
    if (actionMasked == MotionEvent.ACTION_DOWN
            || mFirstTouchTarget != null) {
        final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
        if (!disallowIntercept) {
            intercepted = onInterceptTouchEvent(ev);
            ev.setAction(action); // restore action in case it was changed
        } else {
            intercepted = false;
        }
    } else {
        // There are no touch targets and this action is not an initial down
        // so this view group continues to intercept touches.
        intercepted = true;
    }
    // If intercepted, start normal event dispatch. Also if there is already
    // a view that is handling the gesture, do normal event dispatch.
    if (intercepted || mFirstTouchTarget != null) {
        ev.setTargetAccessibilityFocus(false);
    }
/<code>

是不是瞬間沒有任何興趣了?請注意我還沒有全部都複製上來,就只是一小段而已。

再看KunMinX大哥的精簡版偽代碼:

https://juejin.im/post/5d3140c951882565dd5a66ef

Android開發:除了編碼以外,這個技能也很重要

是不是瞬間就豁然開朗了。

寫文檔也是這樣,如果我是審查者看到上來就貼項目內部的源代碼的設計文檔,對不起,我會直接打零分,貼源代碼的設計文檔,像極了初中的時候為了湊八百字而生硬的加排比句的作文一樣,看起來很豐滿,其實都是骨頭沒有肉。

當你自己想鑽研技術的時候,一行行的研究源碼是沒毛病的,但是如果你是要分享給他人的時候,千萬別直接複製粘貼。

五、完成要求

這個環節也不多做介紹,這是和公司/產品內部的需求有關係。比如你做結構的修改,做完之後是否會影響到原有的開發流程,如果有,是否會嚴重的影響,這些都是需要列出來的。

六、現有架構和建議架構

一提到架構,很多人都會覺得很虛,感覺無從下手。

其實在這方面,流程圖和組件通信圖都是很好的幫手。有時候可能自己會覺得無從寫起,但是其實只要把流程圖/組件通信圖一畫,其實就豁然開朗了。

這裡我以我司的一個最近剛剛開源的移動開發框架 Amplify(aws.amazon.com/cn/amplify/)為例。

Android開發:除了編碼以外,這個技能也很重要

假如我在我的最新設計中提議使用這麼一個新的框架,那麼首先我得闡明這個框架是做什麼的(PS:這是我自己總結的,和公司文案無關):

Amplify Mobile sdk 給客戶端提供了一套離線應用解決方案,它包括了離線存儲,和服務端數據增量更新,還有身份驗證,日誌發送等等移動端所需的功能。該框架以GraphQL語言為基礎,通過WebSocket保持和服務器端的實時連接,還有基於時間戳的增量/全量更新保持客戶端和服務端的數據一致。

好了,那你們組的高級工程師可能會問,那這個Amplify Mobile SDK內部是大概怎麼實現離線還有和服務器端數據一致性的呢?

這個時候組件通信圖就派上用場了。話不多說,先上圖 (這裡我們用 arcentry.com/app/ 來做示範,這個工具提供了很多AWS相關的服務組件圖,比較好上手)。

Android開發:除了編碼以外,這個技能也很重要

同時,讓我來以一個設計者的角度來說明這個架構圖大概內容:

在Amplify Android SDK的架構設計上,每當用戶在客戶端進行數據操作(CRUD)的時候,Amplify都會通過Data 組件把用戶本地的數據先進行修改(Model DataBase),在修改數據的同時,會把每一次CRUD操作進行序列化,存儲在另一個Mutation數據庫裡面。

Amplify Android SDK的Engine組件通過Observer模式,註冊了一個數據源變化的觀察者,如果有新的Mutation,Engine就會從Mutation數據庫將Mutation取出併發送到API組件,API組件再將其封裝成一個GraphQL的Mutation 請求發送至後端

圖中的左邊的區域為客戶端,右邊為後端

有了組件通信圖,描述架構就變成了看圖說話,小學四年級咱就學過了,非常輕鬆!

從以上的圖和描述中,我們隊友們就應該知道,數據存儲在Sqlite 數據庫內,同時保存了數據本身和對數據操作的序列化對象,並且他們也會有更多的問題,比如說

  1. 既然有Model數據庫,我們怎麼定義客戶端的model,model長啥樣,是Amplify有工具自動生成,還是必須我們手寫?
  2. 既然是先寫入數據庫再和服務端更新,萬一網絡連接暫時不可用怎麼辦?Amplify怎麼處理數據不一致?

這些都是文檔閱讀者在閱讀完你寫的簡明易懂的架構簡介之後會問的問題,是一個順其自然的事情,當他們問到這裡的時候,你應該感到高興而不是緊張害怕,因為這說明大家把你的文檔讀進去了,而不是敷衍和不耐煩。能讓閱讀者和作者產生互動的技術文檔,是好文檔!

有了架構圖,再加入一個流程圖,就更棒了。

這裡我會用 www.plantuml.com/ 作為示範工具來構建流程圖。

還是以Amplify為例子。既然我們決定使用Amplify了,那使用Amplify前後我們的代碼和架構會發生很大的變化麼?

假如我們的產品是一款點餐的軟件,我們的Model(數據模型)是一道一道的菜,同時菜本身可以修改相應的元數據,比如辣的程度,是否加入了配菜等等。每當我們把菜加入到購物車的時候,不同設備同一賬號的軟件的購物車應該出現相同的菜品。

在使用Amplify之前,我們都是手動存入自己設置好的數據庫,然後馬上發送給服務端,來更新購物車的。

Android開發:除了編碼以外,這個技能也很重要

在使用Amplify之後,我們不需要存進自己的數據庫了,而是直接面向Amplify的Model編程

Android開發:除了編碼以外,這個技能也很重要

如果大家覺得對比還明顯,咱再來一個一刀切式的對比,把兩幅圖放在一起,再使用中間切割的方式:

Android開發:除了編碼以外,這個技能也很重要

通過上面這個對比圖,閱讀者可以很清晰的看到,在現有的設計中,我們完全沒有修改Adapter和View之間的通信方式和流程順序,僅僅是修改了Adapter和數據源的操作,從原來的Adapter修改本地數據庫和發送網絡請求兩手一把抓,變成了現在僅需向Amplify SDK修改模型Model數據。

配上組件通信圖和流程圖,可以讓你的文檔不只是有枯燥的文字,使閱讀者有更大的想象空間,加上和原有架構的對比,高級工程師看了也會直呼你是老司機。

Android開發:除了編碼以外,這個技能也很重要

七、開發時間線

開發時間線一般需要和產品經理協商,但是一個很重要的小技巧是,當你設計你的開發時間線的時候,最好是通過功能/產品發版的時間進行倒推,算時間線。

比如,我要2020年10月一號正式發版,那麼假設我們Beta內測需要兩個周,Beta bug修復一個周,QA測試內測版release兩個周,那麼我們開發的Code Freeze日期就定下來,大概是八月26號左右。有了Code Freeze日期,設計,開發週期就有了:

Android開發:除了編碼以外,這個技能也很重要

八、開發時間線

有時候,對於同一個項目,同一個功能,還有其他的第三方類庫或者結構可用,那麼最好也要列舉出來,同時比如各自的優劣勢,這是給你選用的架構的很好的背書。

這裡就不列舉例子了。這個環節也可以參考之前講過的架構描述方法。

九、參考文獻

是的,雖然咱不是寫論文,但是肯定多多少少有引用到一些文章,技術博客,哪怕是第三方類庫的官方簡介,也都要放在文末,以供其他組員參考。同時這也是一個霸氣的結尾,"老子調研的這麼辛苦這麼盡責,看了這麼多文獻,你好意思反對麼?",此時無聲勝有聲。。。。。

十、最後

其實寫文檔就像寫作文一樣,是一件非常消耗時間,並且需要積累的過程。

我記得大二考雅思和GRE的時候,寫作都是拿最低分的一part,當時的大學英語老師和我說,寫作就是一個輸入和輸出的關係,你需要有100%的輸入,才可能有10%的輸出。

要看很多,練很多,才可能有你練和看的那10%的成果,是一件非常辛苦的事情。但是在公司裡面,寫好了文檔真的是一件對職業發展非常有利的事情。

在谷歌的朋友曾經和我說,他在Android Support Libray組工作(現在是AndroidX了),因為support library太複雜,而且需要很強的backward compatible(向後兼任)的設計,所以經常性的是改幾行代碼,寫1000字的文檔和申請,在谷歌,寫文檔成了高級工程師常規操作,我相信大部分大廠也都是一樣的。

所以希望大家都能多寫,多練,多拿反饋,不要害怕一開始被人批評或者吐槽,這些都是你的墊腳石。

最後,我也分享一份大佬收錄整理的Android學習PDF+架構視頻+面試文檔+源碼筆記高級架構技術進階腦圖、Android開發面試專題資料,高級進階架構資料

這些都是我現在閒暇還會反覆翻閱的精品資料。裡面對近幾年的大廠面試高頻知識點都有詳細的講解。相信可以有效的幫助大家掌握知識、理解原理。

當然你也可以拿去查漏補缺,提升自身的競爭力。

如果你有需要的,只需私信我【進階】即可獲取

喜歡本文的話,不妨順手給我點個贊、評論區留言或者轉發支持一下唄~

Android開發:除了編碼以外,這個技能也很重要

Android開發:除了編碼以外,這個技能也很重要

Android開發:除了編碼以外,這個技能也很重要

Android開發:除了編碼以外,這個技能也很重要


分享到:


相關文章: