一司澎、前言
代碼規(guī)范是我們每個(gè)程序員要做的事物独,假設(shè)我們按照自己的喜好來(lái)寫代碼房揭,那么很可能出現(xiàn)的問(wèn)題就是我看不懂你的代碼或者你看不懂我的代碼备闲,這樣會(huì)給后續(xù)維護(hù)形成巨大的障礙。這個(gè)時(shí)候問(wèn)題來(lái)了捅暴,如何讓代碼不分你我恬砂,或許只需要一套規(guī)則,你和我都認(rèn)可并且遵守的代碼規(guī)范守則蓬痒。
那么你的疑問(wèn)可能又來(lái)了泻骤,怎么樣才能算好的代碼規(guī)范,答案只有一個(gè)乳幸,真正好的代碼規(guī)范就是別人的代碼你一眼就能看懂瞪讼,更不需要反復(fù)去看。之所以這樣并不是因?yàn)榭吹娜?Review 代碼的能力有多強(qiáng)粹断,而是寫代碼的人愿意遵守規(guī)則符欠,他知道自己想這么寫,但是知道你會(huì)看不懂瓶埋,所以換了一種方式來(lái)寫希柿,這種方式就是代碼規(guī)范。
代碼規(guī)范:一個(gè)好的代碼規(guī)范可以幫助我們快速了解和熟悉相關(guān)的業(yè)務(wù)养筒,降低后續(xù)維護(hù)的成本(二次開發(fā)或者排查問(wèn)題)曾撤。
代碼不規(guī)范:代碼不規(guī)范會(huì)導(dǎo)致項(xiàng)目可讀性變差,具體表現(xiàn)為難分辨和難理解晕粪,在無(wú)形之中加大項(xiàng)目后續(xù)維護(hù)的成本挤悉。
經(jīng)驗(yàn)總結(jié):編碼不規(guī)范,同行淚兩行
二巫湘、常規(guī)規(guī)范
使用0px代替0dp装悲,這樣就可以在獲取時(shí)避免系統(tǒng)進(jìn)行換算,提升代碼的執(zhí)行效率尚氛。
字符串比較诀诊,應(yīng)該用"xxx".equals(object),而不應(yīng)該用object.equals("xxx")阅嘶,因?yàn)閛bject對(duì)象可能為空属瓣,我們應(yīng)該把不為空的條件放置在表達(dá)式的前面载迄。
盡量采用switch case來(lái)判斷,如果不能實(shí)現(xiàn)則再考慮用if else抡蛙,因?yàn)樵诙鄺l件下使用switch case語(yǔ)句判斷會(huì)更加簡(jiǎn)潔护昧。
不推薦用layout_marginLeft,而應(yīng)該用layout_marginStart溜畅;不推薦用layout_marginRight捏卓,而應(yīng)該用layout_marginEnd极祸,原因有兩個(gè)慈格,一個(gè)是適配 Android 4.4反方向特性(可在開發(fā)者選項(xiàng)中開啟),第二個(gè)是 XML 布局中使用layout_marginLeft和layout_marginRight會(huì)有代碼警告遥金,padding屬性也是同理浴捆,這里不再贅述。
如果在layout_marginStart和layout_marginEnd的值相同的情況下稿械,請(qǐng)?zhí)鎿Q使用layout_marginHorizontal选泻,而layout_marginTop和layout_marginBottom也同理,請(qǐng)?zhí)鎿Q使用layout_marginVertical美莫,能用一句代碼能做的事不要寫兩句页眯,padding屬性也是同理,這里不再贅述厢呵。
過(guò)期和高版本的 API 一定要做對(duì)應(yīng)的兼容(包含 Java 代碼和 XML 屬性)窝撵,如果不需要兼容處理的,需要對(duì)警告進(jìn)行抑制襟铭。
在能滿足需求的情況下碌奉,盡量用invisible來(lái)代替gone,因?yàn)間one會(huì)觸發(fā)當(dāng)前整個(gè) View 樹進(jìn)行重新測(cè)量和繪制寒砖。
api和implementation赐劣,在能滿足使用的情況下,優(yōu)先選用implementation哩都,因?yàn)檫@樣可以減少一些編譯時(shí)間魁兼。
ListView和RecyclerView都能實(shí)現(xiàn)需求的前提下,優(yōu)先選用RecyclerView漠嵌,具體原因不解釋咐汞,大家應(yīng)該都懂。
ScrollView和NestedScrollView都能實(shí)現(xiàn)需求的前提下献雅,優(yōu)先選用NestedScrollView碉考,是因?yàn)镹estedScrollView和RecyclerView支持相互嵌套,而ScrollView是不支持嵌套滾動(dòng)的挺身。
不能在項(xiàng)目中創(chuàng)建副本文件侯谁,例如創(chuàng)建HomeActivity2.java、home_activity_v2.xml類似的副本文件,因?yàn)檫@樣不僅會(huì)增加項(xiàng)目的維護(hù)難度墙贱,同時(shí)對(duì)編譯速度也會(huì)造成一定的影響热芹,正確的做法應(yīng)該是在原有的文件基礎(chǔ)上面修改,如果出現(xiàn)需求變更的情況惨撇,請(qǐng)直接使用Git或者SVN進(jìn)行版本回退伊脓。
如果一個(gè)類不需要被繼承,請(qǐng)直接用final進(jìn)行修飾魁衙,如果一個(gè)字段在類初始化過(guò)程中已經(jīng)賦值并且沒(méi)有地方進(jìn)行二次賦值报腔,也應(yīng)當(dāng)用final修飾,如果一個(gè)字段不需要被外部訪問(wèn)剖淀,那么需要用private進(jìn)行修飾纯蛾。
每個(gè)小組成員應(yīng)當(dāng)安裝阿里巴巴代碼約束插件,并及時(shí)地對(duì)插件所提示的代碼警告進(jìn)行處理或者抑制警告纵隔。
應(yīng)用圖標(biāo)應(yīng)該放在mipmap目錄下翻诉,其他圖片資源應(yīng)當(dāng)放到drawable目錄下,具體原因可以看谷歌官方文檔對(duì)這兩個(gè)文件夾給出的介紹:
三捌刮、后臺(tái)接口規(guī)范
后臺(tái)返回的id 值碰煌,不要使用int或者long類型來(lái)接收,而應(yīng)該用string類型來(lái)接收绅作,因?yàn)槲覀儾恍枰獙?duì)這個(gè)id 值進(jìn)行運(yùn)算芦圾,所以我們不需要關(guān)心它是什么類型的。
后臺(tái)返回的金額數(shù)值應(yīng)該使用String來(lái)接收棚蓄,而不能用浮點(diǎn)數(shù)來(lái)接收堕扶,因?yàn)閒loat或者double在數(shù)值比較大的時(shí)候會(huì)容易丟失精度,并且還需要自己手動(dòng)轉(zhuǎn)換出想要保留的小數(shù)位梭依,最好的方式是后臺(tái)返回什么前端就展示什么稍算,而到了運(yùn)算的時(shí)候,則應(yīng)該用BigDecimal類來(lái)進(jìn)行轉(zhuǎn)換和計(jì)算役拴,當(dāng)然金額在前端一般展示居多糊探,運(yùn)算的情況還算是比較少的。
我們?cè)诙x后臺(tái)返回的 Bean 類時(shí)河闰,不應(yīng)當(dāng)將一些我們沒(méi)有使用到的字段添加到代碼中科平,因?yàn)檫@樣會(huì)消耗性能,因?yàn)?Gson 是通過(guò)反射將后臺(tái)字段賦值到 Java 字段中姜性,所以我們應(yīng)當(dāng)避免一些不必要的字段解析瞪慧,另外臃余的字段也會(huì)給我們排查問(wèn)題造成一定的阻礙。
如果后臺(tái)給定的字段名不符合代碼命名的時(shí)候部念,例如當(dāng)遇到student_name這種命名時(shí)弃酌,我們應(yīng)當(dāng)使用 Gson 框架中的@SerializedName注解進(jìn)行重命名氨菇。
請(qǐng)求的接口參數(shù)和返回字段必須要寫上注釋,除此之外還應(yīng)該備注對(duì)應(yīng)的后臺(tái)接口文檔地址妓湘,以便我們后續(xù)能夠更好地進(jìn)行維護(hù)和迭代查蓉。
后臺(tái)返回的 Bean 類字段不能直接訪問(wèn),而應(yīng)該通過(guò)生成Get方法榜贴,然后使用這個(gè)Get方法來(lái)訪問(wèn)字段豌研。
接口請(qǐng)求成功的提示可以不顯示,但請(qǐng)求失敗的提示需要顯示給到用戶唬党,否則會(huì)加大排查問(wèn)題的難度鹃共,也極有可能會(huì)把問(wèn)題掩蓋掉,從而導(dǎo)致問(wèn)題遺留到線上去初嘹。
四及汉、變量命名規(guī)范
嚴(yán)禁使用中文或者中文拼音進(jìn)行重命名
使用駝峰式命名風(fēng)格(單詞最好控制在三個(gè)以內(nèi))
局部變量或者公開的成員變量應(yīng)該以作用來(lái)命名,例如:
publicStringname;publicTextView nameView;publicFrameLayout nameLayout;
//命名規(guī)范附帶技巧(當(dāng)布局中同個(gè)類型的控件只有一個(gè)的時(shí)候屯烦,也可以這樣命名)
publicTextView textView;publicRecyclerView recyclerView;
非公開的成員變量必須以小m開頭,例如:
privateStringmName;privateTextView mNameView;privateFrameLayout mNameLayout;
//命名規(guī)范附帶技巧(當(dāng)布局中同個(gè)類型的控件只有一個(gè)的時(shí)候房铭,也可以這樣命名)
privateTextView mTextView;privateRecyclerView mRecyclerView;
布爾值命名規(guī)范驻龟,無(wú)論是局部變量還是成員變量,都不應(yīng)該攜帶is缸匪,例如:
// 不規(guī)范寫法示例privatebooleanmIsDebug;booleanisDebug;// 規(guī)范寫法示例privatebooleanmDebug;booleandebug;
靜態(tài)變量則用小s開頭翁狐,例如:
staticHandlersHandler;
常量則需要用大寫,并且用下劃線代替駝峰凌蔬,例如:
staticfinalStringREQUEST_INSTALL_PACKAGES;
有細(xì)心的同學(xué)可能會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題露懒,為什么我們最常用的 Glide 和 OkHttp 的源碼中,非公開的成員變量為什么沒(méi)有用小m開頭砂心?但是谷歌的 SDK 源碼和 Support 庫(kù)就有呢懈词?那究竟是用還是不用呢?這個(gè)問(wèn)題其實(shí)很好回答辩诞,我們可以先從體量上分析坎弯,首先谷歌的開發(fā)人員和項(xiàng)目數(shù)量肯定是最多的,那么谷歌在這塊的探索和研究肯定是多于 Glie 和 OkHttp 的译暂,其次是 Glide 和 OkHttp 的源碼都有一個(gè)特點(diǎn)抠忘,很多類都維持在 1k 行代碼左右,而谷歌源碼很多類都接近 10k 行代碼外永,例如 Activity 的源碼在 API 30 上面有 8.8k 行代碼崎脉,所以谷歌在這塊略勝一籌,如果非要二選一伯顶,我選擇谷歌的代碼風(fēng)格囚灼,并不是說(shuō) Glide 和 OkHttp 命名風(fēng)格不好呛踊,是因?yàn)榛蛟S在未來(lái)的某一天,可能會(huì)有新的圖片請(qǐng)求框架和網(wǎng)絡(luò)請(qǐng)求框架來(lái)代替 Glide 和 OkHttp啦撮,但是基本不可能會(huì)出現(xiàn)有代替 Android SDK 或者 Support 庫(kù)的一天谭网。
最后讓我們靜靜地欣賞一下Activity類中成員變量的命名:
publicclassActivity{
publicstaticfinalintRESULT_CANCELED=0;
publicstaticfinalintRESULT_OK=-1;
privateInstrumentationmInstrumentation;
privateIBindermToken;
privateIBindermAssistToken;
privateApplicationmApplication;
/*package*/IntentmIntent;
/*package*/StringmReferrer;
privateComponentNamemComponent;
/*package*/ActivityInfomActivityInfo;
/*package*/ActivityThreadmMainThread;
ActivitymParent;
booleanmCalled;
/*package*/booleanmResumed;
/*package*/booleanmStopped;
booleanmFinished;
booleanmStartedActivity;
privatebooleanmDestroyed;
privatebooleanmDoReportFullyDrawn=true;
privatebooleanmRestoredFromBundle;
privatefinalArrayList
newArrayList();
privateWindowmWindow;
privateWindowManagermWindowManager;
privateCharSequencemTitle;
privateintmTitleColor=0;
finalHandlermHandler=newHandler();
finalFragmentControllermFragments=FragmentController.createController(newHostCallbacks());
}
五、包名命名規(guī)范
不允許包名中攜帶英文大寫
包名應(yīng)該以簡(jiǎn)潔的方式命名
包名要按照模塊或者作用來(lái)劃分
請(qǐng)不要在某一包名下放置一些無(wú)關(guān)的類
六赃春、方法命名規(guī)范
initXX:初始化相關(guān)方法愉择,使用init為前綴標(biāo)識(shí),如初始化布局initView
isXX:方法返回值為 boolean 型的請(qǐng)使用is或check為前綴標(biāo)識(shí)
getXX:返回某個(gè)值的方法织中,使用get為前綴標(biāo)識(shí)锥涕,例如getName
setXX:設(shè)置某個(gè)屬性值,使用set為前綴標(biāo)識(shí)狭吼,例如setName
handleXX/processXX:對(duì)數(shù)據(jù)進(jìn)行處理的方法层坠,例如handleMessage
displayXX/showXX:彈出提示框和提示信息,例如showDialog
updateXX:更新某個(gè)東西刁笙,例如updateData
saveXX:保存某個(gè)東西破花,例如saveData
resetXX:重置某個(gè)東西,例如resetData
clearXX:清除某個(gè)東西疲吸,例如clearData
removeXX:移除數(shù)據(jù)或者視圖等座每,例如removeView
drawXX:繪制數(shù)據(jù)或效果相關(guān)的,使用draw前綴標(biāo)識(shí)摘悴,例如drawText
七峭梳、類文件命名規(guī)范
業(yè)務(wù)模塊:請(qǐng)以模塊 + 類型來(lái)命名,例如:
HomeActivity.javaSettingFragment.javaHomeAdapter.javaAddressDialog.java
技術(shù)模塊:請(qǐng)以類的作用來(lái)命名蹂喻,例如:例如
CrashHandler.javaGridSpaceDecoration.javaPickerLayoutManager.java
八葱椭、接口文件命名規(guī)范
如果是監(jiān)聽事件可以參考View的寫法及命名:
publicclassView{privateView.OnClickListener mListener;publicvoidsetOnClickListener(OnClickListener listener){? ? ? ? mListener = listener;? ? }publicinterfaceOnClickListener{voidonClick(View v);? ? }}
如果是回調(diào)事件可以參考Handler的寫法及命名:
publicclassHandler{publicinterfaceCallback{booleanhandleMessage(Message msg);? ? }}
至于接口寫在內(nèi)部還是外部,具體可以視實(shí)際情況而定口四,如果功能比較龐大孵运,就可以考慮抽取成外部的,只作用在某個(gè)類上的窃祝,則就可以直接寫成內(nèi)部的掐松。
九、接口實(shí)現(xiàn)規(guī)范
一般情況下粪小,我們會(huì)在類中這樣實(shí)現(xiàn)接口大磺,這樣寫的好處是,可以減少對(duì)象的創(chuàng)建探膊,并且代碼也比較美觀杠愧。
publicfinalclassPasswordEditTextextendsEditTextimplementsView.OnTouchListener,View.OnFocusChangeListener,TextWatcher{
publicPasswordEditText(Contextcontext,AttributeSetattrs,intdefStyleAttr) {
super(context, attrs, defStyleAttr);
setOnTouchListener(this);
setOnFocusChangeListener(this);
addTextChangedListener(this);
}
@Override
publicvoidonFocusChange(Viewview,booleanhasFocus) {
......
}
@Override
publicbooleanonTouch(Viewview,MotionEventevent) {
......
}
@Override
publicvoidonTextChanged(CharSequences,intstart,intbefore,intcount) {
......
}
@Override
publicvoidbeforeTextChanged(CharSequences,intstart,intcount,intafter) {
......
}
@Override
publicvoidafterTextChanged(Editables) {
......
}
}
但是有兩個(gè)美中不足的地方,就是在實(shí)現(xiàn)的接口過(guò)多時(shí)逞壁,我們很難分辨是哪個(gè)方法是哪個(gè)接口的流济,這個(gè)時(shí)候可以使用注釋的方式來(lái)解決這個(gè)問(wèn)題锐锣,加上@link還可以幫助我們快速定位接口類在項(xiàng)目中所在的位置;另外一個(gè)是implements修飾符換行的問(wèn)題绳瘟,合理的換行會(huì)使代碼更加簡(jiǎn)單直觀雕憔。
publicfinalclassPasswordEditTextextendsEditText
implementsView.OnTouchListener,
View.OnFocusChangeListener,TextWatcher{
publicPasswordEditText(Contextcontext,AttributeSetattrs,intdefStyleAttr) {
super(context, attrs, defStyleAttr);
setOnTouchListener(this);
setOnFocusChangeListener(this);
addTextChangedListener(this);
}
/**
* {@linkOnFocusChangeListener}
*/
@Override
publicvoidonFocusChange(Viewview,booleanhasFocus) {
......
}
/**
* {@linkOnTouchListener}
*/
@Override
publicbooleanonTouch(Viewview,MotionEventevent) {
......
}
/**
* {@linkTextWatcher}
*/
@Override
publicvoidonTextChanged(CharSequences,intstart,intbefore,intcount) {
......
}
@Override
publicvoidbeforeTextChanged(CharSequences,intstart,intcount,intafter) {
......
}
@Override
publicvoidafterTextChanged(Editables) {
......
}
}
十、異常捕獲規(guī)范
請(qǐng)不要使用此方式捕獲異常糖声,因?yàn)檫@種方式會(huì)把問(wèn)題給隱藏掉斤彼,從而會(huì)加大后續(xù)排查問(wèn)題的難度。
try{? ? Xxx.xxx();}catch(Exceptione) {}
如需捕獲異常蘸泻,請(qǐng)用以下方式進(jìn)行捕獲琉苇,列出具體的異常類型,并在代碼中輸出對(duì)應(yīng)的日志悦施。
//捕獲這個(gè)異常并扇,避免程序崩潰
try{
//目前發(fā)現(xiàn)在 Android 7.1 主線程被阻塞之后彈吐司會(huì)導(dǎo)致崩潰,可使用 Thread.sleep(5000) 進(jìn)行復(fù)現(xiàn)
//查看源碼得知 Google 已經(jīng)在 Android 8.0 已經(jīng)修復(fù)了此問(wèn)題
//主線程阻塞之后 Toast 也會(huì)被阻塞抡诞,Toast 因?yàn)轱@示超時(shí)導(dǎo)致 Window Token 失效
mHandler.handleMessage(msg);
}catch(WindowManager.BadTokenException|IllegalStateExceptione) {
//android.view.WindowManager$BadTokenException:Unable to add window -- token android.os.BinderProxy is not valid; is your activity running?
//java.lang.IllegalStateException:View android.widget.TextView has already been added to the window manager.
e.printStackTrace();
}
如果這個(gè)異常不是通過(guò)方法 throws 關(guān)鍵字拋出穷蛹,則需要在 try 塊中說(shuō)明崩潰的緣由,并注明拋出的異常信息沐绒。
還有一個(gè)問(wèn)題俩莽,有異常就一定要try catch?乔遮,這種想法其實(shí)是錯(cuò)的,例如我們項(xiàng)目用 Glide 加載圖片會(huì)拋出以下異常:
Causedby:java.lang.IllegalArgumentException:Youcannot start a loadfora destroyed activity
atcom.bumptech.glide.manager.RequestManagerRetriever.assertNotDestroyed(RequestManagerRetriever.java:348)
atcom.bumptech.glide.manager.RequestManagerRetriever.get(RequestManagerRetriever.java:148)
atcom.bumptech.glide.Glide.with(Glide.java:826)
這是因?yàn)?Activity 的銷毀了而去加載圖片導(dǎo)致的(場(chǎng)景:異步執(zhí)行圖片加載)取刃,大多人的解決方式可能是:
try{
//Activity 銷毀后執(zhí)行加載圖片會(huì)觸發(fā) crash
Glide.with(this)
.load(url)
.into(mImageView);
}catch(IllegalArgumentExceptione) {
//java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity
e.printStackTrace();
}
雖然這種方式可以解決crash的問(wèn)題蹋肮,但是顯得不夠嚴(yán)謹(jǐn),Glide 拋異常給外層璧疗,其實(shí)無(wú)非就想告訴調(diào)用者坯辩,調(diào)用的時(shí)機(jī)錯(cuò)了,正確的處理方式不是直接捕獲這個(gè)異常崩侠,而是應(yīng)該在外層做好邏輯判斷漆魔,避免會(huì)進(jìn)入出現(xiàn)crash的代碼,正確的處理示例如下:
if(isFinishing() || isDestroyed()) {// Glide:You cannot start a load for a destroyed activityreturn;}Glide.with(this)? ? ? ? .load(url)? ? ? ? .into(mImageView);
所以盡量不要通過(guò)捕獲的方式來(lái)處理異常却音,除非外層真的判斷不了改抡,否則應(yīng)該通過(guò)一些邏輯判斷來(lái)避免進(jìn)入一些會(huì)crash的代碼。
十一系瓢、Activity 跳轉(zhuǎn)約定
應(yīng)當(dāng)將 Intent 中的 key 常量保存到一個(gè)管理類中阿纤,又或者定義在目標(biāo)的 Activity 中
publicclassIntentKey{/** id */publicstaticfinalString ID ="id";/** token */publicstaticfinalString TOKEN ="token";/** 訂單 */publicstaticfinalString ORDER ="order";/** 余額 */publicstaticfinalString BALANCE ="balance";/** 時(shí)間 */publicstaticfinalString TIME ="time";/** URL */publicstaticfinalString URL ="url";/** 路徑 */publicstaticfinalString PATH ="path";/** 其他 */publicstaticfinalString OTHER ="other";? ? .....}
如果跳轉(zhuǎn)的 Activity 需要傳遞參數(shù),應(yīng)該在目標(biāo)的 Activity 中定義靜態(tài)的start又或者newIntent方法
publicfinalclassWebActivityextendsActivity{publicstaticvoidstart(Context context, String url){Intent intent =newIntent(context, WebActivity.class);? ? ? ? intent.putExtra(IntentKey.URL, url);? ? ? ? context.startActivity(intent);? ? }}publicfinalclassWebActivityextendsActivity{publicstaticIntentnewIntent(Context context, String url){Intent intent =newIntent(context, WebActivity.class);? ? ? ? intent.putExtra(IntentKey.URL, url);returnintent;? ? }}
如果創(chuàng)建的 Fragment 需要傳遞參數(shù)夷陋,應(yīng)該在目標(biāo)的 Fragment 中定義靜態(tài)的newInstance方法
publicfinalclassWebFragmentextendsFragment{publicstaticWebFragmentnewInstance(String url){WebFragment fragment =newWebFragment();Bundle bundle =newBundle();? ? ? ? bundle.putString(IntentKey.URL, url);? ? ? ? fragment.setArguments(bundle);returnfragment;? ? }}
如果跳轉(zhuǎn)的 Activity 或者創(chuàng)建的 Fragment 不需要傳任何參數(shù)欠拾,可以不需要定義這些靜態(tài)方法
十二胰锌、第三方框架使用規(guī)范
集成一些第三方框架或者 SDK,必須注明作用和出處藐窄,以便出現(xiàn)問(wèn)題時(shí)能夠快速核查和反饋资昧。
//權(quán)限請(qǐng)求框架:https://github.com/getActivity/XXPermissions
implementation'com.hjq:xxpermissions:9.8'
盡量不要選擇功能兩套相同的框架,應(yīng)當(dāng)引用最合適的一套框架進(jìn)行開發(fā)荆忍。
使用第三方庫(kù)必須要依賴指定的版本號(hào)格带,而不能使用 + 號(hào)來(lái)指定依賴庫(kù)最新的版本號(hào)。
使用第三方開源庫(kù)出現(xiàn)問(wèn)題或者 Bug 時(shí)應(yīng)及時(shí)通知到開源庫(kù)的作者东揣,如果沒(méi)有及時(shí)回復(fù)就根據(jù)實(shí)際情況對(duì)問(wèn)題進(jìn)行修復(fù)践惑。
盡量避免 Copy 第三方庫(kù)的技術(shù)代碼到項(xiàng)目中,特別是在放置到項(xiàng)目業(yè)務(wù)模塊中嘶卧,因?yàn)檫@樣會(huì)增加項(xiàng)目的復(fù)雜度尔觉,從而降低可維護(hù)性。
如果出現(xiàn)問(wèn)題不能找到開源庫(kù)的作者芥吟,如果需要修改侦铜,應(yīng)當(dāng)將這些代碼抽取到單獨(dú)的 Module 中。
能用框架就用成熟框架钟鸵,盡量不要自己編寫或者修改框架钉稍,如果有需要,要對(duì)這塊進(jìn)行嚴(yán)格測(cè)試棺耍。
十三贡未、多模塊規(guī)范
模塊命名規(guī)范:應(yīng)該以簡(jiǎn)單明了的方式來(lái)命名
appbasewidgetumengcoursesocketliveshop
模塊混淆配置:請(qǐng)不要使用proguardFiles語(yǔ)句,而是應(yīng)該使用consumerProguardFiles語(yǔ)句蒙袍,因?yàn)閏onsumerProguardFiles語(yǔ)句會(huì)將混淆規(guī)則和資源代碼一同合并到aar包中俊卤,這樣做的好處在于:在項(xiàng)目編譯時(shí)會(huì)將 aar 包中的混淆規(guī)則合并到主模塊中。
android{defaultConfig{//模塊混淆配置consumerProguardFiles'proguard-xxx.pro'}}
資源前綴限制:我們應(yīng)該在模塊中加入此限制害幅,這樣我們?cè)谀K中添加資源時(shí)消恍,編譯器如果發(fā)現(xiàn)資源名稱前綴不符合規(guī)范,則會(huì)出現(xiàn)代碼警告以现。這樣做的好處在于狠怨,以某一名稱作為前綴,可以有效避免在編譯時(shí)引發(fā)的一些資源合并沖突邑遏。
android {// 資源前綴限制resourcePrefix"xxx_"}
框架版本管理:我們應(yīng)該統(tǒng)一抽取框架的版本到config.gradle文件中:
ext {
android=[compileSdkVersion:28,
minSdkVersion:19,
targetSdkVersion:28,
versionCode:40102,
versionName:"4.1.2",
]
dependencies=[
"appcompat":"androidx.appcompat:appcompat:1.2.0",
"material":"com.google.android.material:material:1.2.0",
]
}
然后在每個(gè)模塊下這樣定義佣赖,這樣做的好處是可以做到版本號(hào)的統(tǒng)一管理。
applyfrom :'../config.gradle'android {compileSdkVersionrootProject.ext.android["compileSdkVersion"]? ? defaultConfig {minSdkVersionrootProject.ext.android["minSdkVersion"]targetSdkVersion rootProject.ext.android["targetSdkVersion"]? ? }}dependencies {implementationrootProject.ext.dependencies["appcompat"]implementation rootProject.ext.dependencies["material"]}
除此之外還有另外一種寫法无宿,我們可以把config.gradle修改成這樣:
android{compileSdkVersion28defaultConfig{minSdkVersion19targetSdkVersion28versionName'4.1.2'versionCode40102}}dependencies{implementation'androidx.appcompat:appcompat:1.2.0'implementation'com.google.android.material:material:1.2.1'}
然后在每個(gè)模塊上添加一句引用即可茵汰,相比上一種寫方法,這種方式更強(qiáng)大孽鸡,因?yàn)樗粌H可以配置版本號(hào)蹂午,還支持統(tǒng)一其他的配置項(xiàng)栏豺。
applyfrom:'../config.gradle'
具體要用哪一種,可以根據(jù)實(shí)際情況而定豆胸,如果項(xiàng)目采用的是組件化奥洼,則可以考慮使用第一種方式,如果項(xiàng)目采用的是模塊化晚胡,則可以考慮使用第二種方式灵奖。
十四、代碼注釋規(guī)范
類注釋規(guī)范:author是創(chuàng)建者(必填項(xiàng))估盘、time是創(chuàng)建時(shí)間(必填項(xiàng))瓷患、desc是類的描述(必填項(xiàng)),doc是文檔地址(非必填)遣妥,github是開源地址(如果項(xiàng)目是開源的則必填擅编,否則不填)
/**
*? ? author : Android 輪子哥
*? ? github : https://github.com/getActivity/XXPermissions
*? ? time? : 2018/06/15
*? ? desc? : 權(quán)限請(qǐng)求實(shí)體類
*? ? doc? ? : https://developer.android.google.cn/reference/android/Manifest.permission?hl=zh_cn
*? ? ? ? ? ? https://developer.android.google.cn/guide/topics/permissions/overview?hl=zh-cn#normal-dangerous
*/
publicfinalclassPermission{
....
}
方法注釋規(guī)范:方法注釋可根據(jù)實(shí)際情況而定
/** * 設(shè)置請(qǐng)求的對(duì)象 * * @param activity? ? ? ? ? 當(dāng)前 Activity,可以傳入棧頂?shù)?Activity */publicstaticXXPermissionswith(FragmentActivity activity){return....;}
字段注釋規(guī)范:根據(jù)字段的作用而定
/** 請(qǐng)求的權(quán)限組 */privatestaticfinalStringREQUEST_PERMISSIONS ="request_permissions";/** 權(quán)限回調(diào)對(duì)象 */privateOnPermissionCallback mCallBack;
變量注釋規(guī)范(如果 API 是比較常見并且容易理解可以不用寫箫踩,如果是復(fù)雜并且羞澀難懂則需要寫上)
// 設(shè)置保留實(shí)例爱态,不會(huì)因?yàn)槠聊环较蚧蚺渲米兓匦聞?chuàng)建fragment.setRetainInstance(true);
注釋什么情況下要寫?什么情況下不用寫境钟?這個(gè)問(wèn)題我很有感觸锦担,代碼注釋寫多了不好,顯得太啰嗦慨削,也會(huì)增加工作量洞渔,寫少了也不好,又怕別人看不懂缚态,也害怕給自己后面留坑痘煤。我個(gè)人的建議是盡量用規(guī)范的命名來(lái)減少不必要的注釋,很多時(shí)候我們只需要換位思考一下猿规,忘記這段代碼是自己寫的,再問(wèn)一下自己能不能一下子讀懂宙橱,如果可以的話姨俩,注釋就可以不用寫,否則注釋還是要考慮寫上师郑。
十五环葵、代碼硬編碼規(guī)范
請(qǐng)盡量避免使用硬編碼,例如系統(tǒng)的一些常量值宝冕,不能直接寫死张遭,而是應(yīng)該通過(guò)代碼引用,例如:
//不規(guī)范寫法示例
if(view.getVisibility()!=0) {
return;
}
Intentintent=newIntent("android.settings.APPLICATION_DETAILS_SETTINGS");
startActivity(intent);
//規(guī)范寫法示例
if(view.getVisibility() != View.VISIBLE) {return;}Intent intent =newIntent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);startActivity(intent);
在項(xiàng)目開發(fā)中地梨,被多次使用到的數(shù)值或者字符串也應(yīng)該提取成常量來(lái)供外部引用菊卷,例如:
publicfinalclassUserInfoManager{/** 學(xué)生 */publicstaticfinalintTYPE_STUDENT =0;/** 老師 */publicstaticfinalintTYPE_TEACHER =1;/** 家長(zhǎng) */publicstaticfinalintTYPE_PATRIARCH =2;}
但并不代表所有的數(shù)值都需要常量化缔恳,有一些數(shù)值常量化的意義并不大,例如:
ValueAnimatoranimator = ValueAnimator.ofInt(0, 100);animator.setDuration(500);animator.start();
所以衡量一個(gè)數(shù)值或者字符串是否進(jìn)行常量化的標(biāo)準(zhǔn)有兩點(diǎn):
這個(gè)數(shù)值或者字符串是否會(huì)被多次使用
這個(gè)數(shù)值或者字符串是否具有一定的含義
十六洁闰、布局文件命名規(guī)范
以模塊 + 類型來(lái)命名歉甚,例如:
home_activity.xmlsetting_fragment.xmlmenu_item.xmladdress_dialog.xml
這樣寫的好處在于,由于 res 文件夾下是沒(méi)有層級(jí)概念的
通過(guò)前綴的命名可以幫助我們更好定位到同一模塊下的資源
例如分享對(duì)話框中扑眉,有對(duì)話框 Root 布局和 Item 布局
share_dialog.xml(Root布局)share_item.xml(Item布局)
十七墅诡、資源文件命名規(guī)范
如果是業(yè)務(wù)模塊下的資源剿骨,以模塊 + 類型來(lái)命名,例如分享對(duì)話框的資源:
share_link_ic.png(復(fù)制鏈接)share_moment_ic.png(分享到朋友圈)share_qq_ic.png(分享到QQ好友)share_qzone_ic.png(分享到QQ空間)share_wechat_ic.png(分享到微信好友)
如果和業(yè)務(wù)模塊不相干的資源,以作用 + 類型來(lái)命名寡润,例如通用的控件樣式資源:
button_rect_selector.xml(通用直角按鈕樣式)button_round_selector.xml(通用圓角按鈕樣式)
這種資源有一個(gè)共同特點(diǎn),它不屬于哪個(gè)模塊兵扬,但是在不同模塊都有用到秒咨,所以不能用業(yè)務(wù)的模塊名作為文件名前綴,最后附上常見類型名稱對(duì)應(yīng)表:
十八计呈、String ID 命名規(guī)范
請(qǐng)以模塊 + 功能來(lái)命名砰诵,例如:
<!--主界面-->
首頁(yè)
發(fā)現(xiàn)
消息
我的
再按一次退出
<!--登錄界面-->
注冊(cè)
請(qǐng)輸入手機(jī)號(hào)
請(qǐng)輸入密碼
忘記密碼?
登錄
其他登錄方式
<!--注冊(cè)界面-->
注冊(cè)
手機(jī)號(hào)僅用于登錄和保護(hù)賬號(hào)安全
登錄
設(shè)置密碼
再次輸入密碼
兩次密碼輸入不一致捌显,請(qǐng)重新輸入
<!--設(shè)置界面-->
設(shè)置
語(yǔ)言切換
簡(jiǎn)體中文
繁體中文
另外有一類 String 被多個(gè)模塊所引用茁彭,需要以common + 作用來(lái)命名,例如:
加載中…</string>確定取消年月日時(shí)分秒
十九扶歪、Color ID 命名規(guī)范
請(qǐng)以模塊 + 含義 + color來(lái)命名理肺,例如:
#FFBBBBBB#FF33B5E5#FF99CC00#FFFFBB33#FFFF4444#FFFFFFFF
但是實(shí)際開發(fā)中,我們常常會(huì)遇到下面這種命名方式:
#color_FF35BF30</color>
但其實(shí)這種命名方式是不規(guī)范的善镰,因?yàn)樗鼘?duì)Color ID的名稱定義比較模糊妹萨,會(huì)容易給別人造成誤導(dǎo);舉個(gè)例子:假設(shè)項(xiàng)目中有200個(gè)地方引用了這個(gè)color_FF35BF30色值炫欺,其中有150地方是你自己引用的乎完,另外50個(gè)地方是別人引用的,但是別人不知道你那個(gè)色值是干什么的品洛,看到你有寫就直接引用了树姨,突然有一天產(chǎn)品經(jīng)理心情不好要改這個(gè)色值,那么你要從200地方區(qū)分150個(gè)需要修改的地方和50個(gè)不需要修改的地方桥状。
二十帽揪、Anim ID 命名規(guī)范
應(yīng)用到某個(gè)模塊View,例如:
login_left_balloon_view.xmllogin_right_balloon_view.xml
應(yīng)用到全局Activity辅斟,例如:
left_in_activity.xmlleft_out_activity.xml
應(yīng)用到全局Dialog转晰,例如:
bottom_in_dialog.xmlbottom_out_dialog.xml
二十一、View ID 命名規(guī)范
應(yīng)該以控件的縮寫 + 模塊名 + 作用來(lái)命名,例如
@+id/R.id.rg_login_type@+id/R.id.et_login_phone@+id/R.id.et_login_sms@+id/R.id.et_login_password@+id/R.id.btn_login_commit
View 和 Layout 控件縮寫表查邢,這里列舉最常見的幾個(gè)
二十二蔗崎、Style 命名規(guī)范
如果只是主題相關(guān)的樣式,以Theme命名結(jié)尾侠坎,控件樣式則以Style命名結(jié)尾蚁趁,命名要求盡量簡(jiǎn)潔,并且需要有代碼注釋实胸,示例如下:
<!-- 應(yīng)用主題樣式 -->? ? .....<!-- 全屏主題樣式 -->? ? .....<!-- 閃屏頁(yè)主題樣式 -->? ? .....<!-- 默認(rèn)圓角按鈕樣式 -->? ? .....<!-- 不帶圓角按鈕樣式 -->? ? .....<!-- 默認(rèn)文本框樣式 -->? ? .....<!-- 驗(yàn)證碼按鈕樣式 -->? ? .....
二十三他嫡、XML 編碼規(guī)范
不推薦用dp作為字體單位,雖然在大部分手機(jī)上面dp和sp計(jì)算是差不多的庐完,但是會(huì)有一部分老年用戶群钢属,例如咱們的長(zhǎng)輩,他們通常會(huì)把手機(jī)顯示的字體大小調(diào)大门躯,這樣他們才不需要帶眼鏡看手機(jī)淆党,如果我們用dp作為字體單位,無(wú)論手機(jī)怎么調(diào)整字體大小讶凉,應(yīng)用的字體大小都不會(huì)有任何的變化染乌,所以這種操作顯然是非常不人性化的。
<!-- 不規(guī)范寫法示例 --><!-- 規(guī)范寫法示例 -->
不能根據(jù)設(shè)計(jì)圖給定的寬高把TextView或者Button的寬高定死懂讯,而是通過(guò)wrap_content和padding的方式來(lái)調(diào)整 View 的寬高荷憋,因?yàn)樵诓煌謾C(jī)上面字體大小不一致,在字體顯示比較小的手機(jī)上面會(huì)顯示正常褐望,但是在字體顯示比較大的平板上面文字上半部分極有可能會(huì)出現(xiàn)被裁剪的情況勒庄,所以我們不能把寬高定死,而是通過(guò)padding來(lái)調(diào)整到控件的大小瘫里。不過(guò)需要注意的是实蔽,TextView 有自帶的文字間距,我們?cè)谀迷O(shè)計(jì)圖給定的padding值時(shí)谨读,需要拿設(shè)計(jì)圖給定的值適當(dāng)減去這一部分值(一般大概是在2~3dp)局装。
<!-- 不規(guī)范寫法示例 --><!-- 規(guī)范寫法示例 -->
ImageView的寬高任一項(xiàng)定義成match_parent時(shí),另外一項(xiàng)不能寫死大小劳殖,而是應(yīng)該使用wrap_content贼邓,否則很可能會(huì)因?yàn)楸壤粚?duì)導(dǎo)致圖片變形,另外還需要使用android:adjustViewBounds="true"屬性闷尿,否則ImageView無(wú)法根據(jù)圖片的寬高來(lái)調(diào)整自己的寬高。
<!-- 不規(guī)范寫法示例 --><!-- 規(guī)范寫法示例 -->
XML 節(jié)點(diǎn)編寫應(yīng)該規(guī)范女坑,在沒(méi)有子節(jié)點(diǎn)的情況下填具,應(yīng)當(dāng)以/>節(jié)點(diǎn)結(jié)尾,如果有則以</xxx.xxx.xxx>節(jié)點(diǎn)結(jié)尾
<!-- 不規(guī)范寫法示例 --><!-- 不規(guī)范寫法示例 --><!-- 規(guī)范寫法示例 --><!-- 規(guī)范寫法示例 -->
二十四、預(yù)覽屬性約定
應(yīng)該在布局文件根布局中定義tools:context屬性劳景,以便在布局文件中快速定位到對(duì)應(yīng)的類
tools:context=".ui.activity.HomeActivity"tools:context=".ui.fragment.SettingFragment"tools:context=".ui.adapter.HomeAdapter"tools:context=".ui.dialog.PersonDataDialog"
此外誉简,tools 屬性還有各種各樣的用途,例如RecyclerView的tools屬性
這種命名方式不止可以應(yīng)用于RecyclerView盟广,還可以應(yīng)用于其他View的屬性闷串,比如常用的TextView和ImageView
如果某個(gè)TextView顯示的字符串是一成不變的,那么可以直接定義在布局文件中筋量,如果是動(dòng)態(tài)變化的烹吵,那么應(yīng)該使用tools:text預(yù)覽屬性,而不應(yīng)該使用android:text桨武,其他布局屬性也同理肋拔。
二十五、資源硬編碼規(guī)范
String 硬編碼規(guī)范:如果項(xiàng)目已經(jīng)適配了多語(yǔ)種呀酸,則嚴(yán)禁寫死在 Java 代碼或者布局文件中凉蜂,如果沒(méi)有這塊需求的話,也建議將 String 資源定義在string.xml文件性誉,此項(xiàng)不強(qiáng)制要求窿吩,大家根據(jù)實(shí)際情況而定。
Color 硬編碼規(guī)范:在沒(méi)有使用夜間模式的情況下错览,允許大部分 Color 值直接定義在布局文件中纫雁,但是如果某個(gè)色值引用得比較多(例如主題強(qiáng)調(diào)色、默認(rèn)背景色等)蝗砾,需要抽取到color.xml文件中先较。
Dimens 硬編碼規(guī)范:允許寫死在 Java 代碼或者布局文件中,但是如果使用了通配符方案對(duì)屏幕進(jìn)行適配悼粮,那么則不能直接寫死闲勺。
Style 樣式規(guī)范:對(duì)于一些常用并且樣式比較統(tǒng)一的控件,例如Button扣猫、EditText等菜循,我們對(duì)這些控件的樣式進(jìn)行抽取到style.xml文件中來(lái),避免屬性重復(fù)定義申尤。
版權(quán)申明:內(nèi)容來(lái)源網(wǎng)絡(luò)癌幕,版權(quán)歸原創(chuàng)者所有。除非無(wú)法確認(rèn)昧穿,都會(huì)標(biāo)明作者及出處勺远,如有侵權(quán)煩請(qǐng)告知,我們會(huì)立即刪除并致歉时鸵。謝謝!