文章目錄
- 前言
- 一、Android Studio 規範
- 二、Kotlin命名
- 三、Kotlin代碼樣式
- 3.1 類型推斷
- 3.2 分號
- 3.3 聲明When
- 3.4 可空類型
- 3.5 語言
- 3.6 大括號
- 3.7 編寫簡短方法
- 3.8 類成員的順序
- 3.9 函數參數的排序
- 3.10 字符串常量的命名和值
- 3.11 行長限制
- 四、Android資源文件
- 4.1 動畫資源文件(anim/ 和 animator/)
- 4.2 顏色資源文件(color/)
- 4.3 圖片資源文件(drawable/ 和 mipmap/)
- 4.4 佈局資源文件(layout/)
- 4.5 菜單資源文件(menu/)
- 4.6 values 資源文件(values/)
- 五、Android基本組件
- 六、代碼
- 七、UI與佈局
- 八、安全
- 九、註釋
- 十、測試
- 十一、Git使用
- 附錄
- 參考
前言
爲了有利於項目維護、增強代碼可讀性、提升 Code Review 效率以及規範團隊安卓開發,故提出以下安卓開發規範。
一、Android Studio 規範
- 儘量使用最新的穩定版 Android Studio 進行開發;
- 編碼格式統一爲 UTF-8;
- 編輯完 .java、.kt、.xml 等文件後一定要 格式化,格式化,格式化(如果團隊有公共的樣式包,那就遵循它,否則統一使用 AS 默認模板即可,
Mac
下可以使用快捷鍵cmd + alt + L
進行代碼格式化,Window
下可以使用快捷鍵ctrl + alt + L
進行代碼格式化); - 刪除多餘的 import,減少警告出現,
Mac
下可以使用快捷鍵ctrl + alt + O
進行 import 優化,Window
下可以使用快捷鍵ctrl + alt + O
進行 import 優化;
二、Kotlin命名
代碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。正確的英文拼寫和語法可以讓閱讀者易於理解,避免歧義。
注意:即使純拼音命名方式也要避免採用。但
alibaba
、taobao
、youku
、hangzhou
等國際通用的名稱,可視同英文。
2.1 包名
包名全部小寫,連續的單詞只是簡單地連接起來,不使用下劃線,採用反域名命名規則,全部使用小寫字母。一級包名是頂級域名,通常爲 com
、edu
、gov
、net
、org
等,二級包名爲公司名,三級包名根據應用進行命名,後面就是對包名的劃分了,關於包名的劃分,推薦採用 PBF(按功能分包 Package By Feature)。
// BAD
com.RayWenderlich.funky_widget
// GOOD
com.raywenderlich.funkywidget
2.2 類名
類名通常是名詞或名詞短語,接口名稱有時可能是形容詞或形容詞短語。現在還沒有特定的規則或行之有效的約定來命名註解類型。
名詞,採用大駝峯命名法,儘量避免縮寫,除非該縮寫是衆所周知的, 比如 HTML、URL,如果類名稱中包含單詞縮寫,則單詞縮寫的每個字母均應大寫。
測試類的命名以它要測試的類的名稱開始,以 Test 結束。例如:HashTest
或 HashIntegrationTest
。
接口(interface):命名規則與類一樣採用大駝峯命名法,多以 able 或 ible 結尾,如 interface Runnable
、interface Accessible
;或者以 I 爲前綴。
類 | 描述 | 示例 |
---|---|---|
Activity 類 | 以Activity 爲後綴標識 | 歡迎頁面類 WelcomeActivity |
Adapter 類 | 以Adapter 爲後綴標識 | 新聞詳情適配器 NewsDetailAdapter |
解析類 | 以Parser 爲後綴標識 | 首頁解析類 HomePosterParser |
工具方法類 | 以Util、Tool、Manager 爲後綴標識 | 線程池管理類:ThreadPoolManager,日誌工具類:LogUtil,網絡請求工具類:HttpTool |
數據庫類 | 以 DBHelper 後綴標識 | 新聞數據庫:NewsDBHelper |
Service 類 | 以 Service 爲後綴標識 | 時間服務 TimeService 用戶組件服務IUserService |
BroadcastReceiver 類 | 以 Receiver 爲後綴標識 | 推送接收 JPushReceiver |
ContentProvider 類 | 以 Provider 爲後綴標識 | ShareProvider |
自定義的共享基礎類 | 以 Base 爲前綴 | BaseActivity, BaseFragment |
上面提到工具類以 Util 和 Tool 爲後綴,那麼 Util 和 Tool 的區別是什麼?Util 是無業務邏輯的,Tool 是有業務邏輯的。比如 HttpUtil 只是包含了基本網絡請求,而 HttpTool 中包含了項目的一些配置,如在每個請求增加 token 。也可以這麼說,HttpUtil 可以跨項目使用,而 HttpTool 只能在該項目中使用。
2.3 方法名
方法名都以 lowerCamelCase
風格編寫。
方法名通常是動詞或動詞短語。
方法 | 說明 |
---|---|
initXX() | 初始化相關方法,如初始化佈局 initView() |
isXX(), checkXX() | 方法返回值爲 boolean 型 |
handleXX(), processXX() | 對數據進行處理的方法 |
displayXX(), showXX() | 彈出提示框和提示信息 |
resetXX() | 重置數據 |
clearXX() | 清除數據 |
drawXX() | 繪製數據或效果相關的 |
setXX() | 設置某個屬性值 |
getXX() | 返回某個值或單個對象 |
listXX() | 返回多個對象 |
countXX() | 返回統計值 |
saveXX(), insertXX() | 保存或插入數據 |
removeXX(), deleteXX() | 移除數據或者視圖等,如 removeView() |
updateXX() | 更新數據 |
2.4 常量名
常量名命名模式爲 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"};
Android SDK 中的很多類都用到了鍵值對函數,比如 SharedPreferences
、Bundle
、Intent
,所以,即便是一個小應用,我們最終也不得不編寫大量的字符串常量。
當時用到這些類的時候,我們 必須 將它們的鍵定義爲 const val
字段,並遵循以下指示作爲前綴。
類 | 字段名前綴 |
---|---|
SharedPreferences | PREF_ |
Bundle | BUNDLE_ |
Fragment Arguments | ARGUMENT_ |
Intent Extra | EXTRA_ |
Intent Action | ACTION_ |
save instance state | SAVED_ |
雖然
Fragment.getArguments()
和onSaveInstanceState
也是使用的Bundle
,但爲了區分不同的使用場景,所以定義了不同的前綴。
2.5 非常量字段名
非常量字段名以 lowerCamelCase
風格的基礎上改造爲如下風格:基本結構爲 scope{Type0}VariableName{Type1}
、type0VariableName{Type1}
、variableName{Type1}
。
說明:{}
中的內容爲可選。
注意:所有的 VO(值對象)統一採用標準的 lowerCamelCase 風格編寫,所有的 DTO(數據傳輸對象)就按照接口文檔中定義的字段名編寫。
2.5.1 scope(範圍)
非公有,非靜態字段命名以 m
開頭。
靜態字段命名以 s
開頭。
其他字段以小寫字母開頭。
例如:
public class MyClass {
public int publicField;
private static MyClass sSingleton;
int mPackagePrivate;
private int mPrivate;
protected int mProtected;
}
使用 1 個字符前綴來表示作用範圍,1 個字符的前綴必須小寫,前綴後面是由表意性強的一個單詞或多個單詞組成的名字,而且每個單詞的首寫字母大寫,其它字母小寫,這樣保證了對變量名能夠進行正確的斷句。
通過IDE 自動生成get 、set 和構造函數的時候,這個沒有任何實際意義的m前綴會被包含到變量名稱當中去,顯得很low也很容易影響可讀性。在 AS 中,Settings -> Editor -> Code Style -> Java -> Code Generation 中,Field Name prefix 設置 m,Static Field Name prefix 設置 s。這樣 AS 就可以識別了,自動生成方法的時候會去掉 s 或 m。
2.5.2 Type0(控件類型)
考慮到 Android 衆多的 UI 控件,爲避免控件和普通成員變量混淆以及更好地表達意思,所有用來表示控件的成員變量統一加上控件縮寫作爲前綴(具體見附錄 [UI 控件縮寫表](#UI 控件縮寫表))。
例如:mIvAvatar
、rvBooks
、flContainer
。
2.5.3 VariableName(變量名)
變量名中可能會出現量詞,我們需要創建統一的量詞,它們更容易理解,也更容易搜索。
例如:mFirstBook
、mPreBook
、curBook
。
量詞列表 | 量詞後綴說明 |
---|---|
First |
一組變量中的第一個 |
Last |
一組變量中的最後一個 |
Next |
一組變量中的下一個 |
Pre |
一組變量中的上一個 |
Cur |
一組變量中的當前變量 |
2.5.4 Type1(數據類型)
對於表示集合或者數組的非常量字段名,我們可以添加後綴來增強字段的可讀性,比如:
集合添加如下後綴:List、Map、Set。
數組添加如下後綴:Arr。
例如:mIvAvatarList
、userArr
、firstNameSet
。
注意:如果數據類型不確定的話,比如表示的是很多書,那麼使用其複數形式來表示也可,例如
mBooks
。
2.6 參數名
參數名以 lowerCamelCase
風格編寫。
2.7 局部變量名
局部變量名以 lowerCamelCase
風格編寫,比起其它類型的名稱,局部變量名可以有更爲寬鬆的縮寫。
雖然縮寫更寬鬆,但還是要避免用單字符進行命名,除了臨時變量和循環變量。
即使局部變量是 final
和不可改變的,也不應該把它示爲常量,自然也不能用常量的規則去命名它。
2.8 臨時變量
臨時變量通常被取名爲 i
、j
、k
、m
和 n
,它們一般用於整型;c
、d
、e
,它們一般用於字符型。 如:for (int i = 0; i < len; i++)
。
2.9 泛型變量名
類型變量可用以下兩種風格之一進行命名:
- 單個的大寫字母,後面可以跟一個數字(如:
E
,T
,X
,T2
)。 - 以類命名方式(參考類名),後面加個大寫的 T(如:
RequestT
,FooBarT
)。
2.10 id命名
插件 apply plugin: 'kotlin-android-extensions'
,可以在代碼中通過 xml 定義的控件 id 直接操作控件,而不用寫 findViewById
,所以這裏控件的命名直接使用 lowerCamelCase
風格編寫。
命名規則:view 縮寫{模塊名}邏輯名
,例如: btnMainSearch
、btnBack
。
三、Kotlin代碼樣式
3.1 類型推斷
在可能的情況下,應選擇使用類型推斷代替顯式聲明實際的類型。
// BAD
val something: MyType = MyType()
val meaningOfLife: Int = 42
// GOOD
val something = MyType()
val meaningOfLife = 42
3.2 分號
應該儘可能避免在Kotlin中使用分號。
// BAD
val horseGiftedByTrojans = true;
if (horseGiftedByTrojans) {
bringHorseIntoWalledCity();
}
// GOOD
val horseGiftedByTrojans = true
if (horseGiftedByTrojans) {
bringHorseIntoWalledCity()
}
3.3 聲明When
如果應該以相同的方式處理它們,請使用逗號分隔。始終包括else情況。
// BAD
when (anInput) {
1 -> doSomethingForCaseOne()
2 -> doSomethingForCaseOneOrTwo()
3 -> doSomethingForCaseThree()
}
// GOOD
when (anInput) {
1, 2 -> doSomethingForCaseOneOrTwo()
3 -> doSomethingForCaseThree()
else -> println("No case satisfied")
}
3.4 可空類型
避免使用 !! ,減少出現空指針的情況。
// BAD
editText!!.setText("foo")
tvTitle.setText(title!!)
// GOOD
editText?.setText("foo")
tvTitle.setText(title ?: "") or title?.let { tvTitle.setText(it) }
3.5 語言
使用美式英語 ??拼寫。
// BAD
val colourName = "red"
// GOOD
val colorName = "red"
3.6 大括號
左大括號不單獨佔一行,與其前面的代碼位於同一行:
class MyClass {
fun func(): Int {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}
我們需要在條件語句周圍添加大括號。例外情況:如果整個條件語句(條件和主體)適合放在同一行,那麼您可以(但不是必須)將其全部放在一行上。例如,我們接受以下樣式:
if (condition) {
body()
}
同樣也接受以下樣式:
if (condition) body()
但不接受以下樣式:
if (condition)
body() // bad!
3.7 編寫簡短方法
在可行的情況下,儘量編寫短小精煉的方法。有些情況下較長的方法是恰當的,因此對方法的代碼長度沒有做出硬性限制。如果某個方法的代碼超出 40 行,請考慮是否可以在不破壞程序結構的前提下對其拆解,一個方法最好只做一件事情。
3.8 類成員的順序
這並沒有唯一的正確解決方案,但如果都使用一致的順序將會提高代碼的可讀性,推薦使用如下排序:
- 常量
- 字段(public -> protected -> private)
- 構造函數
- 重寫函數和回調 (在Android 中,應該將生命週期的函數放在前面)
- 公有函數
- 私有函數
- 內部類或接口
class MainActivity : Activity() {
companion object {
private const val TAG = "tag"
}
var updateTime = 0L
protected var mContent = ""
private var mTitle = ""
private lateinit var mTextViewTitle: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
}
fun setTitle(title:String){
mTitle = title
}
private fun setUpView(){
...
}
inner class AnInnerClass{
}
}
如果類繼承於 Android 組件(例如 Activity
或 Fragment
),那麼把重寫函數按照他們的生命週期進行排序是一個非常好的習慣,例如,Activity
實現了 onCreate()
、onDestroy()
、onPause()
、onResume()
,它的正確排序如下所示:
class MainActivity : Activity() {
// Order matches Activity lifecycle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onResume() {
super.onResume()
}
override fun onPause() {
super.onPause()
}
override fun onDestroy() {
super.onDestroy()
}
}
3.9 函數參數的排序
在 Android 開發過程中,Context
在函數參數中是再常見不過的了,我們最好把 Context
作爲其第一個參數。
把回調接口或函數應該作爲其最後一個參數。
// Context always goes first
fun loadUser(context: Context, userId: Int): User
// Callbacks or funcation always go last
fun loadUserAsync(context: Context, userId: Int, callback: UserCallback)
fun loadUserAsync(context: Context, userId: Int, action: (User) -> Unit)
3.10 字符串常量的命名和值
Android SDK 中的很多類都用到了鍵值對函數,比如 SharedPreferences
、Bundle
、Intent
,所以,即便是一個小應用,我們最終也不得不編寫大量的字符串常量。
當時用到這些類的時候,我們 必須 將它們的鍵定義爲 static final
字段,並遵循以下指示作爲前綴。
類 | 字段名前綴 |
---|---|
SharedPreferences | PREF_ |
Bundle | BUNDLE_ |
Fragment Arguments | ARGUMENT_ |
Intent Extra | EXTRA_ |
Intent Action | ACTION_ |
onSaveInstanceState | SAVED_ |
說明:雖然 Fragment.getArguments()
和 onSaveInstanceState
也是 Bundle
,但因爲這是 Bundle
的常用用法,所以特意爲此定義一個不同的前綴。
3.11 行長限制
代碼中每一行文本的長度都應該不超過 100 個字符。雖然關於此規則存在很多爭論,但最終決定仍是以 100 個字符爲上限,如果行長超過了 100(AS 窗口右側的豎線就是設置的行寬末尾 ),我們通常有兩種方法來縮減行長。
- 提取一個局部變量或方法(最好)。
- 使用換行符將一行換成多行。
不過存在以下例外情況:
- 如果備註行包含長度超過 100 個字符的示例命令或文字網址,那麼爲了便於剪切和粘貼,該行可以超過 100 個字符。
- 導入語句行可以超出此限制,因爲用戶很少會看到它們(這也簡化了工具編寫流程)。
3.11.1 換行策略
這沒有一個準確的解決方案來決定如何換行,通常不同的解決方案都是有效的,但是有一些規則可以應用於常見的情況。
3.11.2 操作符的換行
除賦值操作符之外,我們把換行符放在操作符之前,例如:
int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
+ theFinalOne;
賦值操作符的換行我們放在其後,例如:
int longName =
anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;
3.11.3 函數鏈的換行
當同一行中調用多個函數時(比如使用構建器時),對每個函數的調用應該在新的一行中,我們把換行符插入在 .
之前。
例如:
Picasso.with(context).load("https://blankj.com/images/avatar.jpg").into(ivAvatar);
我們應該使用如下規則:
Picasso.with(context)
.load("https://blankj.com/images/avatar.jpg")
.into(ivAvatar);
3.11.4 多參數的換行
當一個方法有很多參數或者參數很長的時候,我們應該在每個 ,
後面進行換行。
比如:
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);
3.11.5 RxJava 鏈式的換行
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;
}
});
}
四、Android資源文件
資源文件命名爲全部小寫,採用下劃線命名法,資源文件需帶module名作爲前綴。
4.1 動畫資源文件(anim/ 和 animator/)
安卓主要包含屬性動畫和視圖動畫,其視圖動畫包括補間動畫和逐幀動畫。屬性動畫文件需要放在 res/animator/
目錄下,視圖動畫文件需放在 res/anim/
目錄下。
命名規則:module_邏輯名稱
。
例如:common_refresh_progress.xml
如果是普通的補間動畫或者屬性動畫,可採用:動畫類型_方向
的命名方式。
例如:
名稱 | 說明 |
---|---|
module_fade_in |
淡入 |
module_fade_out |
淡出 |
module_push_down_in |
從下方推入 |
module_push_down_out |
從下方推出 |
module_push_left |
推向左方 |
module_slide_in_from_top |
從頭部滑動進入 |
module_zoom_enter |
變形進入 |
module_slide_in |
滑動進入 |
module_shrink_to_middle |
中間縮小 |
4.2 顏色資源文件(color/)
命名規則:module_類型_邏輯名稱
。
例如:module_sel_btn_font.xml
。
顏色資源也可以放於 res/drawable/
目錄,引用時則用 @drawable
來引用,但不推薦這麼做,最好還是把兩者分開。
4.3 圖片資源文件(drawable/ 和 mipmap/)
res/drawable/
目錄下放的是位圖文件(.png、.9.png、.jpg、.gif)或編譯爲可繪製對象資源子類型的 XML 文件,而 res/mipmap/
目錄下放的是不同密度的啓動圖標,所以 res/mipmap/
只用於存放啓動圖標,其餘圖片資源文件都應該放到 res/drawable/
目錄下。
命名規則:module_類型_邏輯名稱
例如:user_btn_logout.png
大分辨率圖片(單維度超過 1000)建議統一放在 xxhdpi 目錄下管理,否則將導致佔用內存成倍數增加 。
如果有多種形態,如按鈕選擇器:module_sel_btn_xx.xml
,採用如下命名:
名稱 | 說明 |
---|---|
module_sel_btn_xx |
作用在 btn_xx 上的 selector |
module_btn_xx_normal |
默認狀態效果 |
module_btn_xx_pressed |
state_pressed 點擊效果 |
module_btn_xx_focused |
state_focused 聚焦效果 |
module_btn_xx_disabled |
state_enabled 不可用效果 |
module_btn_xx_checked |
state_checked 選中效果 |
module_btn_xx_selected |
state_selected 選中效果 |
module_btn_xx_hovered |
state_hovered 懸停效果 |
module_btn_xx_checkable |
state_checkable 可選效果 |
module_btn_xx_activated |
state_activated 激活效果 |
module_btn_xx_window_focused |
state_window_focused 窗口聚焦效果 |
注意:使用 Android Studio 的插件 SelectorChapek 可以快速生成 selector,前提是命名要規範。
4.4 佈局資源文件(layout/)
按照對應類型增加前綴:
類型 | 前綴 |
---|---|
Activity | module_activity |
Fragment | module_fragment |
Dialog | module_dialog |
include | module_include |
ListView | module_list_item |
RecyclerView | module_recycle_item |
GridView | module_grid_item |
Activity/Fragment/Dialog的類名不需要增加module前綴,直接爲每一個module分配一個package,比如:a.b.c.user、a.b.c.launch,所有的代碼都放在該package下。
4.5 菜單資源文件(menu/)
命名規則:module_邏輯名稱
4.6 values 資源文件(values/)
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
。
一個 Module 的資源文件都在 res 目錄下,如果一個 Module 涉及到的頁面很多,資源文件很多,不方便管理,可以多配置幾個資源文件目錄,這樣我們可以對每個模塊的資源都進行具體分類。
方法很簡單,配置我們的app文件夾下的 build.gradle 文件,比如:
android {
...
sourceSets {
main {
res.srcDirs('src/main/res', 'src/main/res_core', 'src/main/res_sub')
}
}
}
配置完之後,sync project 一下就成功了。
4.6.1 colors.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
。
4.6.2 dimens.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
字符串一樣直接寫值,像這樣規範的尺寸很容易修改或重構,會使應用所有用到的尺寸一目瞭然。 這樣寫會非常有感覺,會使組織和改變風格或佈局非常容易。
4.6.3 strings.xml
<string>
的 name
命名使用下劃線命名法,採用以下規則:module_邏輯名稱
,這樣方便同一個界面的所有 string
都放到一起,方便查找。
減少複用字符串,每個組件維護自己的,不要嫌麻煩。因爲一些語言同樣一個意思在不同的語境下,會用到不同的單詞。
使用字符串格式化替代字符串拼接。
4.6.4 styles.xml
<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>
中。
五、Android基本組件
-
Activity 間的數據通信,對於數據量比較大的,避免使用 Intent + Parcelable 的方式傳遞大數據(binder transaction緩存爲 1MB),可能導致 OOM。可以考慮 EventBus 等替代方案,以免造成 TransactionTooLargeException。
-
Activity#onSaveInstanceState()方法不是 Activity 生命週期方法,也不保證一定會被調用。它是用來在 Activity 被意外銷燬時保存 UI 狀態的,只能用於保存臨時性數據,例如 UI 控件的屬性等,不能跟數據的持久化存儲混爲一談。持久化存儲應該在 Activity#onPause()/onStop()中實行。
-
避免在 Service#onStartCommand()/onBind()方法中執行耗時操作,如果確 實有需求,應改用 IntentService 或採用其他異步機制完成。
-
避免在 BroadcastReceiver#onReceive()中執行耗時操作,如果有耗時工作, 應該創建 IntentService 完成,而不應該在 BroadcastReceiver 內創建子線程去做。
-
對於只用於應用內的廣播,優先使用 LocalBroadcastManager 來進行註冊 和發送,LocalBroadcastManager 安全性更好,同時擁有更高的運行效率。
-
添 加 Fragment 時 , 確 保 FragmentTransaction#commit() 在 Activity#onPostResume()或者 FragmentActivity#onResumeFragments()內調用。 不要隨意使用 FragmentTransaction#commitAllowingStateLoss()來代替,任何 commitAllowingStateLoss()的使用必須經過 code review,確保無負面影響。
-
不要在 Activity#onDestroy()內執行釋放資源的工作,例如一些工作線程的 銷燬和停止,因爲 onDestroy()執行的時機可能較晚。可根據實際需要,在 Activity#onPause()/onStop()中結合 isFinishing()的判斷來執行。
-
當前 Activity 的 onPause 方法執行結束後纔會創建(onCreate)或恢復 (onRestart)別的 Activity,所以在 onPause 方法中不適合做耗時較長的工作,這 會影響到頁面之間的跳轉效率。
-
Activity 或者 Fragment 中動態註冊 BroadCastReceiver 時,registerReceiver() 和 unregisterReceiver()要成對出現。
-
Android的最小兼容版本到17.關於Android不同系統版本的市場佔比情況詳解
-
Activity
和
Fragment裏面有許多重複的操作以及操作步驟,所以我們都需要提供一個
BaseActivity和
BaseFragment,讓所有的
Activity和
Fragment` 都繼承這個基類。 -
必須支持界面被系統回收,用戶再次打開App的時候能夠恢復用戶當時的使用狀態,分析出具體在哪些情況下需要,哪些情況下不需要。例如彈窗,恢復彈窗上顯示的數據,恢復彈窗的按鈕的點擊事件。
-
使用ArrayMap、ArraySet、SparseArray替換HashMap<T, T>、HashMap<int, T> 和
HashSet<T>
。 -
自定義控件的自定義屬性,必須使用控件名稱作爲前綴,否則容易造成同名屬性。
-
新建線程時,必須通過線程池提供(AsyncTask 或者 ThreadPoolExecutor或者其他形式自定義的線程池),不允許在應用中自行顯式創建線程 。
-
子線程中不能更新界面,更新界面必須在主線程中進行,網絡操作不能在主線程中調用 。
-
不要在非 UI 線程中初始化 ViewStub,否則會返回 null。
-
任何時候不要硬編碼文件路徑,請使用 Android 文件系統 API 訪問。
示例:Android 應用提供內部和外部存儲,分別用於存放應用自身數據以及應用產生的用
戶數據。可以通過相關 API 接口獲取對應的目錄,進行文件操作。android.os.Environment#getExternalStorageDirectory() android.os.Environment#getExternalStoragePublicDirectory() android.content.Context#getFilesDir() android.content.Context#getCacheDir
-
當使用外部存儲時,必須檢查外部存儲的可用性 。
-
SharedPreference 中只能存儲簡單數據類型(int、 boolean、 String 等),複雜數據類型建議使用文件、數據庫等其他方式存儲 。
-
SharedPreference 提 交 數 據 時 , 盡 量 使 用 Editor#apply() , 而 非Editor#commit()。一般來講,僅當需要確定提交結果,並據此有後續操作時,才使用 Editor#commit()。
-
多線程操作寫入數據庫時,需要使用事務,以免出現同步問題 。
-
執行 SQL 語句時,應使用 SQLiteDatabase#insert()、 update()、 delete(),不要使用SQLiteDatabase#execSQL(),以免 SQL 注入風險。
-
不要通過 Msg 傳遞大的對象,會導致內存問題。
-
不能在 Activity 沒有完全顯示時顯示 PopupWindow 和 Dialog。
-
不要在 Android 的 Application 對象中緩存數據。基礎組件之間的數據共享請使用 Intent 等機制,也可使用 SharedPreferences 等數據持久化機制。
-
使用 Toast 時,建議定義一個全局的 Toast 對象,這樣可以避免連續顯示Toast 時不能取消上一次 Toast 消息的情況(如果你有連續彈出 Toast 的情況,避免使用 Toast.makeText)。
六、代碼
- 使用Gson中的
@SerializedName
將服務器端返回數據字段不符合命名規範的轉換爲符合規範的命名,分離服務器和客戶端字段的硬綁定。 - 多用組合,少用繼承。
- 當一個類有多個構造函數,或是多個同名函數,這些函數應該按順序出現在一起,中間不要放進其它函數。
- 提取方法,去除重複代碼。對於必要的工具類抽取也很重要,這在以後的項目中是可以重用的。
- 通過引入事件總線,如:
EventBus
、AndroidEventBus
、RxBus
,它允許我們在DataLayer
中發送事件,以便ViewLayer
中的多個組件都能夠訂閱到這些事件,減少回調。 - 儘可能使用局部變量。
- 及時關閉流。
- 不要在循環中使用
try…catch…
,應該把其放在最外層。 - 使用帶緩衝的輸入輸出流進行 IO 操作。
- 儘量在合適的場合使用單例;
- 使用單例可以減輕加載的負擔、縮短加載的時間、提高加載的效率,但並不是所有地方都適用於單例,簡單來說,單例主要適用於以下三個方面:
- 控制資源的使用,通過線程同步來控制資源的併發訪問。
- 控制實例的產生,以達到節約資源的目的。
- 控制數據的共享,在不建立直接關聯的條件下,讓多個不相關的進程或線程之間實現通信。
- 使用 AS 自帶的 Lint 來優化代碼結構(右鍵 module、目錄或者文件,選擇 Analyze -> Inspect Code)。
- Kotlin 和 Java 是兩種語言,在使用Kotlin的時候應該使用Kotlin中的特性,而不是固執的使用Java特性。比如:對於變量的賦值,如果僅僅是賦值,那麼應該直接開放變量權限;如果涉及到一系列後續操作,那麼使用方法。
七、UI與佈局
- 使用 start 和 end 替代 left 和 right,使佈局能適應 RightRoLeft佈局。
- 合理佈局,有效運用
<merge>
、<ViewStub>
、<include>
標籤。比如content 是一個 FrameLayout ,所以這裏簡單佈局是不需要外層ViewGroup,直接使用<merge>
來作爲佈局文件的根ViewGroup就可以了; - 所有佈局都需要考慮到大屏幕和小屏幕顯示的問題,尤其是對大屏幕一個頁面就可以展示完,而對於小屏幕不能展示完,需要增加
<ScrollView>
讓頁面能夠滾動。目前一般情況下最小屏幕是 1280 * 720。 - 字體單位使用 dp 設置字體而不是 sp。
- 對於設計圖上按鈕大小小於 24dp * 24dp,應該使用 padding 將按鈕的實際大小控制在大於等於 24dp * 24dp。
- 使用 ConstraintLayout 替換 RelativeLayout。
- 不要過分依賴通用資源,否則會導致修改困難,該拆分的就拆分,不要在乎資源內容是一樣的。
- 所有的文本按鈕,還是使用 Button,不要使用 TextView ,畢竟系統對 Button 有樣式渲染。同理對於圖片按鈕,應該使用 ImageButton 而不是 ImageView。
- 使用Space控件佔據不顯示內容的空間。
- ScrollView 內部嵌套有 ListView 或 RecycleView 等,注意要考慮到ScrollView默認位置不是最頂部的情況。
- 考慮過渡繪製,不要直接在整個佈局增加背景顏色,考慮是否在佈局的一部分設置背景顏色就可以達到UI效果。
八、安全
-
將 android:allowbackup 屬性必須設置爲 false,阻止應用數據被導出。說明:android:allowBackup 原本是 Android 提供的 adb 調試功能,如果設置爲 true, 可以導出應用數據備份並在任意設備上恢復。這對應用安全性和用戶數據隱私構成 極大威脅,所以必須設置爲 false,防止數據泄露。
-
在 SDK 支持的情況下,Android 應用必須使用 V2 簽名,這將對 APK 文件的修改做更多的保護。
-
所有的 Android 基本組件(Activity、Service、BroadcastReceiver、ContentProvider 等)都不應在沒有嚴格權限控制的情況下,將 android:exported 設 置爲 true。
-
確保應用發佈版本的 android:debuggable 屬性設置爲 false。
-
在 Android 4.2(API Level 17)及以上,對安全性要求較高的應用可在 Activity中,對 Activity 所關聯的 Window 應用 WindowManager.LayoutParams.FLAG_SECURE,防止被截屏、錄屏。但要注意的是,一個 Activity 關聯的 Window 可能不止一個,如果使用了 Dialog / DialogFragment 等控件彈出對話框,它們本身也會創建一個新的 Window,也一樣需要保護。
九、註釋
註釋的功能是輔助程序員理解邏輯和業務,不是每一行都需要註釋。一個好的命名就可以反應該字段或者函數的意思,沒有必要多此一舉增加註釋。在具體的業務邏輯或者關鍵代碼處,添加註釋就可。
9.1 類註釋
每個類完成後應該有作者姓名的註釋,對自己的代碼負責。
/**
* <pre>
* author : XiaoYangZi
* time : 2018/04/23
* desc : xxxx 描述
* version: 1.0
* </pre>
*/
class WelcomeActivity {
...
}
具體可以在 AS 中自己配製,進入 Settings -> Editor -> File and Code Templates -> Includes -> File Header,輸入
/**
* <pre>
* author : ${USER} //這裏也可以寫死,比如說 author : XiaoYangZi
* time : ${YEAR}/${MONTH}/${DAY}
* desc :
* version: 1.0
* </pre>
*/
這樣便可在每次新建類的時候自動加上該頭註釋。
9.2 函數註釋
有選擇性的書寫。
/**
* 一句話功能描述
* 功能詳細描述
* @param [參數1] [參數1說明]
* @param [參數2] [參數2說明]
* @return [返回類型說明]
* @exception/throws [異常類型] [異常說明]
* @deprecated [是否廢棄]
*/
9.3 代碼塊註釋
寫出代碼的目的,而不是行爲,行爲可以通過具體的代碼來判斷。
十、測試
十一、Git使用
12.1 分支
master:
主分支,不輕易改動,新版本上線後,將新版本代碼合併到該分支
develop:
團隊人員很少的時候,所有的開發人員都在這個分支進行開發。
團隊人員超過一個規模,每個開發人員在自己分支(dev_xx)上開發,等開發到一定階段,將自己的代碼合併到develop分支上。
fix_version:
修復Bug分支,修復對應版本的bug。
refactor_xx:
重構分支,區別於修改Bug分支,重構不一定會在下一個新版本中上線。
12.2 提交
-
feat: 新功能(feature)
-
fix: 修復bug
-
docs: 僅文檔修改(documentation)
-
style: 不影響代碼含義的變化(空白,格式化,缺少分號等)
-
refactor:重構(既不是新增功能,也不是修改bug的變動)
-
chore: 構建過程、輔助工具、編輯器配置的變動
-
perf: 改進性能的代碼更改
-
test: 添加缺失測試或更正現有測試
-
build: 影響構建系統或外部依賴關係的更改(示例範圍:gulp,broccoli,npm)
-
ci: 更改我們的持續集成文件和腳本(示例範圍:Travis,Circle,BrowserStack,SauceLabs)
示例:
fix(首頁):修復緩存異常
feat(用戶):新增修改用戶頭像的功能
12.3 代碼評審
在兩個及兩個以上開發人員的項目中,應該進行代碼評審,檢查代碼風格和是否有潛在的BUG。
12.4其他
在Android Studio中設置好代碼註釋、模版或者其他配置文件後,導出settings.jar 包,可實現團隊或換電腦時一鍵統一配置。
https://www.jb51.net/softjc/445324.html
附錄
UI 控件縮寫表
名稱 | 縮寫 |
---|---|
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 |
程序中使用單詞縮寫原則:不要用縮寫,除非該縮寫是約定俗成的。
擴展閱讀 :Git使用規範(Android版)
參考
阿里巴巴Android開發手冊
阿里巴巴Java開發手冊