命名應該遵循駝峰命名法
對於繼承自 Android 組件的類來說,命名是應以改組件的名稱結尾;例如: SignInActivity
, SignInFragment
, ImageUploaderService
, ChangePasswordDialog
。
資源文件應該以小寫 + 下劃線( _ )的格式命名。
以下是對於圖片文件的命名習慣
ab_
ab_stacked.9.png
Button
btn_
btn_send_pressed.9.png
Dialog
dialog_
dialog_top.9.png
Divider
divider_
divider_horizontal.9.png
Icon
ic_
ic_star.png
Menu
menu_
menu_submenu_bg.9.png
Notification
notifi_
notifi_bg.9.png
Tabs
tab_
tab_pressed.9.png
對於圖標的命名習慣
ic_
ic_star.png
Launcher icons
ic_launcher
ic_launcher_calendar.png
Menu icons and Action Bar icons
ic_menu
ic_menu_archive.png
Status bar icons
ic_stat_notify
ic_stat_notify_msg.png
Tab icons
ic_tab
ic_tab_recent.png
Dialog icons
ic_dialog
ic_dialog_info.png
對於選擇器狀態的命名習慣
_normal
btn_order_normal.9.png
Pressed
_pressed
btn_order_pressed.9.png
Focused
_focused
btn_order_focused.9.png
Disabled
_disabled
btn_order_disabled.9.png
Selected
_selected
btn_order_selected.9.png
布局文件應該和將要用於的 Android 組件的名稱相匹配,但是這次應以組件的名稱開頭。例如, 如果我們為 SignInActivity
,創建布局文件,那布局文件的名稱就應該為 activity_sign_in.xml
.
UserProfileActivity
activity_user_profile.xml
Fragment
SignUpFragment
fragment_sign_up.xml
Dialog
ChangePasswordDialog
dialog_change_password.xml
AdapterView item
—
item_person.xml
Partial layout
—
partial_stats_bar.xml
一個特殊的情況就是在為 Adapter 中的子項創建布局的時候, 例如, 顯示 ListView 中的內容。在這種情況下,布局文件的前綴應該為 item_
應該注意到還有一個特殊情況的存在,那就是在創建一個布局中的其中一小塊布局時,在這種情況下就應該使用前綴 partial_
與布局文件的命名的規則相似,menu 文件也應該和將要用於的 Android 組件的名稱相匹配。例如,當我們在為 UserActivity
創建 menu 文件時,那 menu 文件的名稱就應該是 activity_user.xml
命名規則是不把單詞 menu
作為名稱的一部分,因為這些文件已經存放在 menu
的文件下了。
在 values
文件夾中的資源文件在命名時應該為復數。例如, strings.xml
, styles.xml
, colors.xml
, dimens.xml
, attrs.xml
永遠不要編寫出以下的代碼
1
2
3
4
5
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) { }
}
不要認為你的代碼永遠不會觸發此類的異常或者不足以處理,或者如以上代碼似的,在你的代碼裡留下缺口讓別人在以後幫你來填上.你必須按照規范來捕獲每一個異常.也可以查看 Android 官方的文檔描述.
永遠不要編寫以下的代碼
1
2
3
4
5
6
7
8
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!
}
具體原因可以查看 Android 官方文檔描述
不要使用 finalizers (不知道 finalize 的可以查看這裡). 即便 finalizer 最終都是會被調用的但是什麼時候會別調用是沒有保證的. 在大多數的情況下,你可以通過好的異常捕獲機制來取代 finalizer. 如果在某種情況下你必須要使用到它, 定義一個 close()
方法(或者類似的)然後准確的說明一下什麼時候該方法會被調用到.
這是一個不好的引用編寫: import foo.*;
這是一個合格的引用編寫: import foo.Bar;
更多信息查看這裡
變量應該定義在文件頭部的位置,並且應該遵循以下的命名規則.
ALL_CAPS_WITH_UNDERSCORES
.1
2
3
4
5
6
7
8
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;
}
XmlHttpRequest
XMLHTTPRequest
getCustomerId
getCustomerID
String url
String URL
long id
long ID
使用4 個空格來進行代碼塊的縮進
1
2
3
if (x == 1) {
x++;
}
使用 8 個空格來進行代碼的換行
1
2
Instrument i =
someLongExpression(that, wouldNotFit, on, one, line);
左大括號應該跟在其之前的代碼在同一行上
1
2
3
4
5
6
7
8
9
10
11
class MyClass {
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}
如果條件語句跟結果語句正好可以在同一行上並且其長度也小於同一行的最大長度限制, 則可以省略大括號
例如
1
if (condition) body();
錯誤的范例
1
2
if (condition)
body(); // bad!
根據 Android 官方文檔, 在 Java 中對於一些預先確定的注解的標准應用如下
@Override
: 該注解必須用與任何時候想要重寫或者實現父類的某個方法的時候. 例如,當你使用了 @inheritdocs
標簽,並且是源於一個類而並不是接口的時候,你必須同時也在此方法上加上 @Override
標簽.@SuppressWarnings
: 該標簽只有在遇到無法忽略的警告的條件下才可以使用. 如果一個警告符合”無法忽略掉”的條件時,該標簽是必須需要被使用的,為的是保證所有的警告都能反映出代碼中實際存在的問題.類,方法和構造函數: 當注解被用於類,方法和構造函數的時候,應該將注解位於注釋的下面,並且每個注解作為一行的形式.如下所示
1
2
3
4
/* This is the documentation block about the class */
@AnnotationA
@AnnotationB
public class MyAnnotatedClass { }
對象: 注解應該與對象保持在同一行,除非該行達到了最大字符的限制數.
1
@Nullable @Mock DataManager mDataManager;
局部變量的作用域應該保持到最小.這樣可以增加代碼的可讀性和維護性,並且降低出錯的概率.
局部變量應該盡量在它第一次被調用的時候被聲明出來.而且聲明局部變量時應該初始化該變量,如果你還沒有足夠的信息來初始化該變量,那就應該推遲聲明直到擁有足夠的信息來初始化此變量的時候.更多信息可查看這裡
請使用公司通用的 LogUtil
類來取代 Android 原生的 Log
類來打印日志.
這部分沒有強制的要求,但是使用一種合乎邏輯並且常用的方式來排列類成員,可以增強代碼的可讀性
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Child extends Parent {
private static final int CONSTANT = 1;
private String mName;
private int mAge;
public Child(String name, int age) {
mName = name;
mAge = age;
}
@Override
public void changeName() {
...
}
public void setName(String name) {
mName = name;
}
private void setSomething() {
...
}
static class AnInnerClass {
}
}
如果你的類是繼承自Android 的組件,例如 Activity 和 Fragment, 比較好的習慣是按照該組件的生命周期來重寫方法.例如,如果你有一個 Activity 實現了 onCreate()
, onDestroy()
, onPause()
和 onResume()
,則正確的順序為:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MainActivity extends Activity {
//Order matches Activity lifecycle
@Override
public void onCreate() {}
@Override
public void onResume() {}
@Override
public void onPause() {}
@Override
public void onDestroy() {}
}
在編寫 Android 的代碼時,函數中含有參數 Context
是非常常見的.如果遇到這種情況,那麼必須將Context作為第一個參數.
對應的回調接口應該永遠作為函數的最後一個參數
例如:
1
2
3
4
5
// Context always goes first
public User loadUser(Context context, int userId);
// Callbacks always go last
public void loadUserAsync(Context context, int userId, UserCallback callback);
Android SDK 中包含很多需要鍵值對的元素例如, SharedPreferences
, Bundle
和 Intent
.即使在寫一個很小的 app 應用時,也會產生很多字符串常量.
當使用以上組件的時候,你必須將字符串定義為 static final
,並且它們的前綴應該遵循以下的命名規則:
PREF_
Bundle
BUNDLE_
Fragment Arguments
ARGUMENT_
Intent Extra
EXTRA_
Intent Action
ACTION_
雖然 Fragment.getArguments()
返回的也是一個 Bundle,但是為了用於區分,所以使用 ARGUMENT_
作為其前綴.
1
2
3
4
5
6
7
8
// 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";
當數據通過 Intent
或 Bundle
傳遞給 Fragment
和 Activity
的時候, Key 的命名必須要遵循以上的命名規范.
當 Activity
或 Fragment
需要接受參數的時候, 需要創建一個 public static
的方法來創建與之相關的 Intent
或 Fragment
.
Activity 的情況下,通常將方法命名為 getStartIntent()
:
1
2
3
4
5
public static Intent getStartIntent(Context context, User user) {
Intent intent = new Intent(context, ThisActivity.class);
intent.putParcelableExtra(EXTRA_USER, user);
return intent;
}
Fragment 的情況下,通常將方法命名為 newInstance()
,通過傳入的參數來創建 Fragment
1
2
3
4
5
6
7
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:如果我們已經聲明了以上的方法,那麼為 Intent 或 Bundle 聲明的 Key
應該是 private
, 因為不需要類之外來使用.
一行代碼的長度不應該超過 100 個字符,如果一行代碼過長,通常有如下兩種方法來減少代碼的長度:
局部變量
或方法
(推薦)有兩種例外的情況可以允許行代碼超過 100 個字符
package
和 import
的聲明這裡也沒有強制行的規范,但是有幾條比較通用的規范希望可以遵守
運算符
當要在運算符處進行換行的時候,換行應該在運算符之前,例如:
1
2
int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
+ theFinalOne;
但是以上規則不適用於 =
運算符,換行應該在等號運算符之後,例如:
1
2
int longName =
anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;
方法鏈的情況下
當多個方法在同一行組合在一起成為方法鏈的時候.例如, Builder 模式下,每一個方法應該獨立成一行,並換行應該在 .
之前.
1
Picasso.with(context).load("http://ribot.co.uk/images/sexyjoe.jpg").into(imageView);
1
2
3
Picasso.with(context)
.load("http://ribot.co.uk/images/sexyjoe.jpg")
.into(imageView);
多個參數的情況下
當一個函數多個參數並且參數過長時,我們應該在 ,
後進行換行.
1
loadPicture(context, "http://ribot.co.uk/images/sexyjoe.jpg", mImageViewProfilePicture, clickListener, "Title of the picture");
1
2
3
4
5
6
7
loadPicture(
context,
"http://ribot.co.uk/images/sexyjoe.jpg",
mImageViewProfilePicture,
clickListener,
"Title of the picture"
);
Rx 的方法鏈同樣需要換行.每一個操作必須獨立為一行,而且換行應該在 .
之前.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Observable<Location> syncLocations() {
return mDatabaseHelper.getAllLocations()
.concatMap(new Func1<Location, Observable<? extends Location>>() {
@Override
public Observable<? extends Location> call(Location location) {
return mRetrofitService.getLocation(location.id);
}
})
.retry(new Func2<Integer, Throwable, Boolean>() {
@Override
public Boolean call(Integer numRetries, Throwable throwable) {
return throwable instanceof RetrofitError;
}
});
}
當一個 XML 裡的元素內沒有其他元素時,應該使用自結束標簽
這是正確的:
1
2
3
4
<TextView
android:id="@+id/text_view_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
這是錯誤的:
1
2
3
4
5
6
<!-- Don\'t do this! -->
<TextView
android:id="@+id/text_view_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</TextView>
資源的 ID 和名稱都應該是小寫 + 下劃線的格式
ID 應該已控件的名稱作為前綴來命名.例如:
TextView
text_
ImageView
image_
Button
button_
Menu
menu_
ImageView 例子:
1
2
3
4
<ImageView
android:id="@+id/image_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Menu 例子:
1
2
3
4
5
<menu>
<item
android:id="@+id/menu_done"
android:title="Done" />
</menu>
字符串的名字應該已一個可以標明其所屬領域的前綴來做開頭. 例如, registration_email_hint
或 registration_name_hint
.如果一個字符串不屬於任何的領域,那應該遵循以下規則:
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”
不同於其他的資源命名規范, Style 中的名字應該遵循駝峰命名法
color 中顏色的命名應該遵循小寫 + 下劃線的格式
雖然使用 enum 很方便,但是會比使用靜態變量產生多於兩倍的內存消耗,所以 Android 官方強烈建議不要在Android程序裡面使用到 enum.
使用 Android Typedef Annotations 可以代替 enum, 具體的使用發放請參考這裡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static class Foo {
int mSplat;
}
Foo[] mArray = ...
// 最慢,消耗最多
public void zero() {
int sum = 0;
for (int i = 0; i < mArray.length; ++i) {
sum += mArray[i].mSplat;
}
}
public void one() {
int sum = 0;
Foo[] localArray = mArray;
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray[i].mSplat;
}
}
// 推薦
public void two() {
int sum = 0;
for (Foo a : mArray) {
sum += a.mSplat;
}
}
zero()
是最慢的方法,因為 JIT 還不能對當數組進行遍歷時每次都要去獲得數組的長度進行優化.one()
更快一點, 因為它所有的數據都拿出來存放在局部變量裡,避免了查找.只有提供了數組的長度對性能有了一定的提升two()
在沒有 JIT 的設備裡是最快的,但在有 JIT 的設備裡於one()難分上下.它使用了 Java 1.5 版本中的增強版語法
所以應該默認使用增強版的 for 循環。
更多Android相關信息見Android 專題頁面 http://www.linuxidc.com/topicnews.aspx?tid=11