爲了有利於項目維護、加強代碼可讀性、提高 Code Review 效率以及規範團隊安卓開發,故提出如下安卓開發規範,該規範結合本人多年的開發經驗並吸收多家之精華,可謂是本人的嘔心瀝血之做,稱其爲當前最完善的安卓開發規範一點也不爲過,若有更好建議,歡迎到 GitHub 提 issue,原文地址:Android 開發規範(完結版)。相關 Demo,能夠查看個人 Android 開發工具類集合項目:Android 開發人員不得不收集的代碼。後續可能會根據該規範出一個 CheckStyle 插件來檢查是否規範,固然也支持在 CI 上運行。html
工欲善其事,必先利其器。java
代碼中的命名嚴禁使用拼音與英文混合的方式,更不容許直接使用中文的方式。正確的英文拼寫和語法可讓閱讀者易於理解,避免歧義。android
注意:即便純拼音命名方式也要避免採用。但
alibaba
、taobao
、youku
、hangzhou
等國際通用的名稱,可視同英文。git
包名所有小寫,連續的單詞只是簡單地鏈接起來,不使用下劃線,採用反域名命名規則,所有使用小寫字母。一級包名是頂級域名,一般爲 com
、edu
、gov
、net
、org
等,二級包名爲公司名,三級包名根據應用進行命名,後面就是對包名的劃分了,關於包名的劃分,推薦採用 PBF(按功能分包 Package By Feature),一開始咱們採用的也是 PBL(按層分包 Package By Layer),很坑爹。PBF 可能不是很好區分在哪一個功能中,不過也比 PBL 要好找不少,且 PBF 與 PBL 相比較有以下優點:github
package 內高內聚,package 間低耦合數據庫
哪塊要添新功能,只改某一個 package 下的東西。編程
PBL 下降了代碼耦合,但帶來了 package 耦合,要添新功能,須要改 model、dbHelper、view、service 等等,須要改動好幾個 package 下的代碼,改動的地方越多,越容易產生新問題,不是嗎?json
PBF 的話 featureA 相關的全部東西都在 featureA 包,feature 內高內聚、高度模塊化,不一樣 feature 之間低耦合,相關的東西都放在一塊兒,還好找。數組
package 有私有做用域(package-private scope)android-studio
你負責開發這塊功能,這個目錄下全部東西都是你的。
PBL 的方式是把全部工具方法都放在 util 包下,小張開發新功能時候發現須要一個 xxUtil,但它又不是通用的,那應該放在哪裏?沒辦法,按照分層原則,咱們還得放在 util 包下,好像不太合適,但放在其它包更不合適,功能愈來愈多,util 類也越定義越多。後來小李負責開發一塊功能時發現須要一個 xxUtil,一樣不通用,去 util 包一看,怎麼已經有了,並且還無法複用,只好放棄 xx 這個名字,改成 xxxUtil……,由於 PBL 的 package 沒有私有做用域,每個包都是 public(跨包方法調用是很日常的事情,每個包對其它包來講都是可訪問的);若是是 PBF,小張的 xxUtil 天然放在 featureA 下,小李的 xxUtil 在 featureB 下,若是以爲 util 好像是通用的,就去 util 包看看要不要把工具方法添進 xxUtil, class 命名衝突沒有了。
PBF 的 package 有私有做用域,featureA 不該該訪問 featureB 下的任何東西(若是非訪問不可,那就說明接口定義有問題)。
很容易刪除功能
統計發現新功能沒人用,這個版本那塊功能得去掉。
若是是 PBL,得從功能入口到整個業務流程把受到牽連的全部能刪的代碼和 class 都揪出來刪掉,一不當心就完蛋。
若是是 PBF,好說,先刪掉對應包,再刪掉功能入口(刪掉包後入口確定報錯了),完事。
高度抽象
解決問題的通常方法是從抽象到具體,PBF 包名是對功能模塊的抽象,包內的 class 是實現細節,符合從抽象到具體,而 PBL 弄反了。
PBF 從肯定 AppName 開始,根據功能模塊劃分 package,再考慮每塊的具體實現細節,而 PBL 從一開始就要考慮要不要 dao 層,要不要 com 層等等。
只經過 class 來分離邏輯代碼
PBL 既分離 class 又分離 package,而 PBF 只經過 class 來分離邏輯代碼。
沒有必要經過 package 分離,由於 PBL 中也可能出現尷尬的狀況:
├── service
├── MainService.java
複製代碼
按照 PBL, service 包下的全部東西都是 Service,應該不須要 Service 後綴,但實際上一般爲了方便,直接 import service 包,Service 後綴是爲了不引入的 class 和當前包下的 class 命名衝突,固然,不用後綴也能夠,得寫清楚包路徑,好比 new com.domain.service.MainService()
,麻煩;而 PBF 就很方便,無需 import,直接 new MainService()
便可。
package 的大小有意義了
PBL 中包的大小無限增加是合理的,由於功能越添越多,而 PBF 中包太大(包裏 class 太多)表示這塊須要重構(劃分子包)。
如要知道更多好處,能夠查看這篇博文:Package by features, not layers,固然,咱們大谷歌也有相應的 Sample:todo-mvp,其結構以下所示,很值得學習。
com
└── example
└── android
└── architecture
└── blueprints
└── todoapp
├── BasePresenter.java
├── BaseView.java
├── addedittask
│ ├── AddEditTaskActivity.java
│ ├── AddEditTaskContract.java
│ ├── AddEditTaskFragment.java
│ └── AddEditTaskPresenter.java
├── data
│ ├── Task.java
│ └── source
│ ├── TasksDataSource.java
│ ├── TasksRepository.java
│ ├── local
│ │ ├── TasksDbHelper.java
│ │ ├── TasksLocalDataSource.java
│ │ └── TasksPersistenceContract.java
│ └── remote
│ └── TasksRemoteDataSource.java
├── statistics
│ ├── StatisticsActivity.java
│ ├── StatisticsContract.java
│ ├── StatisticsFragment.java
│ └── StatisticsPresenter.java
├── taskdetail
│ ├── TaskDetailActivity.java
│ ├── TaskDetailContract.java
│ ├── TaskDetailFragment.java
│ └── TaskDetailPresenter.java
├── tasks
│ ├── ScrollChildSwipeRefreshLayout.java
│ ├── TasksActivity.java
│ ├── TasksContract.java
│ ├── TasksFilterType.java
│ ├── TasksFragment.java
│ └── TasksPresenter.java
└── util
├── ActivityUtils.java
├── EspressoIdlingResource.java
└── SimpleCountingIdlingResource.java
複製代碼
參考以上的代碼結構,按功能分包具體能夠這樣作:
com
└── domain
└── app
├── App.java 定義 Application 類
├── Config.java 定義配置數據(常量)
├── base 基礎組件
├── custom_view 自定義視圖
├── data 數據處理
│ ├── DataManager.java 數據管理器,
│ ├── local 來源於本地的數據,好比 SP,Database,File
│ ├── model 定義 model(數據結構以及 getter/setter、compareTo、equals 等等,不含複雜操做)
│ └── remote 來源於遠端的數據
├── feature 功能
│ ├── feature0 功能 0
│ │ ├── feature0Activity.java
│ │ ├── feature0Fragment.java
│ │ ├── xxAdapter.java
│ │ └── ... 其餘 class
│ └── ...其餘功能
├── injection 依賴注入
├── util 工具類
└── widget 小部件
複製代碼
類名都以 UpperCamelCase
風格編寫。
類名一般是名詞或名詞短語,接口名稱有時多是形容詞或形容詞短語。如今尚未特定的規則或行之有效的約定來命名註解類型。
名詞,採用大駝峯命名法,儘可能避免縮寫,除非該縮寫是衆所周知的, 好比 HTML、URL,若是類名稱中包含單詞縮寫,則單詞縮寫的每一個字母均應大寫。
類 | 描述 | 例如 |
---|---|---|
Activity 類 |
Activity 爲後綴標識 |
歡迎頁面類 WelcomeActivity |
Adapter 類 |
Adapter 爲後綴標識 |
新聞詳情適配器 NewsDetailAdapter |
解析類 | Parser 爲後綴標識 |
首頁解析類 HomePosterParser |
工具方法類 | Utils 或 Manager 爲後綴標識 |
線程池管理類:ThreadPoolManager 日誌工具類: LogUtils (Logger 也可)打印工具類: PrinterUtils |
數據庫類 | 以 DBHelper 後綴標識 |
新聞數據庫:NewsDBHelper |
Service 類 |
以 Service 爲後綴標識 |
時間服務 TimeService |
BroadcastReceiver 類 |
以 Receiver 爲後綴標識 |
推送接收 JPushReceiver |
ContentProvider 類 |
以 Provider 爲後綴標識 |
ShareProvider |
自定義的共享基礎類 | 以 Base 開頭 |
BaseActivity , BaseFragment |
測試類的命名以它要測試的類的名稱開始,以 Test 結束。例如:HashTest
或 HashIntegrationTest
。
接口(interface):命名規則與類同樣採用大駝峯命名法,多以 able 或 ible 結尾,如 interface Runnable
、interface Accessible
。
注意:若是項目採用 MVP,全部 Model、View、Presenter 的接口都以 I 爲前綴,不加後綴,其餘的接口採用上述命名規則。
方法名都以 lowerCamelCase
風格編寫。
方法名一般是動詞或動詞短語。
方法 | 說明 |
---|---|
initXX() |
初始化相關方法,使用 init 爲前綴標識,如初始化佈局 initView() |
isXX() , checkXX() |
方法返回值爲 boolean 型的請使用 is/check 爲前綴標識 |
getXX() |
返回某個值的方法,使用 get 爲前綴標識 |
setXX() |
設置某個屬性值 |
handleXX() , processXX() |
對數據進行處理的方法 |
displayXX() , showXX() |
彈出提示框和提示信息,使用 display/show 爲前綴標識 |
updateXX() |
更新數據 |
saveXX() , insertXX() |
保存或插入數據 |
resetXX() |
重置數據 |
clearXX() |
清除數據 |
removeXX() , deleteXX() |
移除數據或者視圖等,如 removeView() |
drawXX() |
繪製數據或效果相關的,使用 draw 前綴標識 |
常量名命名模式爲 CONSTANT_CASE
,所有字母大寫,用下劃線分隔單詞。那到底什麼算是一個常量?
每一個常量都是一個 static final
字段,但不是全部 static final
字段都是常量。在決定一個字段是不是一個常量時,得考慮它是否真的感受像是一個常量。例如,若是觀測任何一個該實例的狀態是可變的,則它幾乎確定不會是一個常量。只是永遠不打算改變的對象通常是不夠的,它要真的一直不變才能將它示爲常量。
// Constants
static final int NUMBER = 5;
static final ImmutableListNAMES = ImmutableList.of("Ed", "Ann");
static final Joiner COMMA_JOINER = Joiner.on(','); // because Joiner is immutable
static final SomeMutableType[] EMPTY_ARRAY = {};
enum SomeEnum { ENUM_CONSTANT }
// Not constants
static String nonFinal = "non-final";
final String nonStatic = "non-static";
static final SetmutableCollection = new HashSet();
static final ImmutableSetmutableElements = ImmutableSet.of(mutable);
static final Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};
複製代碼
很是量字段名以 lowerCamelCase
風格的基礎上改造爲以下風格:基本結構爲 scope{Type0}VariableName{Type1}
、type0VariableName{Type1}
、variableName{Type1}
。
說明:{}
中的內容爲可選。
注意:全部的 VO(值對象)統一採用標準的 lowerCamelCase 風格編寫,全部的 DTO(數據傳輸對象)就按照接口文檔中定義的字段名編寫。
非公有,非靜態字段命名以 m
開頭。
靜態字段命名以 s
開頭。
其餘字段以小寫字母開頭。
例如:
public class MyClass {
public int publicField;
private static MyClass sSingleton;
int mPackagePrivate;
private int mPrivate;
protected int mProtected;
}
複製代碼
使用 1 個字符前綴來表示做用範圍,1 個字符的前綴必須小寫,前綴後面是由表意性強的一個單詞或多個單詞組成的名字,並且每一個單詞的首寫字母大寫,其它字母小寫,這樣保證了對變量名可以進行正確的斷句。
考慮到 Android 衆多的 UI 控件,爲避免控件和普通成員變量混淆以及更好地表達意思,全部用來表示控件的成員變量統一加上控件縮寫做爲前綴(具體見附錄 UI 控件縮寫表)。
例如:mIvAvatar
、rvBooks
、flContainer
。
變量名中可能會出現量詞,咱們須要建立統一的量詞,它們更容易理解,也更容易搜索。
例如:mFirstBook
、mPreBook
、curBook
。
量詞列表 | 量詞後綴說明 |
---|---|
First |
一組變量中的第一個 |
Last |
一組變量中的最後一個 |
Next |
一組變量中的下一個 |
Pre |
一組變量中的上一個 |
Cur |
一組變量中的當前變量 |
對於表示集合或者數組的很是量字段名,咱們能夠添加後綴來加強字段的可讀性,好比:
集合添加以下後綴:List、Map、Set。
數組添加以下後綴:Arr。
例如:mIvAvatarList
、userArr
、firstNameSet
。
注意:若是數據類型不肯定的話,好比表示的是不少書,那麼使用其複數形式來表示也可,例如
mBooks
。
參數名以 lowerCamelCase
風格編寫,參數應該避免用單個字符命名。
局部變量名以 lowerCamelCase
風格編寫,比起其它類型的名稱,局部變量名能夠有更爲寬鬆的縮寫。
雖然縮寫更寬鬆,但仍是要避免用單字符進行命名,除了臨時變量和循環變量。
即便局部變量是 final
和不可改變的,也不該該把它示爲常量,天然也不能用常量的規則去命名它。
臨時變量一般被取名爲 i
、j
、k
、m
和 n
,它們通常用於整型;c
、d
、e
,它們通常用於字符型。 如:for (int i = 0; i < len; i++)
。
類型變量可用如下兩種風格之一進行命名:
E
, T
, X
, T2
)。RequestT
, FooBarT
)。更多還可參考:阿里巴巴 Java 開發手冊
左大括號不單獨佔一行,與其前面的代碼位於同一行:
class MyClass {
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}
複製代碼
咱們須要在條件語句周圍添加大括號。例外狀況:若是整個條件語句(條件和主體)適合放在同一行,那麼您能夠(但不是必須)將其所有放在一行上。例如,咱們接受如下樣式:
if (condition) {
body();
}
複製代碼
一樣也接受如下樣式:
if (condition) body();
複製代碼
但不接受如下樣式:
if (condition)
body(); // bad!
複製代碼
在可行的狀況下,儘可能編寫短小精煉的方法。咱們瞭解,有些狀況下較長的方法是恰當的,所以對方法的代碼長度沒有作出硬性限制。若是某個方法的代碼超出 40 行,請考慮是否能夠在不破壞程序結構的前提下對其拆解。
這並無惟一的正確解決方案,但若是都使用一致的順序將會提升代碼的可讀性,推薦使用以下排序:
例如:
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
private String mTitle;
private TextView mTextViewTitle;
@Override
public void onCreate() {
...
}
public void setTitle(String title) {
mTitle = title;
}
private void setUpView() {
...
}
static class AnInnerClass {
}
}
複製代碼
若是類繼承於 Android 組件(例如 Activity
或 Fragment
),那麼把重寫函數按照他們的生命週期進行排序是一個很是好的習慣,例如,Activity
實現了 onCreate()
、onDestroy()
、onPause()
、onResume()
,它的正確排序以下所示:
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
做爲其第一個參數。
正相反,咱們把回調接口應該做爲其最後一個參數。
例如:
// 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
,因此,即使是一個小應用,咱們最終也不得不編寫大量的字符串常量。
當時用到這些類的時候,咱們 必須 將它們的鍵定義爲 static final
字段,並遵循如下指示做爲前綴。
類 | 字段名前綴 |
---|---|
SharedPreferences | PREF_ |
Bundle | BUNDLE_ |
Fragment Arguments | ARGUMENT_ |
Intent Extra | EXTRA_ |
Intent Action | ACTION_ |
說明:雖然 Fragment.getArguments()
獲得的也是 Bundle
,但由於這是 Bundle
的經常使用用法,因此特地爲此定義一個不一樣的前綴。
例如:
// 注意:字段的值與名稱相同以免重複問題
static final String PREF_EMAIL = "PREF_EMAIL";
static final String BUNDLE_AGE = "BUNDLE_AGE";
static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID";
// 與意圖相關的項使用完整的包名做爲值的前綴
static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME";
static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER";
複製代碼
當 Activity
或 Fragment
傳遞數據經過 Intent
或 Bundle
時,不一樣值的鍵須遵循上一條所說起到的。
當 Activity
或 Fragment
啓動須要傳遞參數時,那麼它須要提供一個 public static
的函數來幫助啓動或建立它。
這方面,AS 已幫你寫好了相關的 Live Templates,啓動相關 Activity
的只須要在其內部輸入 starter
便可生成它的啓動器,以下所示:
public static void start(Context context, User user) {
Intent starter = new Intent(context, MainActivity.class);
starter.putParcelableExtra(EXTRA_USER, user);
context.startActivity(starter);
}
複製代碼
同理,啓動相關 Fragment
在其內部輸入 newInstance
便可,以下所示:
public static MainFragment newInstance(User user) {
Bundle args = new Bundle();
args.putParcelable(ARGUMENT_USER, user);
MainFragment fragment = new MainFragment();
fragment.setArguments(args);
return fragment;
}
複製代碼
注意:這些函數須要放在
onCreate()
以前的類的頂部;若是咱們使用了這種方式,那麼extras
和arguments
的鍵應該是private
的,由於它們再也不須要暴露給其餘類來使用。
代碼中每一行文本的長度都應該不超過 100 個字符。雖然關於此規則存在不少爭論,但最終決定還是以 100 個字符爲上限,若是行長超過了 100(AS 窗口右側的豎線就是設置的行寬末尾 ),咱們一般有兩種方法來縮減行長。
不過存在如下例外狀況:
這沒有一個準確的解決方案來決定如何換行,一般不一樣的解決方案都是有效的,可是有一些規則能夠應用於常見的狀況。
除賦值操做符以外,咱們把換行符放在操做符以前,例如:
int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
+ theFinalOne;
複製代碼
賦值操做符的換行咱們放在其後,例如:
int longName =
anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;
複製代碼
當同一行中調用多個函數時(好比使用構建器時),對每一個函數的調用應該在新的一行中,咱們把換行符插入在 .
以前。
例如:
Picasso.with(context).load("https://blankj.com/images/avatar.jpg").into(ivAvatar);
複製代碼
咱們應該使用以下規則:
Picasso.with(context)
.load("https://blankj.com/images/avatar.jpg")
.into(ivAvatar);
複製代碼
當一個方法有不少參數或者參數很長的時候,咱們應該在每一個 ,
後面進行換行。
好比:
loadPicture(context, "https://blankj.com/images/avatar.jpg", ivAvatar, "Avatar of the user", clickListener);
複製代碼
咱們應該使用以下規則:
loadPicture(context,
"https://blankj.com/images/avatar.jpg",
ivAvatar,
"Avatar of the user",
clickListener);
複製代碼
RxJava 的每一個操做符都須要換新行,而且把換行符插入在 .
以前。
例如:
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;
}
});
}
複製代碼
資源文件命名爲所有小寫,採用下劃線命名法。
若是是組件化開發,咱們能夠在組件和公共模塊間建立一個 ui 模塊來專門存放資源文件,而後讓每一個組件都依賴 ui 模塊。這樣作的好處是若是老項目要實現組件化的話,只需把資源文件都放入 ui 模塊便可,若是想對資源文件進行分包,能夠參考我這篇文章:Android Studio 下對資源進行分包;還避免了多個模塊間資源不能複用的問題。
若是是三方庫開發,其使用到的資源文件及相關的 name
都應該使用庫名做爲前綴,這樣作能夠避免三方庫資源和實際應用資源重名的衝突。
安卓主要包含屬性動畫和視圖動畫,其視圖動畫包括補間動畫和逐幀動畫。屬性動畫文件須要放在 res/animator/
目錄下,視圖動畫文件需放在 res/anim/
目錄下。
命名規則:{模塊名_}邏輯名稱
。
說明:{}
中的內容爲可選,邏輯名稱
可由多個單詞加下劃線組成。
例如:refresh_progress.xml
、market_cart_add.xml
、market_cart_remove.xml
。
若是是普通的補間動畫或者屬性動畫,可採用:動畫類型_方向
的命名方式。
例如:
名稱 | 說明 |
---|---|
fade_in |
淡入 |
fade_out |
淡出 |
push_down_in |
從下方推入 |
push_down_out |
從下方推出 |
push_left |
推向左方 |
slide_in_from_top |
從頭部滑動進入 |
zoom_enter |
變形進入 |
slide_in |
滑動進入 |
shrink_to_middle |
中間縮小 |
專門存放顏色相關的資源文件。
命名規則:類型{_模塊名}_邏輯名稱
。
說明:{}
中的內容爲可選。
例如:sel_btn_font.xml
。
顏色資源也能夠放於 res/drawable/
目錄,引用時則用 @drawable
來引用,但不推薦這麼作,最好仍是把二者分開。
res/drawable/
目錄下放的是位圖文件(.png、.9.png、.jpg、.gif)或編譯爲可繪製對象資源子類型的 XML 文件,而 res/mipmap/
目錄下放的是不一樣密度的啓動圖標,因此 res/mipmap/
只用於存放啓動圖標,其他圖片資源文件都應該放到 res/drawable/
目錄下。
命名規則:類型{_模塊名}_邏輯名稱
、類型{_模塊名}_顏色
。
說明:{}
中的內容爲可選;類型
能夠是可繪製對象資源類型,也能夠是控件類型(具體見附錄UI 控件縮寫表);最後可加後綴 _small
表示小圖,_big
表示大圖。
例如:
名稱 | 說明 |
---|---|
btn_main_about.png |
主頁關於按鍵 類型_模塊名_邏輯名稱 |
btn_back.png |
返回按鍵 類型_邏輯名稱 |
divider_maket_white.png |
商城白色分割線 類型_模塊名_顏色 |
ic_edit.png |
編輯圖標 類型_邏輯名稱 |
bg_main.png |
主頁背景 類型_邏輯名稱 |
btn_red.png |
紅色按鍵 類型_顏色 |
btn_red_big.png |
紅色大按鍵 類型_顏色 |
ic_head_small.png |
小頭像圖標 類型_邏輯名稱 |
bg_input.png |
輸入框背景 類型_邏輯名稱 |
divider_white.png |
白色分割線 類型_顏色 |
bg_main_head.png |
主頁頭部背景 類型_模塊名_邏輯名稱 |
def_search_cell.png |
搜索頁面默認單元圖片 類型_模塊名_邏輯名稱 |
ic_more_help.png |
更多幫助圖標 類型_邏輯名稱 |
divider_list_line.png |
列表分割線 類型_邏輯名稱 |
sel_search_ok.xml |
搜索界面確認選擇器 類型_模塊名_邏輯名稱 |
shape_music_ring.xml |
音樂界面環形形狀 類型_模塊名_邏輯名稱 |
若是有多種形態,如按鈕選擇器:sel_btn_xx.xml
,採用以下命名:
名稱 | 說明 |
---|---|
sel_btn_xx |
做用在 btn_xx 上的 selector |
btn_xx_normal |
默認狀態效果 |
btn_xx_pressed |
state_pressed 點擊效果 |
btn_xx_focused |
state_focused 聚焦效果 |
btn_xx_disabled |
state_enabled 不可用效果 |
btn_xx_checked |
state_checked 選中效果 |
btn_xx_selected |
state_selected 選中效果 |
btn_xx_hovered |
state_hovered 懸停效果 |
btn_xx_checkable |
state_checkable 可選效果 |
btn_xx_activated |
state_activated 激活效果 |
btn_xx_window_focused |
state_window_focused 窗口聚焦效果 |
注意:使用 Android Studio 的插件 SelectorChapek 能夠快速生成 selector,前提是命名要規範。
命名規則:類型_模塊名
、類型{_模塊名}_邏輯名稱
。
說明:{}
中的內容爲可選。
例如:
名稱 | 說明 |
---|---|
activity_main.xml |
主窗體 類型_模塊名 |
activity_main_head.xml |
主窗體頭部 類型_模塊名_邏輯名稱 |
fragment_music.xml |
音樂片斷 類型_模塊名 |
fragment_music_player.xml |
音樂片斷的播放器 類型_模塊名_邏輯名稱 |
dialog_loading.xml |
加載對話框 類型_邏輯名稱 |
ppw_info.xml |
信息彈窗(PopupWindow) 類型_邏輯名稱 |
item_main_song.xml |
主頁歌曲列表項 類型_模塊名_邏輯名稱 |
菜單相關的資源文件應放在該目錄下。
命名規則:{模塊名_}邏輯名稱
說明:{}
中的內容爲可選。
例如:main_drawer.xml
、navigation.xml
。
values/
資源文件下的文件都以 s
結尾,如 attrs.xml
、colors.xml
、dimens.xml
,起做用的不是文件名稱,而是 <resources>
標籤下的各類標籤,好比 <style>
決定樣式,<color>
決定顏色,因此,能夠把一個大的 xml
文件分割成多個小的文件,好比能夠有多個 style
文件,如 styles.xml
、styles_home.xml
、styles_item_details.xml
、styles_forms.xml
。
<color>
的 name
命名使用下劃線命名法,在你的 colors.xml
文件中應該只是映射顏色的名稱一個 ARGB 值,而沒有其它的。不要使用它爲不一樣的按鈕來定義 ARGB 值。
例如,不要像下面這樣作:
<resources>
<color name="button_foreground">#FFFFFF</color>
<color name="button_background">#2A91BD</color>
<color name="comment_background_inactive">#5F5F5F</color>
<color name="comment_background_active">#939393</color>
<color name="comment_foreground">#FFFFFF</color>
<color name="comment_foreground_important">#FF9D2F</color>
...
<color name="comment_shadow">#323232</color>
複製代碼
使用這種格式,會很是容易重複定義 ARGB 值,並且若是應用要改變基色的話會很是困難。同時,這些定義是跟一些環境關聯起來的,如 button
或者 comment
,應該放到一個按鈕風格中,而不是在 colors.xml
文件中。
相反,應該這樣作:
<resources>
<!-- grayscale -->
<color name="white" >#FFFFFF</color>
<color name="gray_light">#DBDBDB</color>
<color name="gray" >#939393</color>
<color name="gray_dark" >#5F5F5F</color>
<color name="black" >#323232</color>
<!-- basic colors -->
<color name="green">#27D34D</color>
<color name="blue">#2A91BD</color>
<color name="orange">#FF9D2F</color>
<color name="red">#FF432F</color>
</resources>
複製代碼
嚮應用設計者那裏要這個調色板,名稱不須要跟 "green"
、"blue"
等等相同。"brand_primary"
、"brand_secondary"
、"brand_negative"
這樣的名字也是徹底能夠接受的。像這樣規範的顏色很容易修改或重構,會使應用一共使用了多少種不一樣的顏色變得很是清晰。一般一個具備審美價值的 UI 來講,減小使用顏色的種類是很是重要的。
注意:若是某些顏色和主題有關,那就單獨寫一個
colors_theme.xml
。
像對待 colors.xml
同樣對待 dimens.xml
文件,與定義顏色調色板同樣,你同時也應該定義一個空隙間隔和字體大小的「調色板」。 一個好的例子,以下所示:
<resources>
<!-- font sizes -->
<dimen name="font_22">22sp</dimen>
<dimen name="font_18">18sp</dimen>
<dimen name="font_15">15sp</dimen>
<dimen name="font_12">12sp</dimen>
<!-- typical spacing between two views -->
<dimen name="spacing_40">40dp</dimen>
<dimen name="spacing_24">24dp</dimen>
<dimen name="spacing_14">14dp</dimen>
<dimen name="spacing_10">10dp</dimen>
<dimen name="spacing_4">4dp</dimen>
<!-- typical sizes of views -->
<dimen name="button_height_60">60dp</dimen>
<dimen name="button_height_40">40dp</dimen>
<dimen name="button_height_32">32dp</dimen>
</resources>
複製代碼
佈局時在寫 margins
和 paddings
時,你應該使用 spacing_xx
尺寸格式來佈局,而不是像對待 string
字符串同樣直接寫值,像這樣規範的尺寸很容易修改或重構,會使應用全部用到的尺寸一目瞭然。 這樣寫會很是有感受,會使組織和改變風格或佈局很是容易。
<string>
的 name
命名使用下劃線命名法,採用如下規則:{模塊名_}邏輯名稱
,這樣方便同一個界面的全部 string
都放到一塊兒,方便查找。
名稱 | 說明 |
---|---|
main_menu_about |
主菜單按鍵文字 |
friend_title |
好友模塊標題欄 |
friend_dialog_del |
好友刪除提示 |
login_check_email |
登陸驗證 |
dialog_title |
彈出框標題 |
button_ok |
確認鍵 |
loading |
加載文字 |
<style>
的 name
命名使用大駝峯命名法,幾乎每一個項目都須要適當的使用 styles.xml
文件,由於對於一個視圖來講,有一個重複的外觀是很常見的,將全部的外觀細節屬性(colors
、padding
、font
)放在 styles.xml
文件中。 在應用中對於大多數文本內容,最起碼你應該有一個通用的 styles.xml
文件,例如:
<style name="ContentText">
<item name="android:textSize">@dimen/font_normal</item>
<item name="android:textColor">@color/basic_black</item>
</style>
複製代碼
應用到 TextView
中:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/price"
style="@style/ContentText"
/>
複製代碼
或許你須要爲按鈕控件作一樣的事情,不要中止在那裏,將一組相關的和重複 android:xxxx
的屬性放到一個通用的 <style>
中。
命名規則:view 縮寫{_模塊名}_邏輯名
,例如: btn_main_search
、btn_back
。
若是在項目中有用黃油刀的話,使用 AS 的插件:ButterKnife Zelezny,能夠很是方便幫助你生成註解;沒用黃油刀的話可使用 Android Code Generator 插件。
Android 開發存在着衆多版本的不一樣,好比 compileSdkVersion
、minSdkVersion
、targetSdkVersion
以及項目中依賴第三方庫的版本,不一樣的 module 及不一樣的開發人員都有不一樣的版本,因此須要一個統一版本規範的文件。
具體能夠參考我寫的這篇博文:Android 開發之版本統一規範。
若是是開發多個系統級別的應用,當多個應用同時用到相同的 so
庫時,必定要確保 so
庫的版本一致,不然可能會引起應用崩潰。
別再閉門造車了,用用最新最火的技術吧,安利一波:Android 流行框架查速表,順便帶上本身的乾貨:Android 開發人員不得不收集的代碼。
但願 Team 能用時下較新的技術,對開源庫的選取,通常都須要選擇比較穩定的版本,做者在維護的項目,要考慮做者對 issue 的解決,以及開發者的知名度等各方面。選取以後,必定的封裝是必要的。
我的推薦 Team 可以使用以下優秀輪子:
爲了減小他人閱讀你代碼的痛苦值,請在關鍵地方作好註釋。
每一個類完成後應該有做者姓名和聯繫方式的註釋,對本身的代碼負責。
/** * <pre> * author : Blankj * e-mail : xxx@xx * time : 2017/03/07 * desc : xxxx 描述 * version: 1.0 * </pre> */
public class WelcomeActivity {
...
}
複製代碼
具體能夠在 AS 中本身配製,進入 Settings -> Editor -> File and Code Templates -> Includes -> File Header,輸入
/** * <pre> * author : ${USER} * e-mail : xxx@xx * time : ${YEAR}/${MONTH}/${DAY} * desc : * version: 1.0 * </pre> */
複製代碼
這樣即可在每次新建類的時候自動加上該頭註釋。
每個成員方法(包括自定義成員方法、覆蓋方法、屬性方法)的方法頭都必須作方法頭註釋,在方法前一行輸入 /** + 回車
或者設置 Fix doc comment
(Settings -> Keymap -> Fix doc comment)快捷鍵,AS 便會幫你生成模板,咱們只須要補全參數便可,以下所示。
/** * bitmap 轉 byteArr * * @param bitmap bitmap 對象 * @param format 格式 * @return 字節數組 */
public static byte[] bitmap2Bytes(Bitmap bitmap, CompressFormat format) {
if (bitmap == null) return null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(format, 100, baos);
return baos.toByteArray();
}
複製代碼
塊註釋與其周圍的代碼在同一縮進級別。它們能夠是 /* ... */
風格,也能夠是 // ...
風格(//
後最好帶一個空格)。對於多行的 /* ... */
註釋,後續行必須從 *
開始, 而且與前一行的 *
對齊。如下示例註釋都是 OK 的。
/* * This is * okay. */
// And so
// is this.
/* Or you can * even do this. */
複製代碼
註釋不要封閉在由星號或其它字符繪製的框架裏。
Tip:在寫多行註釋時,若是你但願在必要時能從新換行(即註釋像段落風格同樣),那麼使用
/* ... */
。
AS 已幫你集成了一些註釋模板,咱們只須要直接使用便可,在代碼中輸入 todo
、fixme
等這些註釋模板,回車後便會出現以下注釋。
// TODO: 17/3/14 須要實現,但目前還未實現的功能的說明
// FIXME: 17/3/14 須要修正,甚至代碼是錯誤的,不能工做,須要修復的說明
複製代碼
業務開發完成以後,開發人員作單元測試,單元測試完成以後,保證單元測試所有經過,同時單元測試代碼覆蓋率達到必定程度(這個須要開發和測試約定,理論上越高越好),開發提測。
測試類的名稱應該是所測試類的名稱加 Test
,咱們建立 DatabaseHelper
的測試類,其名應該叫 DatabaseHelperTest
。
測試函數被 @Test
所註解,函數名一般以被測試的方法爲前綴,而後跟隨是前提條件和預期的結果。
void methodName 前提條件和預期結果()
void signInWithEmptyEmailFails()
注意:若是函數足夠清晰,那麼前提條件和預期的結果是能夠省略的。
有時一個類可能包含大量的方法,同時須要對每一個方法進行幾回測試。在這種狀況下,建議將測試類分紅多個類。例如,若是 DataManager
包含不少方法,咱們可能要把它分紅 DataManagerSignInTest
、DataManagerLoadUsersTest
等等。
每一個 Espresso 測試一般是針對 Activity
,因此其測試名就是其被測的 Activity
的名稱加 Test
,好比 SignInActivityTest
。
合理佈局,有效運用 <merge>
、<ViewStub>
、<include>
標籤;
Activity
和 Fragment
裏面有許多重複的操做以及操做步驟,因此咱們都須要提供一個 BaseActivity
和 BaseFragment
,讓全部的 Activity
和 Fragment
都繼承這個基類。
方法基本上都按照調用的前後順序在各自區塊中排列;
相關功能做爲小區塊放在一塊兒(或者封裝掉);
當一個類有多個構造函數,或是多個同名函數,這些函數應該按順序出如今一塊兒,中間不要放進其它函數;
數據提供統一的入口。不管是在 MVP、MVC 仍是 MVVM 中,提供一個統一的數據入口,均可以讓代碼變得更加易於維護。好比可以使用一個 DataManager
,把 http
、preference
、eventpost
、database
都放在 DataManager
裏面進行操做,咱們只須要與 DataManager
打交道;
多用組合,少用繼承;
提取方法,去除重複代碼。對於必要的工具類抽取也很重要,這在之後的項目中是能夠重用的。
可引入 Dagger2
減小模塊之間的耦合性。Dagger2
是一個依賴注入框架,使用代碼自動生成建立依賴關係須要的代碼。減小不少模板化的代碼,更易於測試,下降耦合,建立可複用可互換的模塊;
項目引入 RxAndroid
響應式編程,能夠極大的減小邏輯代碼;
經過引入事件總線,如:EventBus
、AndroidEventBus
、RxBus
,它容許咱們在 DataLayer
中發送事件,以便 ViewLayer
中的多個組件都可以訂閱到這些事件,減小回調;
儘量使用局部變量;
及時關閉流;
儘可能減小對變量的重複計算;
以下面的操做:
for (int i = 0; i < list.size(); i++) {
...
}
複製代碼
建議替換爲:
for (int i = 0, len = list.size(); i < len; i++) {
...
}
複製代碼
儘可能採用懶加載的策略,即在須要的時候才建立;
例如:
String str = "aaa";
if (i == 1) {
list.add(str);
}
複製代碼
建議替換爲:
if (i == 1) {
String str = "aaa";
list.add(str);
}
複製代碼
不要在循環中使用 try…catch…
,應該把其放在最外層;
使用帶緩衝的輸入輸出流進行 IO 操做;
儘可能使用 HashMap
、ArrayList
、StringBuilder
,除非線程安全須要,不然不推薦使用 HashTable
、Vector
、StringBuffer
,後三者因爲使用同步機制而致使了性能開銷;
儘可能在合適的場合使用單例;
使用單例能夠減輕加載的負擔、縮短加載的時間、提升加載的效率,但並非全部地方都適用於單例,簡單來講,單例主要適用於如下三個方面:
控制資源的使用,經過線程同步來控制資源的併發訪問。
控制實例的產生,以達到節約資源的目的。
控制數據的共享,在不創建直接關聯的條件下,讓多個不相關的進程或線程之間實現通訊。
把一個基本數據類型轉爲字符串,基本數據類型.toString()
是最快的方式,String.valueOf(數據)
次之,數據 + ""
最慢;
使用 AS 自帶的 Lint 來優化代碼結構(什麼,你不會?右鍵 module、目錄或者文件,選擇 Analyze -> Inspect Code);
最後不要忘了內存泄漏的檢測;
最後囉嗦幾句:
名稱 | 縮寫 |
---|---|
Button | btn |
CheckBox | cb |
EditText | et |
FrameLayout | fl |
GridView | gv |
ImageButton | ib |
ImageView | iv |
LinearLayout | ll |
ListView | lv |
ProgressBar | pb |
RadioButtion | rb |
RecyclerView | rv |
RelativeLayout | rl |
ScrollView | sv |
SeekBar | sb |
Spinner | spn |
TextView | tv |
ToggleButton | tb |
VideoView | vv |
WebView | wv |
名稱 | 縮寫 |
---|---|
average | avg |
background | bg(主要用於佈局和子佈局的背景) |
buffer | buf |
control | ctrl |
current | cur |
default | def |
delete | del |
document | doc |
error | err |
escape | esc |
icon | ic(主要用在 App 的圖標) |
increment | inc |
information | info |
initial | init |
image | img |
Internationalization | I18N |
length | len |
library | lib |
message | msg |
password | pwd |
position | pos |
previous | pre |
selector | sel(主要用於某一 view 多種狀態,不只包括 ListView 中的 selector,還包括按鈕的 selector) |
server | srv |
string | str |
temporary | tmp |
window | win |
程序中使用單詞縮寫原則:不要用縮寫,除非該縮寫是約定俗成的。
Project and code style guidelines