Android代碼規(guī)范

一司澎、前言

代碼規(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ì)立即刪除并致歉时鸵。謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胶逢,一起剝皮案震驚了整個(gè)濱河市厅瞎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌初坠,老刑警劉巖和簸,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異碟刺,居然都是意外死亡锁保,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門半沽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)爽柒,“玉大人,你說(shuō)我怎么就攤上這事抄囚∶股模” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵幔托,是天一觀的道長(zhǎng)穴亏。 經(jīng)常有香客問(wèn)我,道長(zhǎng)重挑,這世上最難降的妖魔是什么嗓化? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮谬哀,結(jié)果婚禮上刺覆,老公的妹妹穿的比我還像新娘。我一直安慰自己史煎,他們只是感情好谦屑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著篇梭,像睡著了一般氢橙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恬偷,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天悍手,我揣著相機(jī)與錄音,去河邊找鬼袍患。 笑死坦康,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诡延。 我是一名探鬼主播滞欠,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼肆良!你這毒婦竟也來(lái)了仑撞?” 一聲冷哼從身側(cè)響起赤兴,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎隧哮,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體座舍,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沮翔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了曲秉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片采蚀。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖承二,靈堂內(nèi)的尸體忽然破棺而出榆鼠,到底是詐尸還是另有隱情,我是刑警寧澤亥鸠,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布妆够,位于F島的核電站,受9級(jí)特大地震影響负蚊,放射性物質(zhì)發(fā)生泄漏神妹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一家妆、第九天 我趴在偏房一處隱蔽的房頂上張望鸵荠。 院中可真熱鬧,春花似錦伤极、人聲如沸蛹找。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)庸疾。三九已至,卻和暖如春齿税,著一層夾襖步出監(jiān)牢的瞬間彼硫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工凌箕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拧篮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓牵舱,卻偏偏與公主長(zhǎng)得像串绩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芜壁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355