感謝原創(chuàng)作者:leowudev 寫的很好泽疆,轉來記錄
http://www.reibang.com/p/3053054bd9da
寫在前面
作為一個程序員即供,閱讀大牛們優(yōu)秀的開源項目源碼是一個提升個人編程能力、擴展思維的重要途徑于微。在實際工作中逗嫡,相信并不是所有人接手的項目代碼都很優(yōu)雅和優(yōu)秀青自,而且很大可能因為歷史遺留、趕進度等原因驱证,導致代碼冗余延窜、模塊耦合嚴重、擴展性差和兼容性差等等抹锄, 這就有可能導致在工作中無法使個人能力得到很好的提高逆瑞,并且會導致個人的思維和眼界有所局限。
其實一種想法的實現(xiàn)往往是多種的伙单,而欠缺能力的人往往采用簡單粗暴的方式获高,另一方面,而有能力的人總能使用優(yōu)雅的方式吻育,盡可能考慮各種可能的需求變動念秧、適應各種使用途徑和場景、想到未來擴展的方式來實現(xiàn)布疼。
優(yōu)秀的開源項目正是這種有能力的人用優(yōu)雅方式實現(xiàn)想法的結晶摊趾!所以,閱讀優(yōu)秀的開源項目對個人編程的思考方式游两、知識擴展都是非常非常有幫助的砾层。
作為經常閱讀別人的優(yōu)秀開源項目的人,想給大家分享下我的閱讀經驗贱案,希望能對大家有所幫助~
正文
下面將通過我最近閱讀的奇虎360的開源項目 Replugin 作為例子肛炮,說說我閱讀源碼的方法。
1.尋找驅動力
當你開始閱讀開源項目首先你得有目的性宝踪,工作需要侨糟?個人學習?這都是很好的驅動力肴沫。
沒驅動力是很難堅持的粟害,特別是開源項目涉及到很多你不怎么了解的知識點蕴忆,很容易會覺得枯燥颤芬、晦澀。畢竟閱讀別人的代碼并不是一件快樂的事情套鹅,我們很難去完成理解代碼作者當時的思路和想法站蝠,這個過程是很痛苦的。但如果你有目標卓鹿、有意圖地去閱讀菱魔,就能在一定程度上減少這痛苦。每天給自己打下雞血吟孙,未來的你等下會為這份堅持感到驕傲澜倦!
2.瀏覽官方文檔聚蝶,對開源項目的功能、架構有大概的印象
好了藻治,有了驅動力碘勉,先別急,看看官方文檔桩卵,看看這個項目能完成什么事情和不能完成什么事情验靡,還有官方對這個項目的定位。例如 Replugin 寫得滿滿的十幾頁的 wiki 雏节,官方定位:
RePlugin是一套完整的胜嗓、穩(wěn)定的、適合全面使用的钩乍,占坑類插件化方案辞州。
完整的:讓插件運行起來“像單品那樣”,支持大部分特性
穩(wěn)定的:如此靈活完整的情況下件蚕,其框架崩潰率僅為業(yè)內很低的“萬分之一”
適合全面使用的:其目的是讓應用內的“所有功能皆為插件”
占坑類:以穩(wěn)定為前提的Manifest占坑思路
插件化方案:基于Android原生API和語言來開發(fā)孙技,充分利用原生特性
可以看到,wiki很詳細地介紹了Replugin定位和優(yōu)點排作,這時相信對技術有追求的人都會冒出一個疑問:“他們如何做到的G@病?” 這又大大激起了你的好奇心妄痪,讓你更有動力堅持下去哈雏。
很多人急功近利,馬上就開始源碼閱讀之旅了衫生,包括我裳瘪。但經過多個項目源碼的閱讀的我,會告訴你罪针,別急彭羹!我們還需要知道它怎么用。
3.在工作中或實踐中使用開源項目
本節(jié)講的不是怎么使用泪酱,這官網文檔肯定會有說明的派殷,而是講為什么使用。一個東西你連使用都不會墓阀,就想去了解他的原理毡惜?就像你開車都不會,就去了解剎車怎么把車停下來斯撮。而事實是你開車都不會经伙,你可能都分不清剎車、離合和油門是哪個跟哪個勿锅,這就去了解原理帕膜,往往會迷失方向枣氧。
所以,閱讀前先使用吧垮刹。但我不建議在實際工作項目中立刻使用作瞄,因為你原理都不清楚,有問題不好排查危纫,會影響線上用戶宗挥,這就很糟糕了。我建議的是看官方demo种蝶,然后在自己的個人練手項目中使用契耿。當對項目的使用有一定地理解了,ok螃征,可以走下一步了搪桂。
4.網上搜索針對該開源項目進行分析的優(yōu)秀文章
一個優(yōu)秀的開源項目總是有很多人閱讀并分析,然后整理寫出總結文章盯滚。既然前人都幫我們分析好了踢械,我們?yōu)槭裁床徽驹谇叭说募绨蛏侠^續(xù)往上爬,這樣就省了從腳到肩膀的力氣了魄藕。但要注意我的字眼内列,是“優(yōu)秀”的文章!現(xiàn)在很多人都寫博客背率,很多都是潦潦而談话瞧,只能說是筆記,而非總結寝姿。
像 Replugin 這樣一個“巨型”的開源項目交排,老實說,對我這種菜雞來說饵筑,很多知識點都只是略知一二埃篓,例如多進程通信、gradle編譯腳本等根资,在實際工作中很少接觸的難免會覺得難懂架专。另外,官方文檔往往不會對實現(xiàn)細節(jié)講得很細嫂冻,這時胶征,看前人的分析就很有必要了塞椎。這樣可以讓你對項目的實現(xiàn)有一定地了解桨仿,當你自己看時,你能很快懂得作者這樣做的意圖案狠。
當然服傍,如果你不想看別人的分析總結也未必不可钱雷,可能在自己閱讀過程中多點磕磕碰碰,但你總不能跳過下一步吹零!
5.對開源項目提出自己的疑問
前面做了這么多準備罩抗,你總會產生疑問吧。什么灿椅?沒有套蒂!好吧,這開源項目對你來說太簡單茫蛹,已經不值得你一讀了操刀。帶著疑問去閱讀是我認為最高效的閱讀方式,當你有了目的婴洼,而不至于在閱讀過程中迷失了方向骨坑,并且在閱讀過程中針對性的看。對一個開源項目的疑問一般可以從以下方向提出:
- 這塊功能為什么這么做柬采?有什么好處欢唾?
- 有沒有另外一種實現(xiàn)方式?
- 我缺少哪些知識會阻礙我看源碼(需要去補)粉捻?
例如我在閱讀 Replugin 之前提出了幾個疑問:
- 如何做到一處hook礁遣?借助gradle?
- 查找坑位策略肩刃?如何替換真正的啟動組件亡脸?
- 為什么需要聲明這么多坑位?
- 為什么不用注入Service树酪?
好了浅碾,當你有了好奇心、驅動力续语、目的垂谢,你已經準備好了。但開始閱讀前還有一件事情先搞定:編譯源碼疮茄。
6.把開源項目下載到本地滥朱,并導入IDE,方便調試力试、測試
工欲善其事徙邻,必先利其器。沒有一個好的調試環(huán)境怎么能順心地看源碼畸裳。但幸虧GitHub讓我們能簡單地把源碼download或clone下來缰犁,很多情況都是直接用IDE打開項目就搞定了。但也有像 Replugin 一樣的,分為多個項目帅容,每個項目都是單獨編譯的颇象,這樣我們就無法只打開一個窗口來調試,很不爽并徘。這時就需要點導入技巧來搞定了遣钳。這里不打算講了,需要點篇幅麦乞,留到后面我寫Replugin閱讀總結時講蕴茴。
當然,我們還可以借助一些小技巧來幫忙我們快速閱讀源碼姐直。例如我會建一個能打印方法調用堆棧的Log工具類荐开,在一些核心的部分打印log,看下它的調用堆棧:
package com.leo;
import android.util.Log;
public class Logger {
private final static boolean showTrace = true;
private final static boolean onlyShowTopStack = false;
// 過濾的方法堆棧
private final static String[] FILTER_TRACE_PREFIX = {
"java.lang",
"android",
"com.android",
"com.leo.Logger" // 過濾掉自己
};
public static void i(Object obj) {
StringBuilder msg = new StringBuilder();
if (showTrace) {
StackTraceElement[] stes = Thread.currentThread().getStackTrace();
for (int i = stes.length - 1, indent = 0; i > 0; i--) {
boolean needFilter = false;
String clsName = stes[i].getClassName();
for (String filter : FILTER_TRACE_PREFIX) {
if (clsName.startsWith(filter)) {
needFilter = true;
break;
}
}
if (needFilter) continue;
if (i != stes.length - 1)
msg.append(indent(indent)).append("-> ");
msg.append(stes[i].getClassName() + "#" + stes[i].getMethodName() + "\n");
indent++;
}
}
msg.append(" >>> " + obj + "\n");
String showMsg = msg.toString();
if (onlyShowTopStack) {
String[] trace = showMsg.split("->");
showMsg = trace[trace.length - 1];
showMsg = showMsg.replace("->", "")
.replace("\n", "")
.trim();
}
Log.i("TEST", showMsg);
}
private static String indent(int i) {
if (i < 0) {
i = 0;
}
StringBuilder ret = new StringBuilder();
for (int n = 0; n < i; n++) {
ret.append(" ");
}
return ret.toString();
}
}
到這里简肴,你已經全部準備就緒晃听,戰(zhàn)爭一觸即發(fā)!開始 READ THE FUCKING SOURCE CODE 吧!
7.帶著疑問閱讀源碼
戰(zhàn)爭打響砰识,在充滿迷霧的大海中能扒,我方對敵人的方位還不甚了解,但不怕辫狼,我們的指北針 —— 疑問 —— 會帶領我們直達敵方腹地初斑,我們終會揭開它的露出廬山真面目。
開源項目往往是龐大而復雜的膨处,我們在閱讀過程中真的非常容易會糾結于細節(jié)见秤,而導致閱讀混亂,迷失了方向真椿,這對閱讀的動力打擊很沉重的鹃答,往往會使人放棄。
而有了疑問就不同了突硝,你知道自己為何要看测摔,你會思考,會有自己的目的解恰,不拘泥于細節(jié)實現(xiàn)锋八,能準確地找到源碼的核心實現(xiàn)。
對于糾結細節(jié)是很多人在閱讀源碼犯的錯誤护盈,有些細節(jié)我們根本不需要去搞清楚它怎么做的挟纱,知道它做什么就可以了。一些具體的實現(xiàn)可以放到當你使用過程中遇到問題腐宋,或者對該具體實現(xiàn)產生另一個疑問時才去深究紊服,也就是說檀轨,還是帶著疑問閱讀代碼。因為一個開源項目往往是多個優(yōu)秀的人花了很多時間寫出來的結晶围苫,你想在短時間內把它完成消化,是不科學的撤师。我們專注于最感興趣的剂府、最有參考價值和最核心的部分就可以了。
8.閱讀源碼過程中多添加注釋剃盾、多做筆記
我得承認腺占,我的記憶力不好,而我也不信我的記憶痒谴。好記性不如爛筆頭衰伯,記憶終將遺忘,但所做的筆記除非被銷毀积蔚,否則永遠都會在那里意鲸,等著你去翻閱回顧。
我們把整個項目都下載下來了尽爆,首先當然是在閱讀源碼過程中添加下自己的注釋了怎顾,寫下自己的理解、疑惑漱贱,或者標記下值得借鑒參考的實現(xiàn)等等槐雾。另外,我們還需要做些簡單的總結筆記幅狮∧记浚可以紙質或者網上很多的筆記類應用。對于我這種無法直視自己的手寫字的崇摄,更傾向于用筆記類應用擎值,這也是我推薦大家用的,多端同步逐抑,不能再省心幅恋。
9.做閱讀總結,吸收和再創(chuàng)造
當你對開源項目閱讀到一定程度了泵肄,對該項目有了深刻的理解捆交,并有了自己的見解,你是不是有話要說腐巢?別憋著了品追,講出來吧!跟大家分享冯丙!寫篇博客總結下閱讀經驗肉瓦、心得和成長等等遭京,既能加深自己的印象,又能幫助到他人泞莉,何樂而不為呢哪雕?!
閱讀開源項目我們最終的目的是把其涉及到的知識點和設計實現(xiàn)思路吸收鲫趁,并且轉化為自己的功力斯嚎。這個轉化不是說你閱讀完了就轉化成功了,往往閱讀是不夠的挨厚,你還需要實踐堡僻。
例如喜歡打球的我深知看NBA球星在球場上各種變向戲耍對手,對我的過人能力幾乎沒任何幫助疫剃,只是讓我知道:“原來還能這么做呀钉疫!” 我還得自己去球場一招一式的練習,反復練習巢价,或者我根據我的身體條件牲阁,做些簡單的變種,直到這招轉化為我的肌肉記憶壤躲,我才能在比賽中自然而然地使用出來咨油。
所以我提倡再創(chuàng)造。所謂再創(chuàng)造不是讓你重復造輪子柒爵,而是能根據自己的工作需求役电,把開源項目應用到工作中。這里的應用不一定是直接引用開源項目來使用棉胀,我是不建議這么做的法瑟,因為開源項目往往考慮全面,考慮到非常多的情況的唁奢,而你項目根本不存在這樣的情況霎挟,這就是浪費。所以我建議的是:根據自己工作的需求麻掸,把開源項目的核心實現(xiàn)抽取出來酥夭,轉化為能滿足自己需求的庫來使用。
而這個抽取的過程就是吸收的過程脊奋。在這個過程你遇到的問題并解決熬北,會使你對開源項目有更深刻的理解。這個過程如果你對開源項目的某個實現(xiàn)不太認同诚隙,可以嘗試改為自己的實現(xiàn)讶隐,這就是吸收。
在寫最后
非常感謝看到這里的童鞋久又,畢竟這些經驗談沒什么干貨巫延,能耐心讀到這里真的非常感謝效五!我們來總結一波閱讀源碼的步驟:
- 尋找驅動力
- 瀏覽官方文檔,對開源項目的功能炉峰、架構有大概的印象
- 在工作中或實踐中使用開源項目
- 網上搜索針對該開源項目進行分析的優(yōu)秀文章
- 對開源項目提出自己的疑問
- 把開源項目下載到本地畏妖,并導入IDE,方便調試疼阔、測試
- 帶著疑問閱讀源碼
- 閱讀源碼過程中多添加注釋戒劫、多做筆記
- 做閱讀總結,吸收和再創(chuàng)造
以上步驟有些可以根據實際情況跳過竿开,程序員都是聰明人谱仪,總也會隨機應變~