Android開發規范參考(Java版)
一、命名風格
1.類名使用 UpperCamelCase 風格
不要中英文混合(通用拼音除外,如Beijing等),以每個單詞首字母大寫,
2.方法名、引數名、成員變數、區域變數都統一使用 lowerCamelCase 風格
駝峰命名法,第一個單詞首字母要小寫,其他單詞首字母大寫,
①為區分成員變數和區域變數,可以在書寫成員變數時,第一個字母用m(member),其他單詞遵循駝峰命名法,如mName,注意在寫物體類時則不能用m表示member,因為物體類一般都是純變數和setter、getter,
②布爾型別的變數,都不要加 is 前綴,否則部分框架決議會引起序列化錯誤,
反例:定義為基本資料型別 Boolean isDeleted;的屬性,它的方法也是 isDeleted(),RPC框架在反向決議的時候,“誤以為”對應的屬性名稱是 deleted,導致屬性獲取不到,進而拋出例外,
3.包名命名
包名統一使用小寫,點分隔符之間有且僅有一個自然語意的英語單詞,包名統一使用單數形式,但是類名如果有復數含義,類名可以使用復數形式,
正例:應用工具類包名為 com.alibaba.ai.util、類名為 MessageUtils
4.陣列定義
型別與中括號緊挨相連來定義陣列,
正例:定義整形陣列 int[] arrayDemo;
反例:在 main 引數中,使用 String args[]來定義,
5.介面
介面類中的方法和屬性不要加任何修飾符號(public 也不要加),保持代碼的簡潔性,并加上有效的 Javadoc 注釋,盡量不要在介面里定義變數,如果一定要定義變數,肯定是與介面方法相關,并且是整個應用的基礎常量,
二、常量定義
1.常量命名全部大寫,單詞間用下劃線隔開
使用public/private static final定義,力求語意表達完整清楚,不要嫌名字長,
正例:MAX_STOCK_COUNT
反例:MAX_COUNT
2.不要使用介面作為常量定義類
3.不要使用一個常量類維護所有常量
按常量功能進行歸類,分開維護,
說明:大而全的常量類,非得使用查找功能才能定位到修改的常量,不利于理解和維護,
正例:快取相關常量放在類 CacheConsts 下;系統配置相關常量放在類 ConfigConsts 下,
三、代碼格式
1.大括號使用
左大括號前不換行,左大括號后換行,右大括號前換行,右大括號后還有 else 等代碼則不換行;表示終止的右大括號后必須換行,
如果是大括號內為空,則簡潔地寫成{}即可,不需要換行,
2.空格使用
①(強制)任何二目、三目運算子的左右兩邊都需要加一個空格;
②(強制)if/for/while/switch/do 等保留字與括號之間都必須加空格;
③(強制)注釋的雙斜線與注釋內容之間有且僅有一個空格;
④(強制)方法引數在定義和傳入時,多個引數逗號后邊必須加空格;
⑤(可選)沒有必要增加若干空格來使某一行的字符與上一行對應位置的字符對齊;
3.撰寫注釋
良好的注釋可以提高團隊開發效率,
①類、方法、成員變數的注釋
/**
* 類、方法、成員變數
*/
對于類注釋,應該完善作者、時間、類的作用等資訊
②代碼中的注釋,使用雙斜線
四、OOP 規約
1.避免通過一個類的物件參考訪問此類的靜態變數或靜態方法
無謂增加編譯器決議成本,直接用類名來訪問即可,
2.相同引數型別,相同業務含義,才可以使用 Java 的可變引數
避免使用 Object引數
說明:可變引數必須放置在引數串列的最后,(提倡同學們盡量不用可變引數編程)
正例:public User getUsers(String type, Integer… ids) {…}
3.構造方法里面禁止加入任何業務邏輯
如果有初始化邏輯,請放在 init 方法中,尤其是不能在構造方法里呼叫成員方法,會發生不可預期的結果(物件都還沒生成,怎么可以使用這個物件來做事情呢?),
4.避免出現重復的代碼(Don’t Repeat Yourself),即 DRY 原則
說明:隨意復制和粘貼代碼,必然會導致代碼的重復,在以后需要修改時,需要修改所有的副本,容易遺漏,必要時抽取共性方法,或者抽象公共類,甚至是組件化,
正例:一個類中有多個 public 方法,都需要進行數行相同的引數校驗操作,這個時候請抽取:
private boolean checkParam(DTO dto) {...}
五、集合處理
1.關于 hashCode 和 equals 的處理
①只要重寫 equals,就必須重寫 hashCode;
②因為 Set 存盤的是不重復的物件,依據 hashCode 和 equals 進行判斷,所以 Set 存盤的
物件必須重寫這兩個方法;
③如果自定義物件作為 Map 的鍵,那么必須重寫 hashCode 和 equals,
2.不要在 foreach 回圈里進行元素的 remove/add 操作
remove 元素請使用 Iterator方式,如果并發操作,需要對 Iterator 物件加鎖
正例:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if ("1".equals(item)) {
iterator.remove();
}
}
反例:
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
3.高度注意 Map 類集合 K/V 能不能存盤 null 值的情況
| 集合類 | Key | Value | Super | 說明 |
|---|---|---|---|---|
| Hashtable | 不允許為 null | 不允許為 null | Dictionary | 執行緒安全 |
| ConcurrentHashMap | 不允許為 null | 不允許為 null | AbstractMap | 鎖分段技術(JDK8:CAS) |
| TreeMap | 不允許為 null | 允許為 null | AbstractMap | 執行緒不安全 |
| HashMap | 允許為 null | 允許為 null | AbstractMap | 執行緒不安全 |
反例: 由于 HashMap 的干擾,很多人認為 ConcurrentHashMap 是可以置入 null 值,而事實上,存盤 null 值時會拋出 NPE 例外,如果實在記不住,區分不了,用的時候查表確認一下,
六、并發處理
1.獲取單例物件需要保證執行緒安全,其中的方法也要保證執行緒安全
2.創建執行緒或執行緒池時請指定有意義的執行緒名稱,方便出錯時回溯
正例:
public class TimerTaskThread extends Thread {
public TimerTaskThread() {
super.setName("TimerTaskThread");
...
}
}
3.執行緒資源必須通過執行緒池提供,不允許在應用中自行顯式創建執行緒
4.SimpleDateFormat 是執行緒不安全的類,一般不要定義為 static 變數
如果定義為static,必須加鎖,或者使用 DateUtils 工具類,
正例:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
如果是 JDK8 的應用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat
5.避免 Random 實體被多執行緒使用
雖然共享該實體是執行緒安全的,但會因競爭同一seed 導致的性能下降,
6.volatile 解決多執行緒記憶體不可見問題
對于一寫多讀,是可以解決變數同步問題,但是如果多寫,同樣無法解決執行緒安全問題,如果是 count++操作,使用如下類實作:
AtomicInteger count = new AtomicInteger();
count.addAndGet(1);
如果是 JDK8,推薦使用 LongAdder 物件,比 AtomicLong 性能更好(減少樂觀鎖的重試次數),
七、控制陳述句
1.在一個 switch 塊內,每個 case 要么通過 break/return 等來終止
在一個 switch 塊內,都必須包含一個 default 陳述句并且放在最后,即使空代碼,
2.在 if/else/for/while/do 陳述句中必須使用大括號
即使只有一行代碼,避免采用單行的編碼方式,反例:if (condition) statements;
3.不要在條件判斷中執行其它復雜的陳述句
將復雜邏輯判斷的結果賦值給一個有意義的布爾變數名,以提高可讀性,
4.回圈體中的陳述句要考量性能
以下操作盡量移至回圈體外處理,如定義物件、變數、獲取資料庫連接,進行不必要的 try-catch 操作(這個 try-catch 是否可以移至回圈體外),
5.謹慎注釋掉代碼
說明:代碼被注釋掉有兩種可能性:1)后續會恢復此段代碼邏輯,2)永久不用,前者如果沒有備注資訊,難以知曉注釋動機,后者建議直接刪掉(代碼倉庫保存了歷史代碼),
6.特殊注釋標記,請注明標記人與標記時間
經常清理此類標記,線上故障有時候就是來源于這些標記處的代碼,
① 待辦事宜(TODO):( 標記人,標記時間,[預計處理時間])
表示需要實作,但目前還未實作的功能,這實際上是一個 Javadoc 的標簽,目前的 Javadoc還沒有實作,但已經被廣泛使用,只能應用于類,介面和方法(因為它是一個 Javadoc 標簽),
② 錯誤,不能作業(FIXME):(標記人,標記時間,[預計處理時間])
在注釋中用 FIXME 標記某代碼是錯誤的,而且不能作業,需要及時糾正的情況,
八、例外處理
1.finally 塊必須對資源物件、流物件進行關閉,有例外也要做 try-catch
說明:如果 JDK7 及以上,可以使用 try-with-resources 方式,
2.避免不必要的try-catch
正例:if (obj != null) {...}
反例:try { obj.method() } catch (NullPointerException e) {…}
3.方法的回傳值可以為 null
不強制回傳空集合,或者空物件等,必須添加注釋充分說明什么情況下會回傳 null 值,
九、安卓專案相關
1.大型專案不應該按屬性分包
反例:所有的Activity類都放在activity包下
正例:按功能模塊分包,如登錄模塊、用戶資料模塊、購物車模塊、發現模塊等
2.布局檔案命名方式
①Activity 的 layout 以 activity_module 開頭;
②Fragment 的 layout 以 fragment_module 開頭;
③Dialog 的 layout 以 dialog_module 開頭;
④include 的 layout 以 include_module 開頭;
⑤ListView/RecyclerView 的行 layout 以 item_module 開頭,
3.大解析度圖片(單維度超過 1000)大解析度圖片建議統一放在 xxhdpi 目錄下管理,否則將導致占用記憶體成倍數增加
說明:為了支持多種螢屏尺寸和密度,Android 為多種螢屏提供不同的資源目錄進行適配,為不同螢屏密度提供不同的位圖可繪制物件,可用于密度特定資源的配置限定符(在下面詳述) 包括 ldpi(低)、mdpi(中)、 hdpi(高)、xhdpi(超高)、xxhdpi (超超高)和 xxxhdpi(超超超高),
根據當前的設備螢屏尺寸和密度,將會尋找最匹配的資源,如果將高解析度圖片放入低密度目錄,將會造成低端機加載過大圖片資源,又可能造成 OOM,同時也是資源浪費,沒有必要在低端機使用大圖,
正例:將 144*144 的應用圖示 PNG 檔案放在 mipmap-xxhdpi 目錄
反例:將 144*144 的應用圖示 PNG 檔案放在 mipmap-mhdpi 目錄
4.Activity#onSaveInstanceState()
onSaveInstanceState()方法不是 Activity 生命周期方法,也不保證一定會被呼叫,它是用來在 Activity 被意外銷毀時保存 UI 狀態的,只能用于保存臨 時性資料,例如 UI 控制元件的屬性等,不能跟資料的持久化存盤混為一談,持久化存盤應該在 Activity#onPause()/onStop()中實行,
注意:當前Activity的onPause方法執行結束后才會執行下一個Activity的onCreate方法,所以在 onPause 方法中不適合做耗時較長的作業,這會影響到頁面之間的跳轉效率,
5.Activity 間通過隱式 Intent 的跳轉,在發出 Intent 之前必須通過 resolveActivity檢查
避免找不到合適的呼叫組件,造成 ActivityNotFoundException 的例外,
public void viewUrl(String url, String mimeType) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(url), mimeType);
if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_
ONLY) != null) {
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
if (Config.LOGD) {
...
}
}
}
}
反例:
Intent intent = new Intent();
intent.setAction("com.great.activity_intent.Intent_Demo1_Result3");
6.避免在 Service#onStartCommand()/onBind()方法中執行耗時操作
如果確實有需求,應改用 IntentService 或采用其他異步機制完成
同樣,避免在 BroadcastReceiver#onReceive()中執行耗時操作,如果有耗時作業,應該創建 IntentService 完成,而不應該在 BroadcastReceiver 內創建子執行緒去做,BroadcastReceiver#onReceive()方法耗時超過 10 秒鐘,可能會被系統殺死,
7.避免使用隱式 Intent 廣播敏感資訊
資訊可能被其他注冊了對應BroadcastReceiver 的 App 接收,對于只用于應用內的廣播,優先使用LocalBroadcastManager 來進行注冊和發送,LocalBroadcastManager 安全性更好,同時擁有更高的運行效率,
8.Activity或者 Fragment 中動態注冊BroadCastReceiver 時,registerReceiver()和 unregisterReceiver()要成對出現
如果 registerReceiver()和 unregisterReceiver()不成對出現,則可能導致已經注冊的receiver 沒有在合適的時機注銷,導致記憶體泄漏,占用記憶體空間,加重 SystemService負擔,
部分華為的機型會對 receiver 進行資源管控,單個應用注冊過多 receiver 會觸發管控模塊拋出例外,應用直接崩潰,
9.文本大小使用單位 sp,view 大小使用單位 dp
對于 Textview,如果在文字大小確定的情況下推薦使用 wrap_content 布局避免出現文字顯示不全的適配問題,
10.靈活使用布局,減少布局嵌套層數
推薦 Merge、ViewStub 來優化布局,盡可能多的減少 UI布局層級,推薦使用 FrameLayout,LinearLayout、RelativeLayout 次之,
注意:不要在非 UI 執行緒中初始化 ViewStub,否則會回傳 null,
11.盡量不要使用 AnimationDrawable
它在初始化的時候就將所有圖片加載到記憶體中,特別占記憶體,并且還不能釋放,釋放之后下次進入再次加載時會報錯,
說明:Android 的幀影片可以使用 AnimationDrawable 實作,但是如果你的幀影片中如果包含過多幀圖片,一次性加載所有幀圖片所導致的記憶體消耗會使低端機發生 OOM例外,幀影片所使用的圖片要注意降低記憶體消耗,當圖片比較大時,容易出現 OOM,
12.不能使用 ScrollView 包裹 ListView/GridView/ExpandableListVIew
因為這樣會把 ListView 的所有 Item 都加載到記憶體中,要消耗巨大的記憶體和 cpu 去繪制圖面,
說明:ScrollView 中嵌套 List 或 RecyclerView 的做法官方明確禁止,除了開發程序中遇到的各種視覺和互動問題,這種做法對性能也有較大損耗,ListView 等 UI 組件自身有垂直滾動功能,也沒有必要在嵌套一層ScrollView,目前為了較好的 UI 體驗,更貼近 Material Design 的設計,推薦使用 NestedScrollView,
13.在Adapter里為控制元件設定樣式/內容時,必須考慮if和else兩種情況
如選中和未選中的樣式,如果僅僅寫了if的樣式,在滾動時可能會造成錯亂
14.不要通過 Intent 在 Android 基礎組件之間傳遞大資料
binder transaction快取為 1MB,可能導致 OOM
15.注意多行程的情況
在 Application 的業務初始化代碼加入行程判斷,確保只在自己需要的行程初始化,特別是后臺行程減少不必要的業務初始化,
public class MyApplication extends Application {
@Override
public void onCreate() {
//在所有行程中初始化
....
//僅在主行程中初始化
if (mainProcess) {
...
}
//僅在后臺行程中初始化
if (bgProcess) {
...
}
}
}
16.新建執行緒
必須通過執行緒池提供(AsyncTask 或者 ThreadPoolExecutor或者其他形式自定義的執行緒池),不允許在應用中自行顯式創建執行緒,
17.禁止在多行程之間用 SharedPreferences 共享資料
雖然可以(MODE_MULTI_PROCESS),但官方已不推薦,
18.SharedPreference 提交資料時,盡量使用 Editor#apply() ,而非Editor#commit()
一般來講,僅當需要確定提交結果,并據此有后續操作時,才使用 Editor#commit(),
說明:SharedPreference 相關修改使用 apply 方法進行提交會先寫入記憶體,然后異步寫入磁盤,commit 方法是直接寫入磁盤,如果頻繁操作的話 apply 的性能會優于 commit,apply 會將最后修改內容寫入磁盤,但是如果希望立刻獲取存盤操作的結果,并據此做相應的其他操作,應當使用 commit,
19.謹防記憶體泄露和句柄溢位
資料庫 Cursor 必須確保使用完后關閉,以免記憶體泄漏,除了寫檔案,其他如網路連接、新建執行緒都會占用句柄,當句柄數量到達最大打開檔案數量(一般為1024)時,應用會閃退,
20.加載大圖片或者一次性加載多張圖片,應該在異步執行緒中進行
圖片的加載,涉及到 IO 操作,以及 CPU 密集操作,很可能引起卡頓,
21.啟動模式singletTop并不能絕對阻止多次啟動同一個Activity
可以有效防止手抖啟動多次同個Activity的情況,但如果是for回圈或者并發啟動的情況,就不好使了,如果確實要保證只啟動一個實體,使用singleInstance,一般來說,不會在for回圈里啟動同一個Activity,所以一般情況下做好手抖點擊的情況即可,
22.打release包開啟混淆注意事項
一定要仔細檢查相關開源庫的混淆規則,保證自身APP的bean不被混淆,否則JSON決議可能會出錯
23.使用ViewBinding時要謹慎
如果已經在抽象的父類Activity執行了setContentView方法,則子類絕對不能在重寫onCreate方法的時候再次setContentView,否則會提示找不到相關的view,表現出來是RecyclerView不顯示等
24.禁止使用BigDecimal的引數為double型別的建構式:BigDecimal(double val)
反例:
BigDecimal b1 = new BigDecimal(0.1);
BigDecimal b2 = new BigDecimal(0.5);
System.out.println("b1="+b1+"\nb2="+b2);
---------------結果----------------------
b1=0.1000000000000000055511151231257827021181583404541015625
b2=0.5
因為二進制運算不能提供準確的值,其實不能說二進制不能提供準確的值,而是二進制不能準確的表示一個小數,就像十進制不能準確的表示1/3,1/6等(因為1/3=0.333333333333333…,我們始終不能說清楚這后面有多少個3,所以說十進制表示不了1/3),
正例:
//使用String構造
BigDecimal b1 = new BigDecimal("0.1");
//或者是:
BigDecimal b1 = BigDecimal.valueOf(0.1);
25.一些推薦的習慣
1.如果僅僅使用到一個方法,不涉及到成員變數,則寫成靜態方法;
2.適當使用軟參考和弱參考;
3.謹慎使用多行程;
4.對于需要用到傳applicationcontext的方法,不要直接傳activity或fragment等,推薦傳context.getApplicationContext();
5.使用靜態內部類以免引起記憶體泄露;
6.謹防靜態變數、單例、屬性影片等引起記憶體泄露的情況,注冊、解注冊要成對出現;
7.對Cursor、Receiver、Sensor、File、Bitmap等物件,需注意回收與解注冊;
8.使用LeakCanary檢測記憶體泄漏;
9.減少不必要的成員變數;
10.盡量不要使用列舉;
不可否認enums會使得代碼更易讀更安全,但是在撰寫高效Android代碼時避免使用列舉,我們在很多經典的Java書已經看到推薦使用列舉來代替int常量了,特別是大型的App中,能不用則不用,因為它會犧牲執行的速度和并大幅增加檔案體積,這也是性能優化中減少OOM的一個方面,
參考:https://www.cnblogs.com/zgz345/p/5871351.html
11.盡量不要使用反射;
①與反射相關的代碼,經常是難以閱讀的,
②反射對性能影響還是比較大的,比正常代碼運行速度慢一到兩個數量級,所以,對于一個專案中處于運行效率關鍵位置的代碼,盡量避免使用反射特性,
總結:如果不是為了實作某些特殊的功能,如呼叫系統隱藏的api等,盡量少用反射,
12.使用OpenGL進行復雜的繪圖操作;
13.使用SurfaceView替代View進行大量、頻繁的繪圖操作;
14.使用視圖快取,而不是每次都執行inflate()
26.參考與進階推薦
參考:
1.阿里巴巴 Java 開發手冊
2.阿里巴巴Android開發手冊
推薦:《Effective Java》
要摳的知識點:設計模式、演算法、編程思想(開源與實作原理)
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/345790.html
標籤:其他
