一個(gè)項(xiàng)目從開(kāi)始開(kāi)發(fā)到后期的迭代版本,是多個(gè)人共同開(kāi)發(fā)的結(jié)果,所以當(dāng)團(tuán)隊(duì)人多起來(lái)的時(shí)候,編譯的規(guī)范就顯得很重要.這里整理了普遍使用的一些編譯規(guī)范,希望大家都遵守.
簡(jiǎn)單說(shuō)明
Android下的應(yīng)用程序大部分是基于java語(yǔ)言編寫的,所以規(guī)范都是按照java的來(lái)
Java 樣式規(guī)則
使用Javadoc 標(biāo)準(zhǔn)注釋
每個(gè)文件應(yīng)該在頂部,有版權(quán)的聲明接著包和導(dǎo)入語(yǔ)句 (由一個(gè)空行分隔每個(gè)塊),最后是類或接口聲明.在 Javadoc 注釋中,描述類或接口做些什么.
/*
* Copyright (C) 2016 Globalegrow E-Commerce
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.foo;
import android.os.Blah;
import android.view.Yada;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 做 X 和 Y 還有為 Z 提供一種抽象
*
* @author: zhengwu
* @date: 2016-12-29
*/
public class Foo {
...
}
每個(gè)類和公共方法,你寫必須與至少一個(gè)句子描述的類或方法并包含的 Javadoc 注釋.這句話應(yīng)該開(kāi)始用第三人稱描述性動(dòng)詞.
例子:
單行注釋
/** 返回一個(gè)雙精度值的正確圓正平方根. */
static double sqrt(double a) { ...}
or
多行注釋
/**
* 構(gòu)造一個(gè)新的字符串,通過(guò)轉(zhuǎn)換指定的字節(jié)數(shù)組的
* 使用平臺(tái)的默認(rèn)字符編碼.
*/
public String(byte[] bytes) { ...}
你不需要為簡(jiǎn)單的 get 和 set 方法寫 Javadoc ,如 setFoo() .如果該方法做更復(fù)雜的事物 (如強(qiáng)制約束或有一個(gè)重要的副作用),這個(gè)時(shí)候你必須記錄它.
寫簡(jiǎn)短的方法
在可行的情況下,保持方法小而且比較集中.我們認(rèn)識(shí)到,很長(zhǎng)的方法有時(shí)候是適當(dāng)?shù)? 所以沒(méi)有硬性限制放在方法長(zhǎng)度.如果一種方法超過(guò) 40 行左右,想想是否它可以被分解而不傷害程序的結(jié)構(gòu)。
在標(biāo)準(zhǔn)的地方定義字段
在頂部的文件或緊接使用它們的方法之前定義字段,這里建議在頂部的文件中定義,這樣方便查找.
限制變量范圍
將局部變量的范圍降到最低。通過(guò)這樣做,您增加可讀性和可維護(hù)性代碼并減少出錯(cuò)的可能性.每個(gè)變量應(yīng)該在包含變量所有使用的最內(nèi)層塊中聲明.
局部變量應(yīng)該在它們首次使用的點(diǎn)上聲明。 幾乎每個(gè)局部變量聲明都應(yīng)該包含一個(gè)初始化器。 如果你還沒(méi)有足夠的信息來(lái)明智地初始化變量,推遲聲明直到你這樣做.
異常是try-catch語(yǔ)句. 如果一個(gè)變量用一個(gè)拋出被檢查異常的方法的返回值初始化,它必須在try塊中初始化.如果值必須在try塊之外使用,那么它必須在try塊之前聲明,在那里它還不能明智地初始化:
// 初始化類cl, 這個(gè)類用來(lái)表示某種Set
Set s = null;
try {
s = (Set) cl.newInstance();
} catch(IllegalAccessException e) {
throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
throw new IllegalArgumentException(cl + " not instantiable");
}
...
// 使用這個(gè) set
s.addAll(Arrays.asList(args));
下面的這種寫法是推薦的
Set createSet(Class cl) {
// 初始化類cl, 這個(gè)類用來(lái)表示某種Set
try {
return (Set) cl.newInstance();
} catch(IllegalAccessException e) {
throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
throw new IllegalArgumentException(cl + " not instantiable");
}
}
...
// 使用這個(gè) set
Set s = createSet(cl);
s.addAll(Arrays.asList(args));
循環(huán)變量應(yīng)該在for語(yǔ)句本身中聲明,除非有強(qiáng)制的理由不這樣做:
for (int i = 0; i < n; i++) {
doSomething(i);
}
and
for (Iterator i = c.iterator(); i.hasNext(); ) {
doSomethingElse(i.next());
}
導(dǎo)入語(yǔ)句的順序
import語(yǔ)句的排序是:
- Android imports
- Imports from third parties (com, junit, net, org)
- java and javax
還有
- 每個(gè)分組中按字母順序排列,大寫字母前加小寫字母(e.g. Z before a)
- 在每個(gè)主要分組(android,com,junit,net,org,java,javax)之間用空行分隔
使用縮進(jìn)空格
使用4個(gè)空格縮進(jìn)塊,而不是制表符
我們使用8個(gè)空格縮進(jìn)進(jìn)行換行,包括函數(shù)調(diào)用和賦值,例如,這是正確的:
Instrument i =
someLongExpression(that, wouldNotFit, on, one, line);
下面這個(gè)是不正確的
Instrument i =
someLongExpression(that, wouldNotFit, on, one, line);
換行
沒(méi)有一個(gè)精確的公式解釋如何換行,并且經(jīng)常不同的解決方案是有效的.然而,有一些規(guī)則可以應(yīng)用于常見(jiàn)的情況, 這里列舉了常見(jiàn)的幾種.
int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
+ theFinalOne;
int longName =
anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;
Picasso.with(context)
.load("http://ribot.co.uk/images/sexyjoe.jpg")
.into(imageView);
loadPicture(context,
"http://ribot.co.uk/images/sexyjoe.jpg",
mImageViewProfilePicture,
clickListener,
"Title of the picture");
命名約定
變量的命名
- 非公共,非靜態(tài)字段名以m開(kāi)頭
- 靜態(tài)字段名稱以s開(kāi)頭
- 其他字段以小寫字母開(kāi)頭
- 公共靜態(tài)最終字段(常量)所以大寫字母并以下畫線連接
例子
public class MyClass {
public static final int SOME_CONSTANT = 42;
public int publicField;
private static MyClass sSingleton;
int mPackagePrivate;
private int mPrivate;
protected int mProtected;
}
Android 的許多元素(如SharedPreferences,Bundle或Intent)都使用鍵值對(duì)方法,因此對(duì)這個(gè)也規(guī)范如下:
所有的變量前面都加static final
| 組件 | 命名前綴 |
| ------------- |: -----:|
|SharedPreferences |PREF_
|Bundle |BUNDLE_
|Fragment Arguments |ARGUMENT_
|Intent Extra | EXTRA_
|Intent Action |ACTION_
|BroadCast Action|ACTION_
請(qǐng)注意,F(xiàn)ragment - Fragment.getArguments()的參數(shù)也是一個(gè)Bundle。 然而,因?yàn)檫@是一個(gè)很常見(jiàn)的使用Bundles,我們?yōu)樗鼈兌x一個(gè)不同的前綴。
例子:
// Note the value of the field is the same as the name to avoid duplication issues
static final String PREF_EMAIL = "PREF_EMAIL";
static final String BUNDLE_AGE = "BUNDLE_AGE";
static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID";
// Intent-related items use full package name as value
static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME";
static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER";
如果是啟動(dòng)Activity
public static Intent getStartIntent(Context context, User user) {
Intent intent = new Intent(context, ThisActivity.class);
intent.putParcelableExtra(EXTRA_USER, user);
return intent;
}
如果是啟動(dòng)Fragment
public static UserFragment newInstance(User user) {
UserFragment fragment = new UserFragment;
Bundle args = new Bundle();
args.putParcelable(ARGUMENT_USER, user);
fragment.setArguments(args)
return fragment;
}
注意1:這些方法應(yīng)該在onCreate()之前的類的頂部
注意2:如果我們提供上面描述的方法,extras和參數(shù)的鍵應(yīng)該是私有的,因?yàn)樗鼈儾恍枰┞对陬愅獠俊?/p>
類的命名
類名使用大寫駱駝拼寫法來(lái)命名,如果類是繼承了相應(yīng)的組件,則要以組件名字結(jié)尾來(lái)命名,如:SignInActivity, SignInFragment, ImageUploaderService,ChangePasswordDialog
| 類 | 命名格式 | 示例 |
| ------------- |: -------------:|: -----:|
| Activity | 描述+Activity |HomeActivity,MainActivity |
|Fragment |描述+Fragment | 如購(gòu)物車,CartFragment|
| Service | 描述+Service | PushMessageService |
| BroadcastReceiver | 描述+Receiver | OnlineReceiver |
|ContentProvider | 描述+Receiver| 如聯(lián)系人的內(nèi)容提供者,ContactsProvider|
|Dialog|描述+Dialog|如普通的選擇提示對(duì)話框,ChoiceDialog|
|Adapter|描述+Adapter|如聯(lián)系人列表,ContactsListAdapter|
|基礎(chǔ)功能類|Base+父類名| 如BaseActivity,BaseFragment|
|工具類|描述+Utils|如處理字符串的工具類,StringUtils|
|管理類|描述+Manager|如管理聯(lián)系人的類,ContactsManager|
方法的命名
| 命名風(fēng)格 | 含義 |
| ------------- |: -----:|
|initXX()|初始化,如初始化所有控件initView()|
|isXX()|是否滿足某種要求,如是否為注冊(cè)用戶isRegister()|
|displayXX()|顯示提示信息,如displayXXDialog,displayToast,displayXXPopupWindow|
|saveXX()| 保存XX數(shù)據(jù)|
|resetXX()| 重置XX數(shù)據(jù)|
資源的命名
資源的名字是用小寫的、下劃線連接
圖片資源的命名,沒(méi)有在下面列出來(lái)的圖片可以參考: 圖片的用途_用到的地方 來(lái)命名,如bg_sign_in, start_splash

用到的一些圖標(biāo)的命名


Layout 下資源的命名
| 組件 | 類名字 | 布局文件名 |
| ------------- |: -------------:|: -----:|
|Activity| UserProfileActivity| activity_user_profile.xml|
|Fragment| SignUpFragment | fragment_sign_up.xml|
|Dialog| ChangePasswordDialog| dialog_change_password.xml|
|AdapterView Item| -- | item_person.xml|
|抽取出來(lái)復(fù)用的xml布局(include)| --|include_bottom_tabs|
當(dāng)XML元素沒(méi)有任何內(nèi)容時(shí),您必須使用自動(dòng)關(guān)閉標(biāo)記。
正確的
<TextView
android:id="@+id/text_view_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
不對(duì)的
<TextView
android:id="@+id/text_view_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</TextView>
Menu 下資源的命名
與布局文件類似,菜單文件應(yīng)與組件的名稱匹配。 例如,如果我們定義將要在UserActivity中使用的菜單文件,則文件的名稱應(yīng)為activity_user.xml.
注意這里不要加menu,因?yàn)橐呀?jīng)在menu目錄下了
Values 下的文件命名
values文件夾中的資源文件應(yīng)為復(fù)數(shù),如:strings.xml,styles.xml,colors.xml,dimens.xml,attrs.xml
colors.xml: 定義顏色值, 如果是通用的顏色,用具體的顏色名稱來(lái)表示.如果是只有單獨(dú)的界面用到的或者有 代表性的,則用如goods_price,progressbar_bg,main_window_background
themes.xml: 定義主題,所有的主題名字以Theme結(jié)尾
styles.xml: 定義使用的樣式,所有的樣式名字以Style結(jié)尾
strings.xml: 定義所有使用的字符串, 字符串名稱以標(biāo)識(shí)其所屬性的前綴開(kāi)頭,看下面表格:
| 前綴 | 描述 |
| ------------- |: -----:|
|error_ |An error message
|msg_ |A regular information message
|title_ |A title, i.e. a dialog title|
|action_ |An action such as "Save" or "Create"|
id 的命名
都是用小寫字母、下劃線鏈接
| 組件 | 描述 |
| ------------- |: -----:|
|TextView|_text|
|ImageView|_image|
|Button|_button|
|CheckBox|_check|
|ProgressBar|_bar或者_(dá)view|
|ScrollView|_view|
|自定義view|_view|
|LinearLayout|_layout或者_(dá)container|
|RelativeLayout|_layout或者_(dá)container|
|Fragment|fragment|
|Menu|menu|
<ImageView
android:id="@+id/profile_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<menu>
<item
android:id="@+id/menu_done"
android:title="Done" />
</menu>
使用標(biāo)準(zhǔn)括號(hào)樣式
大括號(hào)不自己另起一行; 他們和他們之前的代碼在同一行:
class MyClass {
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}
我們需要在條件語(yǔ)句周圍添加括號(hào). 特例:如果整個(gè)條件(條件和正文)符合一行,您可以(但不是必須)將它全部放在一行上,例如,這是可以接受的:
if (condition) {
body();
}
下面這樣也可以
if (condition) body();
但是這樣就不可以
if (condition)
body(); // 不好的!容易造成歧義
限制代碼每一行的長(zhǎng)度
代碼中的每行文字長(zhǎng)度應(yīng)試最多為100個(gè)字符,也有特例:
- 如果注釋行包含示例命令或長(zhǎng)度超過(guò)100個(gè)字符的文字URL,那么該行可能長(zhǎng)于100個(gè)字符,以便于剪切和粘貼.
- import行可以超過(guò)限制,因?yàn)槿藗兒苌倏吹剿鼈儯ㄟ@也簡(jiǎn)化了工具寫入)
正確使用首字母縮略詞
將縮寫詞作為命名變量,方法和類中的單詞,以使名稱更易讀:

使用TODO注釋
對(duì)臨時(shí)代碼使用TODO注釋,短期解決方案,或者足夠好但不完美的代碼。 TODO應(yīng)在所有大寫字母中包含字符串TODO,后跟冒號(hào):
Java 語(yǔ)言規(guī)則
不要忽略異常的處理
可能很容易編寫完全忽略異常的代碼,例如:
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) { }
}
不要這樣做.雖然你可能認(rèn)為你的代碼永遠(yuǎn)不會(huì)遇到這個(gè)錯(cuò)誤條件或者它不重要的處理它,忽略異常如上所示在你的代碼中為別人觸發(fā)一天.你必須以原則的方式處理你的代碼中的每一個(gè)異常; 具體處理根據(jù)情況而變化.
因該使用下面的下發(fā)來(lái)替換(按優(yōu)先順序):
- 將異常拋出給方法的調(diào)用者
void setServerPort(String value) throws NumberFormatException {
serverPort = Integer.parseInt(value);
}
- 拋出一個(gè)適合你的抽象層次的新異常
void setServerPort(String value) throws ConfigurationException {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new ConfigurationException("Port " + value + " is not valid.");
}
}
- 處理錯(cuò)誤并在catch {}塊中替換一個(gè)適當(dāng)?shù)闹?/li>
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
serverPort = 80; // default port for server
}
}
不捕獲泛型異常
try {
someComplicatedIOFunction(); // may throw IOException
someComplicatedParsingFunction(); // may throw ParsingException
someComplicatedSecurityFunction(); // may throw SecurityException
// phew, made it all the way
} catch (Exception e) { // I'll just catch all exceptions
handleError(); // with one generic handler!
}
導(dǎo)入具體的包路徑
當(dāng)你想使用包foo中的類Bar時(shí),有兩種可能的方法來(lái)導(dǎo)入它:
- import foo.*;
- import foo.Bar; //使用這個(gè)方式,代碼可讀性更強(qiáng).
其他還需要注意的地方
- 縮放保證不失真的,圖片可以做成點(diǎn)9圖
- strings.xml中使用%1$s實(shí)現(xiàn)字符串的通配
- 代碼中不出現(xiàn)中文,最多注釋中可以出現(xiàn)中文