一個項目從開始開發到后期的迭代版本,是多個人共同開發的結果,所以當團隊人多起來的時候,編譯的規范就顯得很重要.這里整理了普遍使用的一些編譯規范,希望大家都遵守.
簡單說明
Android下的應用程序大部分是基于java語言編寫的,所以規范都是按照java的來
Java 樣式規則
使用Javadoc 標準注釋
每個文件應該在頂部,有版權的聲明接著包和導入語句 (由一個空行分隔每個塊),最后是類或接口聲明.在 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 {
...
}
每個類和公共方法,你寫必須與至少一個句子描述的類或方法并包含的 Javadoc 注釋.這句話應該開始用第三人稱描述性動詞.
例子:
單行注釋
/** 返回一個雙精度值的正確圓正平方根. */
static double sqrt(double a) { ...}
or
多行注釋
/**
* 構造一個新的字符串,通過轉換指定的字節數組的
* 使用平臺的默認字符編碼.
*/
public String(byte[] bytes) { ...}
你不需要為簡單的 get 和 set 方法寫 Javadoc ,如 setFoo() .如果該方法做更復雜的事物 (如強制約束或有一個重要的副作用),這個時候你必須記錄它.
寫簡短的方法
在可行的情況下,保持方法小而且比較集中.我們認識到,很長的方法有時候是適當的, 所以沒有硬性限制放在方法長度.如果一種方法超過 40 行左右,想想是否它可以被分解而不傷害程序的結構。
在標準的地方定義字段
在頂部的文件或緊接使用它們的方法之前定義字段,這里建議在頂部的文件中定義,這樣方便查找.
限制變量范圍
將局部變量的范圍降到最低。通過這樣做,您增加可讀性和可維護性代碼并減少出錯的可能性.每個變量應該在包含變量所有使用的最內層塊中聲明.
局部變量應該在它們首次使用的點上聲明。 幾乎每個局部變量聲明都應該包含一個初始化器。 如果你還沒有足夠的信息來明智地初始化變量,推遲聲明直到你這樣做.
異常是try-catch語句. 如果一個變量用一個拋出被檢查異常的方法的返回值初始化,它必須在try塊中初始化.如果值必須在try塊之外使用,那么它必須在try塊之前聲明,在那里它還不能明智地初始化:
// 初始化類cl, 這個類用來表示某種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");
}
...
// 使用這個 set
s.addAll(Arrays.asList(args));
下面的這種寫法是推薦的
Set createSet(Class cl) {
// 初始化類cl, 這個類用來表示某種Set
try {
return (Set) cl.newInstance();
} catch(IllegalAccessException e) {
throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
throw new IllegalArgumentException(cl + " not instantiable");
}
}
...
// 使用這個 set
Set s = createSet(cl);
s.addAll(Arrays.asList(args));
循環變量應該在for語句本身中聲明,除非有強制的理由不這樣做:
for (int i = 0; i < n; i++) {
doSomething(i);
}
and
for (Iterator i = c.iterator(); i.hasNext(); ) {
doSomethingElse(i.next());
}
導入語句的順序
import語句的排序是:
- Android imports
- Imports from third parties (com, junit, net, org)
- java and javax
還有
- 每個分組中按字母順序排列,大寫字母前加小寫字母(e.g. Z before a)
- 在每個主要分組(android,com,junit,net,org,java,javax)之間用空行分隔
使用縮進空格
使用4個空格縮進塊,而不是制表符
我們使用8個空格縮進進行換行,包括函數調用和賦值,例如,這是正確的:
Instrument i =
someLongExpression(that, wouldNotFit, on, one, line);
下面這個是不正確的
Instrument i =
someLongExpression(that, wouldNotFit, on, one, line);
換行
沒有一個精確的公式解釋如何換行,并且經常不同的解決方案是有效的.然而,有一些規則可以應用于常見的情況, 這里列舉了常見的幾種.
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");
命名約定
變量的命名
- 非公共,非靜態字段名以m開頭
- 靜態字段名稱以s開頭
- 其他字段以小寫字母開頭
- 公共靜態最終字段(常量)所以大寫字母并以下畫線連接
例子
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)都使用鍵值對方法,因此對這個也規范如下:
所有的變量前面都加static final
| 組件 | 命名前綴 |
| ------------- |: -----:|
|SharedPreferences |PREF_
|Bundle |BUNDLE_
|Fragment Arguments |ARGUMENT_
|Intent Extra | EXTRA_
|Intent Action |ACTION_
|BroadCast Action|ACTION_
請注意,Fragment - Fragment.getArguments()的參數也是一個Bundle。 然而,因為這是一個很常見的使用Bundles,我們為它們定義一個不同的前綴。
例子:
// 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";
如果是啟動Activity
public static Intent getStartIntent(Context context, User user) {
Intent intent = new Intent(context, ThisActivity.class);
intent.putParcelableExtra(EXTRA_USER, user);
return intent;
}
如果是啟動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:這些方法應該在onCreate()之前的類的頂部
注意2:如果我們提供上面描述的方法,extras和參數的鍵應該是私有的,因為它們不需要暴露在類外部。
類的命名
類名使用大寫駱駝拼寫法來命名,如果類是繼承了相應的組件,則要以組件名字結尾來命名,如:SignInActivity, SignInFragment, ImageUploaderService,ChangePasswordDialog
| 類 | 命名格式 | 示例 |
| ------------- |: -------------:|: -----:|
| Activity | 描述+Activity |HomeActivity,MainActivity |
|Fragment |描述+Fragment | 如購物車,CartFragment|
| Service | 描述+Service | PushMessageService |
| BroadcastReceiver | 描述+Receiver | OnlineReceiver |
|ContentProvider | 描述+Receiver| 如聯系人的內容提供者,ContactsProvider|
|Dialog|描述+Dialog|如普通的選擇提示對話框,ChoiceDialog|
|Adapter|描述+Adapter|如聯系人列表,ContactsListAdapter|
|基礎功能類|Base+父類名| 如BaseActivity,BaseFragment|
|工具類|描述+Utils|如處理字符串的工具類,StringUtils|
|管理類|描述+Manager|如管理聯系人的類,ContactsManager|
方法的命名
| 命名風格 | 含義 |
| ------------- |: -----:|
|initXX()|初始化,如初始化所有控件initView()|
|isXX()|是否滿足某種要求,如是否為注冊用戶isRegister()|
|displayXX()|顯示提示信息,如displayXXDialog,displayToast,displayXXPopupWindow|
|saveXX()| 保存XX數據|
|resetXX()| 重置XX數據|
資源的命名
資源的名字是用小寫的、下劃線連接
圖片資源的命名,沒有在下面列出來的圖片可以參考: 圖片的用途_用到的地方 來命名,如bg_sign_in, start_splash
用到的一些圖標的命名
Layout 下資源的命名
| 組件 | 類名字 | 布局文件名 |
| ------------- |: -------------:|: -----:|
|Activity| UserProfileActivity| activity_user_profile.xml|
|Fragment| SignUpFragment | fragment_sign_up.xml|
|Dialog| ChangePasswordDialog| dialog_change_password.xml|
|AdapterView Item| -- | item_person.xml|
|抽取出來復用的xml布局(include)| --|include_bottom_tabs|
當XML元素沒有任何內容時,您必須使用自動關閉標記。
正確的
<TextView
android:id="@+id/text_view_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
不對的
<TextView
android:id="@+id/text_view_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</TextView>
Menu 下資源的命名
與布局文件類似,菜單文件應與組件的名稱匹配。 例如,如果我們定義將要在UserActivity中使用的菜單文件,則文件的名稱應為activity_user.xml.
注意這里不要加menu,因為已經在menu目錄下了
Values 下的文件命名
values文件夾中的資源文件應為復數,如:strings.xml,styles.xml,colors.xml,dimens.xml,attrs.xml
colors.xml: 定義顏色值, 如果是通用的顏色,用具體的顏色名稱來表示.如果是只有單獨的界面用到的或者有 代表性的,則用如goods_price,progressbar_bg,main_window_background
themes.xml: 定義主題,所有的主題名字以Theme結尾
styles.xml: 定義使用的樣式,所有的樣式名字以Style結尾
strings.xml: 定義所有使用的字符串, 字符串名稱以標識其所屬性的前綴開頭,看下面表格:
| 前綴 | 描述 |
| ------------- |: -----:|
|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或者_view|
|ScrollView|_view|
|自定義view|_view|
|LinearLayout|_layout或者_container|
|RelativeLayout|_layout或者_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>
使用標準括號樣式
大括號不自己另起一行; 他們和他們之前的代碼在同一行:
class MyClass {
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}
我們需要在條件語句周圍添加括號. 特例:如果整個條件(條件和正文)符合一行,您可以(但不是必須)將它全部放在一行上,例如,這是可以接受的:
if (condition) {
body();
}
下面這樣也可以
if (condition) body();
但是這樣就不可以
if (condition)
body(); // 不好的!容易造成歧義
限制代碼每一行的長度
代碼中的每行文字長度應試最多為100個字符,也有特例:
- 如果注釋行包含示例命令或長度超過100個字符的文字URL,那么該行可能長于100個字符,以便于剪切和粘貼.
- import行可以超過限制,因為人們很少看到它們(這也簡化了工具寫入)
正確使用首字母縮略詞
將縮寫詞作為命名變量,方法和類中的單詞,以使名稱更易讀:
使用TODO注釋
對臨時代碼使用TODO注釋,短期解決方案,或者足夠好但不完美的代碼。 TODO應在所有大寫字母中包含字符串TODO,后跟冒號:
Java 語言規則
不要忽略異常的處理
可能很容易編寫完全忽略異常的代碼,例如:
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) { }
}
不要這樣做.雖然你可能認為你的代碼永遠不會遇到這個錯誤條件或者它不重要的處理它,忽略異常如上所示在你的代碼中為別人觸發一天.你必須以原則的方式處理你的代碼中的每一個異常; 具體處理根據情況而變化.
因該使用下面的下發來替換(按優先順序):
- 將異常拋出給方法的調用者
void setServerPort(String value) throws NumberFormatException {
serverPort = Integer.parseInt(value);
}
- 拋出一個適合你的抽象層次的新異常
void setServerPort(String value) throws ConfigurationException {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new ConfigurationException("Port " + value + " is not valid.");
}
}
- 處理錯誤并在catch {}塊中替換一個適當的值
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!
}
導入具體的包路徑
當你想使用包foo中的類Bar時,有兩種可能的方法來導入它:
- import foo.*;
- import foo.Bar; //使用這個方式,代碼可讀性更強.
其他還需要注意的地方
- 縮放保證不失真的,圖片可以做成點9圖
- strings.xml中使用%1$s實現字符串的通配
- 代碼中不出現中文,最多注釋中可以出現中文