前言
https://juejin.im/post/5ca42bac51882543f96dc4b7
代碼是一個程序猿的門面,有門面的程序猿才是一個好程序猿螟凭。
本文節(jié)選自阿里巴巴開發(fā)手冊壳嚎,下載地址
本手冊以開發(fā)者為中心視角分為Java語言規(guī)范(遵循《阿里巴巴Java開發(fā)手冊》)的烁, Android 資源文件命名與使用戳晌,Android 基本組件秒旋,UI 與布局,進程裳涛、線程與消息通信木张, 文件與數(shù)據(jù)庫,Bitmap端三、Drawable 與動畫舷礼,安全,其他等九大部分郊闯,根據(jù)約束力強弱妻献, 規(guī)約依次分為強制、推薦团赁、參考三大類:
!!!必須遵守育拨,違反本約定或?qū)饑乐氐暮蠊?/p>
@@@盡量遵守,長期遵守有助于系統(tǒng)穩(wěn)定性和合作效率的提升然痊;
1.###充分理解,技術(shù)意識的引導(dǎo)屉符,是個人學(xué)習(xí)剧浸、團隊溝通锹引、項目合作的方向。
對于規(guī)約條目的延伸信息中唆香,“說明”對內(nèi)容做了適當擴展和解釋嫌变;“正例”提倡 什么樣的編碼和實現(xiàn)方式;“反例”說明需要提防的雷區(qū)躬它,以及錯誤案例腾啥。
一、Java 語言規(guī)范
遵循《阿里巴巴 Java 開發(fā)手冊》手冊下載地址:yq.aliyun.com/articles/69…
二冯吓、Android 資源文件命名與使用
1.@@@資源文件需帶模塊前綴倘待。
2.@@@layout 文件的命名方式。 Activity 的 layout 以 module_activity 開頭 Fragment 的 layout 以 module_fragment 開頭 Dialog 的 layout 以 module_dialog 開頭 include 的 layout 以 module_include 開頭 ListView 的行 layout 以 module_list_item 開頭 RecyclerView 的 item layout 以 module_recycle_item 開頭 GridView 的行 layout 以 module_grid_item 開頭
3.@@@drawable 資源名稱以小寫單詞+下劃線的方式命名组贺,根據(jù)分辨率不同存放在不同的 drawable 目錄下凸舵,建議只使用一套,例如 drawable-xhdpi。采用規(guī)則如下:模塊名_業(yè)務(wù)功能描述_控件描述_控件狀態(tài)限定詞 如:module_login_btn_pressed,module_tabs_icon_home_normal
4.@@@anim 資源名稱以小寫單詞+下劃線的方式命名失尖,采用以下規(guī)則:模塊名_邏輯名稱_[方向|序號]tween 動畫資源 : 盡可能以通用的動畫名稱命名啊奄,如 module_fade_in ,module_fade_out , module_push_down_in (動畫+方向);frame 動畫資源:盡可能以模 塊+功能命名+序號掀潮。如:module_loading_grey_001
5.@@@color 資源使用#AARRGGBB 格式菇夸,寫入 modul_colors.xml 文件中,命名格式采用以下規(guī)則:模塊名_邏輯名稱_顏色 如:#33b5e5e5
6.@@@dimen 資源以小寫單詞+下劃線方式命名仪吧,寫入 module_dimens.xml 文件中庄新,采用以下規(guī)則:模塊名_描述信息 如:1dp
7.@@@style 資源采用小寫單詞+下劃線方式命名,寫入 module_styles.xml 文件中邑商,采用以下規(guī)則:父 style 名稱.當前 style 名稱 如:
8.@@@string資源文件或者文本用到字符需要全部寫入module_strings.xml文件中摄咆,字符串以小寫單詞+下劃線的方式命名,采用以下規(guī)則:模塊名_邏輯名稱 如:moudule_login_tips,module_homepage_notice_desc
9.@@@Id 資源原則上以駝峰法命名人断,View 組件的資源 id 需要以 View 的縮寫作為前綴吭从。常用縮寫表如下
其它控件的縮寫推薦使用小寫字母并用下劃線進行分割,例如: ProgressBar 對應(yīng)的縮寫為 progress_bar DatePicker 對應(yīng)的縮寫為 date_picker
10.@@@大分辨率圖片(單維度超過 1000)大分辨率圖片建議統(tǒng)一放在 xxhdpi 目錄下管理恶迈,否則將導(dǎo)致占用內(nèi)存成倍數(shù)增加涩金。 說明: 為了支持多種屏幕尺寸和密度,Android 為多種屏幕提供不同的資源目錄進行適配暇仲。 為不同屏幕密度提供不同的位圖可繪制對象步做,可用于密度特定資源的配置限定符(在 下面詳述) 包括 ldpi(低)、mdpi(中)奈附、 hdpi(高)全度、xhdpi(超高)、xxhdpi (超 超高)和 xxxhdpi(超超超高)斥滤。例如将鸵,高密度屏幕的位圖應(yīng)使用 drawable-hdpi/勉盅。 根據(jù)當前的設(shè)備屏幕尺寸和密度,將會尋找最匹配的資源顶掉,如果將高分辨率圖片放 入低密度目錄草娜,將會造成低端機加載過大圖片資源,又可能造成 OOM痒筒,同時也是資 源浪費宰闰,沒有必要在低端機使用大圖。 正例: 將 144 * 144 的應(yīng)用圖標 PNG 文件放在 drawable-xxhdpi 目錄 反例: 將 144 * 144 的應(yīng)用圖標 PNG 文件放在 drawable-mhdpi 目錄 擴展參考:developer.android.com/guide/pract…
三簿透、Android 基本組件
Android 基本組件指 Activity移袍、Fragment、Service萎战、BroadcastReceiver咐容、 ContentProvider 等等。
1.!!!Activity 間的數(shù)據(jù)通信蚂维,對于數(shù)據(jù)量比較大的戳粒,避免使用 Intent + Parcelable 的方式,可以考慮 EventBus 等替代方案虫啥,以免造成 TransactionTooLargeException蔚约。
2.Activity#onSaveInstanceState()方法不是 Activity 生命周期方法,也不保證 一定會被調(diào)用涂籽。它是用來在 Activity 被意外銷毀時保存 UI 狀態(tài)的苹祟,只能用于保存臨 時性數(shù)據(jù),例如 UI 控件的屬性等评雌,不能跟數(shù)據(jù)的持久化存儲混為一談树枫。持久化存儲 應(yīng)該在 Activity#onPause()/onStop()中實行。
3.Activity 間通過隱式 Intent 的跳轉(zhuǎn)景东,在發(fā)出 Intent 之前必須通過 resolveActivity 檢查砂轻,避免找不到合適的調(diào)用組件,造成 ActivityNotFoundException 的異常斤吐。
4.避免在 Service#onStartCommand()/onBind()方法中執(zhí)行耗時操作搔涝,如果確 實有需求,應(yīng)改用 IntentService 或采用其他異步機制完成和措。
5.避免在 BroadcastReceiver#onReceive()中執(zhí)行耗時操作庄呈,如果有耗時工作, 應(yīng)該創(chuàng)建 IntentService 完成派阱,而不應(yīng)該在 BroadcastReceiver 內(nèi)創(chuàng)建子線程去做诬留。 說明: 由于該方法是在主線程執(zhí)行,如果執(zhí)行耗時操作會導(dǎo)致 UI 不流暢∥亩遥可以使用 IntentService 傀广、 創(chuàng) 建 HandlerThread 或者調(diào)用 Context#registerReceiver (BroadcastReceiver, IntentFilter, String, Handler)方法等方式,在其他 Wroker 線程 執(zhí)行 onReceive 方法彩届。BroadcastReceiver#onReceive()方法耗時超過 10 秒鐘,可 能會被系統(tǒng)殺死誓酒。 擴展參考:developer.android.com/reference/a…(android.content.Context, android.content.Intent)
6.避免使用隱式 Intent 廣播敏感信息樟蠕,信息可能被其他注冊了對應(yīng) BroadcastReceiver 的 App 接收。 說明: 通過 Context#sendBroadcast()發(fā)送的隱式廣播會被所有感興趣的 receiver 接收靠柑,惡 意應(yīng)用注冊監(jiān)聽該廣播的 receiver 可能會獲取到 Intent 中傳遞的敏感信息寨辩,并進行 其他危險操作。如果發(fā)送的廣播為使用 Context#sendOrderedBroadcast()方法發(fā)送 的有序廣播歼冰,優(yōu)先級較高的惡意 receiver 可能直接丟棄該廣播靡狞,造成服務(wù)不可用, 或者向廣播結(jié)果塞入惡意數(shù)據(jù)隔嫡。 如果廣播僅限于應(yīng)用內(nèi)甸怕,則可以使用 LocalBroadcastManager#sendBroadcast()實 現(xiàn),避免敏感信息外泄和 Intent 攔截的風(fēng)險腮恩。 以上廣播可能被其他應(yīng)用的如下 receiver 接收導(dǎo)致敏感信息泄漏 擴展參考:wiki.sei.cmu.edu/confluence/…cwe.mitre.org/data/defini…
7.添 加 Fragment 時 梢杭, 確 保 FragmentTransaction#commit() 在 Activity#onPostResume()或者 FragmentActivity#onResumeFragments()內(nèi)調(diào)用。 不要隨意使用 FragmentTransaction#commitAllowingStateLoss()來代替秸滴,任何 commitAllowingStateLoss()的使用必須經(jīng)過 code review武契,確保無負面影響。 說明: Activity 可 能 因 為 各 種 原 因 被 銷 毀 荡含, Android 支 持 頁 面 被 銷 毀 前 通 過 Activity#onSaveInstanceState() 保 存 自 己 的 狀 態(tài) 咒唆。 但 如 果 FragmentTransaction.commit()發(fā)生在 Activity 狀態(tài)保存之后,就會導(dǎo)致 Activity 重 建释液、恢復(fù)狀態(tài)時無法還原頁面狀態(tài)全释,從而可能出錯。為了避免給用戶造成不好的體 驗均澳,系統(tǒng)會拋出 IllegalStateExceptionStateLoss 異常恨溜。推薦的做法是在 Activity 的 onPostResume() 或 onResumeFragments() ( 對 FragmentActivity )里執(zhí)行 FragmentTransaction.commit(),如有必要也可在 onCreate()里執(zhí)行找前。不要隨意改用 FragmentTransaction.commitAllowingStateLoss() 或 者 直 接 使 用 try-catch 避 免 crash糟袁,這不是問題的根本解決之道,當且僅當你確認 Activity 重建躺盛、恢復(fù)狀態(tài)時项戴, 本次 commit 丟失不會造成影響時才可這么做。 擴展參考:www.androiddesignpatterns.com/2013/08/fra…developer.android.com/reference/a…
8.不要在 Activity#onDestroy()內(nèi)執(zhí)行釋放資源的工作槽惫,例如一些工作線程的 銷毀和停止周叮,因為 onDestroy()執(zhí)行的時機可能較晚辩撑。可根據(jù)實際需要仿耽,在 Activity#onPause()/onStop()中結(jié)合 isFinishing()的判斷來執(zhí)行合冀。
9.如非必須,避免使用嵌套的 Fragment项贺。 說明: 嵌套 Fragment 是在 Android API 17 添加到 SDK 以及 Support 庫中的功能君躺, Fragment 嵌套使用會有一些坑,容易出現(xiàn) bug开缎,比較常見的問題有如下幾種:
onActivityResult()方法的處理錯亂棕叫,內(nèi)嵌的 Fragment 可能收不到該方法的回調(diào),需要由宿主 Fragment 進行轉(zhuǎn)發(fā)處理奕删;
突變動畫效果俺泣;
被繼承的 setRetainInstance(),導(dǎo)致在 Fragment 重建時多次觸發(fā)不必要的邏 輯完残。 非必須的場景盡可能避免使用嵌套 Fragment伏钠,如需使用請注意上述問題。 擴展參考:inthecheesefactory.com/blog/onacti…blog.csdn.net/megatronkin…
10.
總是使用顯式 Intent 啟動或者綁定 Service谨设,且不要為服務(wù)聲明 Intent Filter贝润, 保證應(yīng)用的安全性。如果確實需要使用隱式調(diào)用铝宵,則可為 Service 提供 Intent Filter 并從 Intent 中排除相應(yīng)的組件名稱打掘,但必須搭配使用 Intent#setPackage()方法設(shè)置 Intent 的指定包名,這樣可以充分消除目標服務(wù)的不確定性鹏秋。
11.
Service 需要以多線程來并發(fā)處理多個啟動請求尊蚁,建議使用 IntentService, 可避免各種復(fù)雜的設(shè)置侣夷。 說明: Service 組件一般運行主線程横朋,應(yīng)當避免耗時操作,如果有耗時操作應(yīng)該在 Worker 線程執(zhí)行百拓。 可以使用 IntentService 執(zhí)行后臺任務(wù)琴锭。 擴展參考:developer.android.com/training/ru…
12.
對于只用于應(yīng)用內(nèi)的廣播,優(yōu)先使用 LocalBroadcastManager 來進行注冊 和發(fā)送衙传,LocalBroadcastManager 安全性更好决帖,同時擁有更高的運行效率。 說明: 對于使用 Context#sendBroadcast()等方法發(fā)送全局廣播的代碼進行提示蓖捶。如果該廣 播僅用于應(yīng)用內(nèi)地回,則可以使用 LocalBroadcastManager 來避免廣播泄漏以及廣播被 攔截等安全問題,同時相對全局廣播本地廣播的更高效。
13.
當前Activity的onPause方法執(zhí)行結(jié)束后才會執(zhí)行下一個Activity的onCreate方法刻像,所以在 onPause 方法中不適合做耗時較長的工作畅买,這會影響到頁面之間的跳 轉(zhuǎn)效率。
14.
不要在 Android 的 Application 對象中緩存數(shù)據(jù)细睡」刃撸基礎(chǔ)組件之間的數(shù)據(jù)共享 請使用 Intent 等機制,也可使用 SharedPreferences 等數(shù)據(jù)持久化機制溜徙。
15.
使用 Toast 時洒宝,建議定義一個全局的 Toast 對象,這樣可以避免連續(xù)顯示 Toast 時不能取消上一次 Toast 消息的情況(如果你有連續(xù)彈出 Toast 的情況萌京,避免 使用 Toast.makeText)。
16.
使用 Adapter 的時候宏浩,如果你使用了 ViewHolder 做緩存知残,在 getView()的 方法中無論這項 convertView 的每個子控件是否需要設(shè)置屬性(比如某個 TextView 設(shè)置的文本可能為 null,某個按鈕的背景色為透明比庄,某控件的顏色為透明等)求妹,都需 要為其顯式設(shè)置屬性(Textview 的文本為空也需要設(shè)置 setText(""),背景透明也需要 設(shè)置)佳窑,否則在滑動的過程中制恍,因為 adapter item 復(fù)用的原因,會出現(xiàn)內(nèi)容的顯示錯 亂神凑。
17.
Activity或者 Fragment 中動態(tài)注冊BroadCastReceiver 時净神,registerReceiver() 和 unregisterReceiver()要成對出現(xiàn)。 說明: 如果 registerReceiver()和 unregisterReceiver()不成對出現(xiàn)溉委,則可能導(dǎo)致已經(jīng)注冊的 receiver 沒有在合適的時機注銷鹃唯,導(dǎo)致內(nèi)存泄漏,占用內(nèi)存空間瓣喊,加重 SystemService 負擔(dān)坡慌。 部分華為的機型會對 receiver 進行資源管控,單個應(yīng)用注冊過多 receiver 會觸發(fā)管 控模塊拋出異常藻三,應(yīng)用直接崩潰洪橘。 Activity 的生命周期不對應(yīng),可能出現(xiàn)多次 onResume 造成 receiver 注冊多個棵帽,但 最終只注銷一個熄求,其余 receiver 產(chǎn)生內(nèi)存泄漏。
四逗概、UI 與布局
布局中不得不使用 ViewGroup 多重嵌套時抡四,不要使用 LinearLayout 嵌套,改用 RelativeLayout,可以有效降低嵌套數(shù)指巡。 說明: Android 應(yīng)用頁面上任何一個 View 都需要經(jīng)過 measure淑履、layout、draw 三個步驟 才能被正確的渲染藻雪。從 xml layout 的頂部節(jié)點開始進行 measure秘噪,每個子節(jié)點都需 要向自己的父節(jié)點提供自己的尺寸來決定展示的位置,在此過程中可能還會重新 measure(由此可能導(dǎo)致 measure 的時間消耗為原來的 2-3 倍)勉耀。節(jié)點所處位置越 深指煎,套嵌帶來的 measure 越多,計算就會越費時便斥。這就是為什么扁平的 View 結(jié)構(gòu) 會性能更好至壤。 同時,頁面擁上的 View 越多枢纠,measure像街、layout、draw 所花費的時間就越久晋渺。要縮 短這個時間镰绎,關(guān)鍵是保持 View 的樹形結(jié)構(gòu)盡量扁平,而且要移除所有不需要渲染的 View木西。理想情況下畴栖,總共的 measure,layout八千,draw 時間應(yīng)該被很好的控制在 16ms 以內(nèi)吗讶,以保證滑動屏幕時 UI 的流暢。 要找到那些多余的 View(增加渲染延遲的 view)恋捆,可以用 Android Studio Monitor 里的 Hierarachy Viewer 工具关翎,可視化的查看所有的 view。 多重嵌套導(dǎo)致 measure 以及 layout 等步驟耗時過多鸠信。 擴展參考:
developer.android.com/studio/prof…
www.safaribooksonline.com/library/vie…
在 Activity 中顯示對話框或彈出浮層時纵寝,盡量使用 DialogFragment,而非 Dialog/AlertDialog星立,這樣便于隨Activity生命周期管理對話框/彈出浮層的生命周期爽茴。
源文件統(tǒng)一采用 UTF-8 的形式進行編碼。
禁止在非 ui 線程進行 view 相關(guān)操作绰垂。
文本大小使用單位 dp室奏,view 大小使用單位 dp。對于 Textview劲装,如果在文字大小確定的情況下推薦使用 wrap_content 布局避免出現(xiàn)文字顯示不全的適配問 題胧沫。
禁止在設(shè)計布局時多次設(shè)置子 view 和父 view 中為同樣的背景造成頁面過度繪制昌简,推薦將不需要顯示的布局進行及時隱藏。
靈活使用布局绒怨,推薦 Merge纯赎、ViewStub 來優(yōu)化布局,盡可能多的減少 UI布局層級南蹂,推薦使用 FrameLayout犬金,LinearLayout、RelativeLayout 次之六剥。
在需要時刻刷新某一區(qū)域的組件時晚顷,建議通過以下方式避免引發(fā)全局 layout 刷新:
設(shè)置固定的 view 大小的高寬,如倒計時組件等疗疟;
調(diào)用 view 的 layout 方式修改位置该默,如彈幕組件等;
通過修改 canvas 位置并且調(diào)用 invalidate(int l, int t, int r, int b)等方式限定刷新 區(qū)域策彤;
通過設(shè)置一個是否允許 requestLayout 的變量栓袖,然后重寫控件的 requestlayout、 onSizeChanged 方 法 锅锨, 判 斷 控 件 的 大 小 沒 有 改 變 的 情 況 下 , 當 進 入 requestLayout 的時候恋沃,直接返回而不調(diào)用 super 的 requestLayout 方法必搞。
不能在 Activity 沒有完全顯示時顯示 PopupWindow 和 Dialog。
10.
盡量不要使用 AnimationDrawable囊咏,它在初始化的時候就將所有圖片加載到內(nèi)存中恕洲,特別占內(nèi)存,并且還不能釋放梅割,釋放之后下次進入再次加載時會報錯霜第。 說明: Android 的幀動畫可以使用 AnimationDrawable 實現(xiàn),但是如果你的幀動畫中如果 包含過多幀圖片户辞,一次性加載所有幀圖片所導(dǎo)致的內(nèi)存消耗會使低端機發(fā)生 OOM 異常泌类。幀動畫所使用的圖片要注意降低內(nèi)存消耗,當圖片比較大時底燎,容易出現(xiàn) OOM刃榨。 擴展參考:
stackoverflow.com/questions/8…
developer.android.com/reference/a…
11.
不能使用 ScrollView 包裹 ListView/GridView/ExpandableListVIew;因為這樣會把 ListView 的所有 Item 都加載到內(nèi)存中,要消耗巨大的內(nèi)存和 cpu 去繪制圖面双仍。 說明: ScrollView 中嵌套 List 或 RecyclerView 的做法官方明確禁止枢希。除了開發(fā)過程中遇到 的各種視覺和交互問題,這種做法對性能也有較大損耗朱沃。ListView 等 UI 組件自身有 垂直滾動功能苞轿,也沒有必要在嵌套一層 ScrollView茅诱。目前為了較好的 UI 體驗,更貼 近 Material Design 的設(shè)計搬卒,推薦使用 NestedScrollView瑟俭。 擴展參考:
developer.android.com/reference/a…
developer.android.com/reference/a…rollView.html
五、進程秀睛、線程與消息通信
不要通過 Intent 在 Android 基礎(chǔ)組件之間傳遞大數(shù)據(jù)(binder transaction緩存為 1MB)尔当,可能導(dǎo)致 OOM。
在 Application 的業(yè)務(wù)初始化代碼加入進程判斷蹂安,確保只在自己需要的進程初始化椭迎。特別是后臺進程減少不必要的業(yè)務(wù)初始化。
新建線程時田盈,必須通過線程池提供(AsyncTask 或者 ThreadPoolExecutor 或者其他形式自定義的線程池)畜号,不允許在應(yīng)用中自行顯式創(chuàng)建線程。 說明: 使用線程池的好處是減少在創(chuàng)建和銷毀線程上所花的時間以及系統(tǒng)資源的開銷允瞧,解 決資源不足的問題简软。如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類線程而導(dǎo)致 消耗完內(nèi)存或者“過度切換”的問題述暂。另外創(chuàng)建匿名線程不便于后續(xù)的資源使用分析痹升, 對性能分析等會造成困擾。 擴展參考:blog.mindorks.com/threadpoole…
線程池不允許使用 Executors 去創(chuàng)建畦韭,而是通過 ThreadPoolExecutor 的方式疼蛾,這樣的處理方式讓寫的同學(xué)更加明確線程池的運行規(guī)則,規(guī)避資源耗盡的風(fēng)險艺配。 說明: Executors 返回的線程池對象的弊端如下:
FixedThreadPool 和 SingleThreadPool : 允 許 的 請 求 隊 列 長 度 為 Integer.MAX_VALUE察郁,可能會堆積大量的請求,從而導(dǎo)致 OOM转唉;
CachedThreadPool 和 ScheduledThreadPool : 允 許 的 創(chuàng) 建 線 程 數(shù) 量 為 Integer.MAX_VALUE皮钠,可能會創(chuàng)建大量的線程,從而導(dǎo)致 OOM赠法。
子線程中不能更新界面麦轰,更新界面必須在主線程中進行,網(wǎng)絡(luò)操作不能在主線程中調(diào)用砖织。
不要在非 UI 線程中初始化 ViewStub原朝,否則會返回 null。
盡量減少不同 APP 之間的進程間通信及拉起行為镶苞。拉起導(dǎo)致占用系統(tǒng)資源喳坠,影響用戶體驗。
新建線程時茂蚓,定義能識別自己業(yè)務(wù)的線程名稱壕鹉,便于性能優(yōu)化和問題排查剃幌。
ThreadPoolExecutor 設(shè)置線程存活時間(setKeepAliveTime),確绷涝。空閑時線程能被釋放负乡。
【 推 薦 】 禁 止 在 多 進 程 之 間 用 SharedPreferences 共 享 數(shù) 據(jù) , 雖 然 可 以 (MODE_MULTI_PROCESS)脊凰,但官方已不推薦抖棘。
11.
謹慎使用 Android 的多進程,多進程雖然能夠降低主進程的內(nèi)存壓力狸涌,但會遇到如下問題:
不能實現(xiàn)完全退出所有 Activity 的功能切省;
首次進入新啟動進程的頁面時會有延時的現(xiàn)象(有可能黑屏、白屏幾秒帕胆,是白 屏還是黑屏和新 Activity 的主題有關(guān))朝捆;
應(yīng)用內(nèi)多進程時,Application 實例化多次懒豹,需要考慮各個模塊是否都需要在所 有進程中初始化芙盘;
多進程間通過 SharedPreferences 共享數(shù)據(jù)時不穩(wěn)定。
六脸秽、文件與數(shù)據(jù)庫
任何時候不要硬編碼文件路徑儒老,請使用 Android 文件系統(tǒng) API 訪問。 說明: Android 應(yīng)用提供內(nèi)部和外部存儲记餐,分別用于存放應(yīng)用自身數(shù)據(jù)以及應(yīng)用產(chǎn)生的用 戶數(shù)據(jù)驮樊。可以通過相關(guān) API 接口獲取對應(yīng)的目錄剥扣,進行文件操作巩剖。 擴展參考:
developer.android.com/training/da…
developer.android.com/reference/a…ernalStorageDirectory()
當使用外部存儲時铝穷,必須檢查外部存儲的可用性钠怯。
應(yīng)用間共享文件時,不要通過放寬文件系統(tǒng)權(quán)限的方式去實現(xiàn)曙聂,而應(yīng)使用FileProvider晦炊。
SharedPreference 中只能存儲簡單數(shù)據(jù)類型(int、boolean宁脊、String 等)断国,復(fù)雜數(shù)據(jù)類型建議使用文件、數(shù)據(jù)庫等其他方式存儲榆苞。
SharedPreference 提 交 數(shù) 據(jù) 時 稳衬, 盡 量 使 用 Editor#apply() ,而非 Editor#commit()坐漏。一般來講薄疚,僅當需要確定提交結(jié)果碧信,并據(jù)此有后續(xù)操作時,才使 用 Editor#commit()街夭。 說明: SharedPreference 相關(guān)修改使用 apply 方法進行提交會先寫入內(nèi)存砰碴,然后異步寫入 磁盤,commit 方法是直接寫入磁盤板丽。如果頻繁操作的話 apply 的性能會優(yōu)于 commit呈枉, apply 會將最后修改內(nèi)容寫入磁盤。但是如果希望立刻獲取存儲操作的結(jié)果埃碱,并據(jù)此 做相應(yīng)的其他操作猖辫,應(yīng)當使用 commit。
數(shù)據(jù)庫 Cursor 必須確保使用完后關(guān)閉乃正,以免內(nèi)存泄漏住册。 說明: Cursor 是對數(shù)據(jù)庫查詢結(jié)果集管理的一個類,當查詢的結(jié)果集較小時瓮具,消耗內(nèi)存不 易察覺荧飞。但是當結(jié)果集較大,長時間重復(fù)操作會導(dǎo)致內(nèi)存消耗過大名党,需要開發(fā)者在 操作完成后手動關(guān)閉 Cursor叹阔。 數(shù)據(jù)庫 Cursor 在創(chuàng)建及使用時,可能發(fā)生各種異常传睹,無論程序是否正常結(jié)束耳幢,必須 在最后確保 Cursor 正確關(guān)閉,以避免內(nèi)存泄漏欧啤。同時睛藻,如果 Cursor 的使用還牽涉 多線程場景,那么需要自行保證操作同步邢隧。
多線程操作寫入數(shù)據(jù)庫時店印,需要使用事務(wù),以免出現(xiàn)同步問題倒慧。 說明: Android 的通過 SQLiteOpenHelper 獲取數(shù)據(jù)庫 SQLiteDatabase 實例按摘,Helper 中會 自動緩存已經(jīng)打開的 SQLiteDatabase 實例,單個 App 中應(yīng)使用 SQLiteOpenHelper 的單例模式確保數(shù)據(jù)庫連接唯一纫谅。由于 SQLite 自身是數(shù)據(jù)庫級鎖炫贤,單個數(shù)據(jù)庫操作 是保證線程安全的(不能同時寫入)券敌,transaction 時一次原子操作棘街,因此處于事務(wù)中 的操作是線程安全的夏伊。 若同時打開多個數(shù)據(jù)庫連接莹汤,并通過多線程寫入數(shù)據(jù)庫莹捡,會導(dǎo)致數(shù)據(jù)庫異常巍耗,提示 數(shù)據(jù)庫已被鎖住铸豁。
大數(shù)據(jù)寫入數(shù)據(jù)庫時慷荔,請使用事務(wù)或其他能夠提高 I/O 效率的機制,保證執(zhí)行速度口柳。
執(zhí)行 SQL 語句時苹粟,應(yīng)使用 SQLiteDatabase#insert()、update()跃闹、delete()嵌削,不要使用 SQLiteDatabase#execSQL(),以免 SQL 注入風(fēng)險望艺。
10.
如果 ContentProvider 管理的數(shù)據(jù)存儲在 SQL 數(shù)據(jù)庫中苛秕,應(yīng)該避免將不受信任的外部數(shù)據(jù)直接拼接在原始 SQL 語句中,可使用一個用于將 ? 作為可替換參 數(shù)的選擇子句以及一個單獨的選擇參數(shù)數(shù)組找默,會避免 SQL 注入艇劫。
七、Bitmap惩激、Drawable 與動畫
加載大圖片或者一次性加載多張圖片店煞,應(yīng)該在異步線程中進行。圖片的加載风钻,涉及到 IO 操作顷蟀,以及 CPU 密集操作,很可能引起卡頓骡技。
在 ListView鸣个,ViewPager,RecyclerView布朦,GirdView 等組件中使用圖片時囤萤,應(yīng)做好圖片的緩存,避免始終持有圖片導(dǎo)致內(nèi)存泄露是趴,也避免重復(fù)創(chuàng)建圖片涛舍,引起 性 能 問 題 。 建 議 使 用 Fresco (?github.com/facebook/fr…)右遭、 Glide (github.com/bumptech/gl…
png 圖片使用 tinypng 或者類似工具壓縮處理做盅,減少包體積缤削。
應(yīng)根據(jù)實際展示需要窘哈,壓縮圖片,而不是直接顯示原圖亭敢。手機屏幕比較小滚婉,直接顯示原圖,并不會增加視覺上的收益帅刀,但是卻會耗費大量寶貴的內(nèi)存让腹。
使用完畢的圖片远剩,應(yīng)該及時回收,釋放寶貴的內(nèi)存骇窍。
針對不同的屏幕密度瓜晤,提供對應(yīng)的圖片資源,使內(nèi)存占用和顯示效果達到合理的平衡腹纳。如果為了節(jié)省包體積痢掠,可以在不影響 UI 效果的前提下,省略低密度圖片嘲恍。
在 Activity.onPause()或 Activity.onStop()回調(diào)中足画,關(guān)閉當前 activity 正在執(zhí)行的的動畫。
在動畫或者其他異步任務(wù)結(jié)束時佃牛,應(yīng)該考慮回調(diào)時刻的環(huán)境是否還支持業(yè)務(wù)處理淹辞。例如 Activity 的 onStop()函數(shù)已經(jīng)執(zhí)行,且在該函數(shù)中主動釋放了資源俘侠,此時回調(diào)中如果不做判斷就會空指針崩潰象缀。
使用 inBitmap 重復(fù)利用內(nèi)存空間,避免重復(fù)開辟新內(nèi)存爷速。
10.
使用 ARGB_565 代替 ARGB_888攻冷,在不怎么降低視覺效果的前提下,減少內(nèi)存占用遍希。 說明: android.graphics.Bitmap.Config 類中關(guān)于圖片顏色的存儲方式定義:
ALPHA_8 代表 8 位 Alpha 位圖等曼;
ARGB_4444 代表 16 位 ARGB 位圖;
ARGB_8888 代表 32 位 ARGB 位圖凿蒜;
RGB_565 代表 8 位 RGB 位圖禁谦。 位圖位數(shù)越高,存儲的顏色信息越多废封,圖像也就越逼真州泊。大多數(shù)場景使用的是 ARGB_8888 和 RGB_565,RGB_565 能夠在保證圖片質(zhì)量的情況下大大減少內(nèi)存 的開銷漂洋,是解決 oom 的一種方法遥皂。 但是一定要注意 RGB_565 是沒有透明度的,如果圖片本身需要保留透明度刽漂,那么 就不能使用 RGB_565演训。
11.
盡量減少 Bitmap(BitmapDrawable)的使用,盡量使用純色(ColorDrawable)贝咙、 漸變色(GradientDrawable)样悟、StateSelector(StateListDrawable)等與 Shape 結(jié) 合的形式構(gòu)建繪圖。
12.
謹慎使用 gif 圖片,注意限制每個頁面允許同時播放的 gif 圖片窟她,以及單個gif 圖片的大小陈症。
13.
大圖片資源不要直接打包到 apk,可以考慮通過文件倉庫遠程下載震糖,減小包體積录肯。
14.
根據(jù)設(shè)備性能,選擇性開啟復(fù)雜動畫吊说,以實現(xiàn)一個整體較優(yōu)的性能和體驗嘁信;
15.
在有強依賴 onAnimationEnd 回調(diào)的交互時,如動畫播放完畢才能操作頁面 疏叨, onAnimationEnd 可 能 會 因 各 種 異 常 沒 被 回 調(diào) ( 參 考 :stackoverflow.com/questions/5…)潘靖, 建 議 加 上 超 時 保 護 或 通 過 postDelay 替 代 onAnimationEnd。
16.
當 View Animation 執(zhí)行結(jié)束時蚤蔓,調(diào)用 View.clearAnimation()釋放相關(guān)資源卦溢。
八、安全
使用 PendingIntent 時秀又,禁止使用空 intent单寂,同時禁止使用隱式 Intent 說明:
使用 PendingIntent 時,使用了空 Intent,會導(dǎo)致惡意用戶劫持修改 Intent 的內(nèi) 容吐辙。禁止使用一個空 Intent 去構(gòu)造 PendingIntent宣决,構(gòu)造 PendingIntent 的 Intent 一定要設(shè)置 ComponentName 或者 action。
PendingIntent 可以讓其他 APP 中的代碼像是運行自己 APP 中昏苏。PendingIntent 的intent接收方在使用該intent時與發(fā)送方有相同的權(quán)限尊沸。在使用PendingIntent 時,PendingIntent 中包裝的 intent 如果是隱式的 Intent贤惯,容易遭到劫持洼专,導(dǎo)致 信息泄露。
禁止使用常量初始化矢量參數(shù)構(gòu)建 IvParameterSpec孵构,建議 IV 通過隨機方式產(chǎn)生屁商。 說明: 使用固定初始化向量,結(jié)果密碼文本可預(yù)測性會高得多颈墅,容易受到字典式攻擊蜡镶。iv 的作用主要是用于產(chǎn)生密文的第一個 block,以使最終生成的密文產(chǎn)生差異(明文相 同的情況下)恤筛,使密碼攻擊變得更為困難官还,除此之外 iv 并無其它用途。因此 iv 通過 隨機方式產(chǎn)生是一種十分簡便叹俏、有效的途徑妻枕。
將 android:allowbackup 屬性設(shè)置為 false僻族,防止 adb backup 導(dǎo)出數(shù)據(jù)粘驰。 說明: 在 AndroidManifest.xml 文件中為了方便對程序數(shù)據(jù)的備份和恢復(fù)在 Android API level 8 以后增加了 android:allowBackup 屬性值屡谐。默認情況下這個屬性值為 true,故 當 allowBackup 標志值為 true 時,即可通過 adb backup 和 adb restore 來備份和恢 復(fù)應(yīng)用程序數(shù)據(jù)蝌数。
在實現(xiàn)的 HostnameVerifier 子類中愕掏,需要使用 verify 函數(shù)效驗服務(wù)器主機名的合法性,否則會導(dǎo)致惡意程序利用中間人攻擊繞過主機名效驗顶伞。 說明: 在握手期間饵撑,如果 URL 的主機名和服務(wù)器的標識主機名不匹配,則驗證機制可以 回調(diào)此接口的實現(xiàn)程序來確定是否應(yīng)該允許此連接唆貌。如果回調(diào)內(nèi)實現(xiàn)不恰當滑潘,默認 接受所有域名,則有安全風(fēng)險锨咙。
利用 X509TrustManager 子類中的 checkServerTrusted 函數(shù)效驗服務(wù)器端證書的合法性语卤。 說明: 在實現(xiàn)的 X509TrustManager 子類中未對服務(wù)端的證書做檢驗,這樣會導(dǎo)致不被信 任的證書繞過證書效驗機制酪刀。
META-INF 目錄中不能包含如.apk,.odex,.so 等敏感文件粹舵,該文件夾沒有經(jīng)過簽名,容易被惡意替換骂倘。
Receiver/Provider 不能在毫無權(quán)限控制的情況下眼滤,將 android:export 設(shè)置為 true。
數(shù)據(jù)存儲在 Sqlite 或者輕量級存儲需要對數(shù)據(jù)進行加密历涝,取出來的時候進行解密诅需。
阻止 webview 通過 file:schema 方式訪問本地敏感數(shù)據(jù)。
10.
不要廣播敏感信息荧库,只能在本應(yīng)用使用 LocalBroadcast诱担,避免被別的應(yīng)用收到,或者 setPackage 做限制电爹。
11.
不要把敏感信息打印到 log 中蔫仙。 說明: 在 APP 的開發(fā)過程中,為了方便調(diào)試丐箩,通常會使用 log 函數(shù)輸出一些關(guān)鍵流程的信 息摇邦,這些信息中通常會包含敏感內(nèi)容,如執(zhí)行流程屎勘、明文的用戶名密碼等施籍,這會讓 攻擊者更加容易的了解 APP 內(nèi)部結(jié)構(gòu)方便破解和攻擊,甚至直接獲取到有價值的敏 感信息概漱。
12.
對于內(nèi)部使用的組件丑慎,顯示設(shè)置組件的"android:exported"屬性為 false。 說明: Android 應(yīng)用使用 Intent 機制在組件之間傳遞數(shù)據(jù),如果應(yīng)用在使用 getIntent()竿裂, getAction()玉吁,Intent.getXXXExtra()獲取到空數(shù)據(jù)、異衬逡欤或者畸形數(shù)據(jù)時沒有進行異 常捕獲进副,應(yīng)用就會發(fā)生 Crash,應(yīng)用不可使用(本地拒絕服務(wù))悔常。惡意應(yīng)用可通過向 受害者應(yīng)用發(fā)送此類空數(shù)據(jù)影斑、異常或者畸形數(shù)據(jù)從而使應(yīng)用產(chǎn)生本地拒絕服務(wù)机打。
13.
應(yīng)用發(fā)布前確保 android:debuggable 屬性設(shè)置為 false矫户。
14.
使用 Intent Scheme URL 需要做過濾。 說明: 如果瀏覽器支持 Intent Scheme Uri 語法残邀,如果過濾不當吏垮,那么惡意用戶可能通過瀏 覽器 js 代碼進行一些惡意行為,比如盜取 cookie 等罐旗。如果使用了 Intent.parseUri 函 數(shù) 膳汪, 獲 取 的 intent 必 須 嚴格過濾, intent 至少包含 addCategory(“android.intent.category.BROWSABLE”) 九秀, setComponent(null) 遗嗽, setSelector(null)3 個策略。
15.
密鑰加密存儲或者經(jīng)過變形處理后用于加解密運算鼓蜒,切勿硬編碼到代碼中痹换。 說明: 應(yīng)用程序在加解密時,使用硬編碼在程序中的密鑰都弹,攻擊者通過反編譯拿到密鑰可 以輕易解密 APP 通信數(shù)據(jù)娇豫。
16.
將所需要動態(tài)加載的文件放置在 apk 內(nèi)部,或應(yīng)用私有目錄中畅厢,如果應(yīng)用必須要把所加載的文件放置在可被其他應(yīng)用讀寫的目錄中(比如 sdcard)冯痢,建議對不可信的加載源進行完整性校驗和白名單處理,以保證不被惡意代碼注入框杜。
17.
除非 min API level >=17浦楣,請注意 addJavascriptInterface 的使用。 說明: API level>=17咪辱,允許 js 被調(diào)用的函數(shù)必須以@JavascriptInterface 進行注解振劳,因此 不受影響; 對于 API level < 17油狂,盡量不要使用 addJavascriptInterface历恐,如果一定 要用寸癌,那么:
使用 https 協(xié)議加載 URL,使用證書校驗弱贼,防止訪問的頁面被篡改掛馬蒸苇;
對加載 URL 做白名單過濾、完整性校驗等防止訪問的頁面被篡改哮洽;
如果加載本地 html,應(yīng)該會 HTML 內(nèi)置在 APK 中填渠,以及對 HTML 頁面進行完整 性校驗弦聂。
18.
使用 Android 的 AES/DES/DESede 加密算法時鸟辅,不要使用默認的加密模式ECB,應(yīng)顯示指定使用 CBC 或 CFB 加密模式莺葫。 說明: 加密模式 ECB匪凉、CBC、CFB捺檬、OFB 等再层,其中 ECB 的安全性較弱,會使相同的銘文 在不同的時候產(chǎn)生相同的密文堡纬,容易遇到字典攻擊聂受,建議使用 CBC 或 CFB 模式。
ECB:Electronic codebook烤镐,電子密碼本模式
CBC:Cipher-block chaining蛋济,密碼分組鏈接模式
CFB:Cipher feedback,密文反饋模式
OFB:Output feedback炮叶,輸出反饋模式
19.
不要使用 loopback 來通信敏感信息碗旅。
20.
對于不需要使用 File 協(xié)議的應(yīng)用,禁用 File 協(xié)議镜悉,顯式設(shè)置 webView.getSettings().setAllowFileAccess(false)祟辟,對于需要使用 File 協(xié)議的應(yīng)用,禁止 File 協(xié)議調(diào)用 JavaScript侣肄,顯式設(shè)置 webView.getSettings().setJavaScriptEnabled(false)旧困。
21.
Android APP 在 HTTPS 通信中,驗證策略需要改成嚴格模式稼锅。說明:Android APP 在 HTTPS 通信中叮喳,使用 ALLOW_ALL_HOSTNAME_VERIFIER,表示允許和 所有的 HOST 建立 SSL 通信缰贝,這會存在中間人攻擊的風(fēng)險馍悟,最終導(dǎo)致敏感信息可能會被劫持,以及其他形式的攻擊剩晴。
22.
Android5.0 以后安全性要求 較高的應(yīng)用 應(yīng)該使 用 window.setFlag (LayoutParam.FLAG_SECURE) 禁止錄屏锣咒。
23.
zip 中不建議允許../../file 這樣的路徑侵状,可能被篡改目錄結(jié)構(gòu),造成攻擊毅整。 說明:當 zip 壓縮包中允許存在"../"的字符串趣兄,攻擊者可以利用多個"../"在解壓時改變 zip 文件存放的位置,當文件已經(jīng)存在是就會進行覆蓋悼嫉,如果覆蓋掉的文件是 so艇潭、dex 或者 odex 文件,就有可能造成嚴重的安全問題戏蔑。
24.
開放的 activity/service/receiver 等需要對傳入的 intent 做合法性校驗蹋凝。
25.
加密算法:使用不安全的 Hash 算法(MD5/SHA-1)加密信息,存在被破解的風(fēng)險总棵,建議使用 SHA-256 等安全性更高的 Hash 算法鳍寂。
26.
Android WebView 組件加載網(wǎng)頁發(fā)生證書認證錯誤時,采用默認的處理方法handler.cancel(),停止加載問題頁面情龄。 說明: Android WebView 組件加載網(wǎng)頁發(fā)生證書認證錯誤時迄汛,會調(diào)用 WebViewClient 類的 onReceivedSslError 方法,如果該方法實現(xiàn)調(diào)用了 handler.proceed()來忽略該證書 錯誤骤视,則會受到中間人攻擊的威脅鞍爱,可能導(dǎo)致隱私泄露。
27.
直接傳遞命令字或者間接處理有敏感信息或操作時专酗,避免使用 socket 實現(xiàn)睹逃,使用能夠控制權(quán)限校驗身份的方式通訊。
九笼裳、其他
不要通過 Msg 傳遞大的對象唯卖,會導(dǎo)致內(nèi)存問題。
不能使用 System.out.println 打印 log躬柬。
Log 的 tag 不能是" "拜轨。 說明: 日志的 tag 是空字符串沒有任何意義,也不利于過濾日志允青。
作者:歡子
鏈接:https://juejin.im/post/5ca42bac51882543f96dc4b7
來源:掘金
著作權(quán)歸作者所有橄碾。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處颠锉。