Android開發代碼規範總結

前言

爲了有利於項目維護、增強代碼可讀性、提升 Code Review 效率以及規範團隊安卓開發,故提出以下安卓開發規範。

一、Android Studio 規範

  1. 儘量使用最新的穩定版 Android Studio 進行開發;
  2. 編碼格式統一爲 UTF-8
  3. 編輯完 .java、.kt、.xml 等文件後一定要 格式化,格式化,格式化(如果團隊有公共的樣式包,那就遵循它,否則統一使用 AS 默認模板即可,Mac 下可以使用快捷鍵 cmd + alt + L 進行代碼格式化,Window 下可以使用快捷鍵 ctrl + alt + L 進行代碼格式化);
  4. 刪除多餘的 import,減少警告出現,Mac 下可以使用快捷鍵 ctrl + alt + O 進行 import 優化,Window 下可以使用快捷鍵 ctrl + alt + O 進行 import 優化;

二、Kotlin命名

代碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。正確的英文拼寫和語法可以讓閱讀者易於理解,避免歧義。

注意:即使純拼音命名方式也要避免採用。但 alibabataobaoyoukuhangzhou 等國際通用的名稱,可視同英文。

2.1 包名

包名全部小寫,連續的單詞只是簡單地連接起來,不使用下劃線,採用反域名命名規則,全部使用小寫字母。一級包名是頂級域名,通常爲 comedugovnetorg 等,二級包名爲公司名,三級包名根據應用進行命名,後面就是對包名的劃分了,關於包名的劃分,推薦採用 PBF(按功能分包 Package By Feature)。

// BAD
com.RayWenderlich.funky_widget
// GOOD
com.raywenderlich.funkywidget

2.2 類名

類名通常是名詞或名詞短語,接口名稱有時可能是形容詞或形容詞短語。現在還沒有特定的規則或行之有效的約定來命名註解類型。

名詞,採用大駝峯命名法,儘量避免縮寫,除非該縮寫是衆所周知的, 比如 HTML、URL,如果類名稱中包含單詞縮寫,則單詞縮寫的每個字母均應大寫。

測試類的命名以它要測試的類的名稱開始,以 Test 結束。例如:HashTestHashIntegrationTest

接口(interface):命名規則與類一樣採用大駝峯命名法,多以 able 或 ible 結尾,如 interface Runnableinterface 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 中的很多類都用到了鍵值對函數,比如 SharedPreferencesBundleIntent,所以,即便是一個小應用,我們最終也不得不編寫大量的字符串常量。

當時用到這些類的時候,我們 必須 將它們的鍵定義爲 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 控件縮寫表))。

例如:mIvAvatarrvBooksflContainer

2.5.3 VariableName(變量名)

變量名中可能會出現量詞,我們需要創建統一的量詞,它們更容易理解,也更容易搜索。

例如:mFirstBookmPreBookcurBook

量詞列表 量詞後綴說明
First 一組變量中的第一個
Last 一組變量中的最後一個
Next 一組變量中的下一個
Pre 一組變量中的上一個
Cur 一組變量中的當前變量

2.5.4 Type1(數據類型)

對於表示集合或者數組的非常量字段名,我們可以添加後綴來增強字段的可讀性,比如:

集合添加如下後綴:List、Map、Set。

數組添加如下後綴:Arr。

例如:mIvAvatarListuserArrfirstNameSet

注意:如果數據類型不確定的話,比如表示的是很多書,那麼使用其複數形式來表示也可,例如 mBooks

2.6 參數名

參數名以 lowerCamelCase 風格編寫。

2.7 局部變量名

局部變量名以 lowerCamelCase 風格編寫,比起其它類型的名稱,局部變量名可以有更爲寬鬆的縮寫。

雖然縮寫更寬鬆,但還是要避免用單字符進行命名,除了臨時變量和循環變量。

即使局部變量是 final 和不可改變的,也不應該把它示爲常量,自然也不能用常量的規則去命名它。

2.8 臨時變量

臨時變量通常被取名爲 ijkmn,它們一般用於整型;cde,它們一般用於字符型。 如:for (int i = 0; i < len; i++)

2.9 泛型變量名

類型變量可用以下兩種風格之一進行命名:

  1. 單個的大寫字母,後面可以跟一個數字(如:E, T, X, T2)。
  2. 以類命名方式(參考類名),後面加個大寫的 T(如:RequestT, FooBarT)。

2.10 id命名

插件 apply plugin: 'kotlin-android-extensions',可以在代碼中通過 xml 定義的控件 id 直接操作控件,而不用寫 findViewById ,所以這裏控件的命名直接使用 lowerCamelCase 風格編寫。

命名規則:view 縮寫{模塊名}邏輯名,例如: btnMainSearchbtnBack

三、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 類成員的順序

這並沒有唯一的正確解決方案,但如果都使用一致的順序將會提高代碼的可讀性,推薦使用如下排序:

  1. 常量
  2. 字段(public -> protected -> private)
  3. 構造函數
  4. 重寫函數和回調 (在Android 中,應該將生命週期的函數放在前面)
  5. 公有函數
  6. 私有函數
  7. 內部類或接口
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 組件(例如 ActivityFragment),那麼把重寫函數按照他們的生命週期進行排序是一個非常好的習慣,例如,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 中的很多類都用到了鍵值對函數,比如 SharedPreferencesBundleIntent,所以,即便是一個小應用,我們最終也不得不編寫大量的字符串常量。

當時用到這些類的時候,我們 必須 將它們的鍵定義爲 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.xmlcolors.xmldimens.xml,起作用的不是文件名稱,而是 <resources> 標籤下的各種標籤,比如 <style> 決定樣式,<color> 決定顏色,所以,可以把一個大的 xml 文件分割成多個小的文件,比如可以有多個 style 文件,如 styles.xmlstyles_home.xmlstyles_item_details.xmlstyles_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>
複製代碼

佈局時在寫 marginspaddings 時,你應該使用 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基本組件

  1. Activity 間的數據通信,對於數據量比較大的,避免使用 Intent + Parcelable 的方式傳遞大數據(binder transaction緩存爲 1MB),可能導致 OOM。可以考慮 EventBus 等替代方案,以免造成 TransactionTooLargeException。

  2. Activity#onSaveInstanceState()方法不是 Activity 生命週期方法,也不保證一定會被調用。它是用來在 Activity 被意外銷燬時保存 UI 狀態的,只能用於保存臨時性數據,例如 UI 控件的屬性等,不能跟數據的持久化存儲混爲一談。持久化存儲應該在 Activity#onPause()/onStop()中實行。

  3. 避免在 Service#onStartCommand()/onBind()方法中執行耗時操作,如果確 實有需求,應改用 IntentService 或採用其他異步機制完成。

  4. 避免在 BroadcastReceiver#onReceive()中執行耗時操作,如果有耗時工作, 應該創建 IntentService 完成,而不應該在 BroadcastReceiver 內創建子線程去做。

  5. 對於只用於應用內的廣播,優先使用 LocalBroadcastManager 來進行註冊 和發送,LocalBroadcastManager 安全性更好,同時擁有更高的運行效率。

  6. 添 加 Fragment 時 , 確 保 FragmentTransaction#commit() 在 Activity#onPostResume()或者 FragmentActivity#onResumeFragments()內調用。 不要隨意使用 FragmentTransaction#commitAllowingStateLoss()來代替,任何 commitAllowingStateLoss()的使用必須經過 code review,確保無負面影響。

  7. 不要在 Activity#onDestroy()內執行釋放資源的工作,例如一些工作線程的 銷燬和停止,因爲 onDestroy()執行的時機可能較晚。可根據實際需要,在 Activity#onPause()/onStop()中結合 isFinishing()的判斷來執行。

  8. 當前 Activity 的 onPause 方法執行結束後纔會創建(onCreate)或恢復 (onRestart)別的 Activity,所以在 onPause 方法中不適合做耗時較長的工作,這 會影響到頁面之間的跳轉效率。

  9. Activity 或者 Fragment 中動態註冊 BroadCastReceiver 時,registerReceiver() 和 unregisterReceiver()要成對出現。

  10. Android的最小兼容版本到17.關於Android不同系統版本的市場佔比情況詳解

  11. ActivityFragment裏面有許多重複的操作以及操作步驟,所以我們都需要提供一個BaseActivityBaseFragment,讓所有的ActivityFragment` 都繼承這個基類。

  12. 必須支持界面被系統回收,用戶再次打開App的時候能夠恢復用戶當時的使用狀態,分析出具體在哪些情況下需要,哪些情況下不需要。例如彈窗,恢復彈窗上顯示的數據,恢復彈窗的按鈕的點擊事件。

  13. 使用ArrayMap、ArraySet、SparseArray替換HashMap<T, T>、HashMap<int, T> 和 HashSet<T>

  14. 自定義控件的自定義屬性,必須使用控件名稱作爲前綴,否則容易造成同名屬性。

  15. 新建線程時,必須通過線程池提供(AsyncTask 或者 ThreadPoolExecutor或者其他形式自定義的線程池),不允許在應用中自行顯式創建線程 。

  16. 子線程中不能更新界面,更新界面必須在主線程中進行,網絡操作不能在主線程中調用 。

  17. 不要在非 UI 線程中初始化 ViewStub,否則會返回 null。

  18. 任何時候不要硬編碼文件路徑,請使用 Android 文件系統 API 訪問。

    示例:Android 應用提供內部和外部存儲,分別用於存放應用自身數據以及應用產生的用
    戶數據。可以通過相關 API 接口獲取對應的目錄,進行文件操作。

    android.os.Environment#getExternalStorageDirectory()
    android.os.Environment#getExternalStoragePublicDirectory()
    android.content.Context#getFilesDir()
    android.content.Context#getCacheDir
    
  19. 當使用外部存儲時,必須檢查外部存儲的可用性 。

  20. SharedPreference 中只能存儲簡單數據類型(int、 boolean、 String 等),複雜數據類型建議使用文件、數據庫等其他方式存儲 。

  21. SharedPreference 提 交 數 據 時 , 盡 量 使 用 Editor#apply() , 而 非Editor#commit()。一般來講,僅當需要確定提交結果,並據此有後續操作時,才使用 Editor#commit()。

  22. 多線程操作寫入數據庫時,需要使用事務,以免出現同步問題 。

  23. 執行 SQL 語句時,應使用 SQLiteDatabase#insert()、 update()、 delete(),不要使用SQLiteDatabase#execSQL(),以免 SQL 注入風險。

  24. 不要通過 Msg 傳遞大的對象,會導致內存問題。

  25. 不能在 Activity 沒有完全顯示時顯示 PopupWindow 和 Dialog。

  26. 不要在 Android 的 Application 對象中緩存數據。基礎組件之間的數據共享請使用 Intent 等機制,也可使用 SharedPreferences 等數據持久化機制。

  27. 使用 Toast 時,建議定義一個全局的 Toast 對象,這樣可以避免連續顯示Toast 時不能取消上一次 Toast 消息的情況(如果你有連續彈出 Toast 的情況,避免使用 Toast.makeText)。

六、代碼

  1. 使用Gson中的 @SerializedName 將服務器端返回數據字段不符合命名規範的轉換爲符合規範的命名,分離服務器和客戶端字段的硬綁定。
  2. 多用組合,少用繼承。
  3. 當一個類有多個構造函數,或是多個同名函數,這些函數應該按順序出現在一起,中間不要放進其它函數。
  4. 提取方法,去除重複代碼。對於必要的工具類抽取也很重要,這在以後的項目中是可以重用的。
  5. 通過引入事件總線,如:EventBusAndroidEventBusRxBus,它允許我們在 DataLayer 中發送事件,以便 ViewLayer 中的多個組件都能夠訂閱到這些事件,減少回調。
  6. 儘可能使用局部變量。
  7. 及時關閉流。
  8. 不要在循環中使用 try…catch…,應該把其放在最外層。
  9. 使用帶緩衝的輸入輸出流進行 IO 操作。
  10. 儘量在合適的場合使用單例;
  11. 使用單例可以減輕加載的負擔、縮短加載的時間、提高加載的效率,但並不是所有地方都適用於單例,簡單來說,單例主要適用於以下三個方面:
    1. 控制資源的使用,通過線程同步來控制資源的併發訪問。
    2. 控制實例的產生,以達到節約資源的目的。
    3. 控制數據的共享,在不建立直接關聯的條件下,讓多個不相關的進程或線程之間實現通信。
  12. 使用 AS 自帶的 Lint 來優化代碼結構(右鍵 module、目錄或者文件,選擇 Analyze -> Inspect Code)。
  13. Kotlin 和 Java 是兩種語言,在使用Kotlin的時候應該使用Kotlin中的特性,而不是固執的使用Java特性。比如:對於變量的賦值,如果僅僅是賦值,那麼應該直接開放變量權限;如果涉及到一系列後續操作,那麼使用方法。

七、UI與佈局

  1. 使用 start 和 end 替代 left 和 right,使佈局能適應 RightRoLeft佈局。
  2. 合理佈局,有效運用 <merge><ViewStub><include> 標籤。比如content 是一個 FrameLayout ,所以這裏簡單佈局是不需要外層ViewGroup,直接使用 <merge> 來作爲佈局文件的根ViewGroup就可以了;
  3. 所有佈局都需要考慮到大屏幕和小屏幕顯示的問題,尤其是對大屏幕一個頁面就可以展示完,而對於小屏幕不能展示完,需要增加 <ScrollView> 讓頁面能夠滾動。目前一般情況下最小屏幕是 1280 * 720。
  4. 字體單位使用 dp 設置字體而不是 sp。
  5. 對於設計圖上按鈕大小小於 24dp * 24dp,應該使用 padding 將按鈕的實際大小控制在大於等於 24dp * 24dp。
  6. 使用 ConstraintLayout 替換 RelativeLayout。
  7. 不要過分依賴通用資源,否則會導致修改困難,該拆分的就拆分,不要在乎資源內容是一樣的。
  8. 所有的文本按鈕,還是使用 Button,不要使用 TextView ,畢竟系統對 Button 有樣式渲染。同理對於圖片按鈕,應該使用 ImageButton 而不是 ImageView。
  9. 使用Space控件佔據不顯示內容的空間。
  10. ScrollView 內部嵌套有 ListView 或 RecycleView 等,注意要考慮到ScrollView默認位置不是最頂部的情況。
  11. 考慮過渡繪製,不要直接在整個佈局增加背景顏色,考慮是否在佈局的一部分設置背景顏色就可以達到UI效果。

八、安全

  1. 將 android:allowbackup 屬性必須設置爲 false,阻止應用數據被導出。說明:android:allowBackup 原本是 Android 提供的 adb 調試功能,如果設置爲 true, 可以導出應用數據備份並在任意設備上恢復。這對應用安全性和用戶數據隱私構成 極大威脅,所以必須設置爲 false,防止數據泄露。

  2. 在 SDK 支持的情況下,Android 應用必須使用 V2 簽名,這將對 APK 文件的修改做更多的保護。

  3. 所有的 Android 基本組件(Activity、Service、BroadcastReceiver、ContentProvider 等)都不應在沒有嚴格權限控制的情況下,將 android:exported 設 置爲 true。

  4. 確保應用發佈版本的 android:debuggable 屬性設置爲 false。

  5. 在 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 開發規範(完結版)

Android Studio 下對資源進行分包

不可不知的 Android strings.xml 那些事

kotlin-style-guide

Android Studio中導出設置共享配置

阿里巴巴Android開發手冊

阿里巴巴Java開發手冊

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章