一、重構(gòu)咪奖,夜未眠
1.重新規(guī)劃Android項(xiàng)目結(jié)構(gòu)
第一步辅鲸,建立AndroidLib類(lèi)庫(kù),將與業(yè)務(wù)無(wú)關(guān)的邏輯轉(zhuǎn)移到AndroidLib展箱。activity(無(wú)業(yè)務(wù)無(wú)關(guān)的Activity基類(lèi))旨枯、net(網(wǎng)絡(luò)底層封裝)、cache(緩存數(shù)據(jù)和圖片的相關(guān)處理)析藕、ui(自定義控件)召廷、utils(與業(yè)務(wù)無(wú)關(guān)的公用方法,比如對(duì)SharedPreferences的封裝)
第二步账胧,將主項(xiàng)目中的類(lèi)分門(mén)別類(lèi)地進(jìn)行劃分竞慢,放置在各種包中。activity(將不同模塊的Activity劃分到不同的包下)治泥、adapter(所有適配器放在一起)筹煮、entity(所有的實(shí)體都放在一起)、db(SQLite相關(guān)邏輯的封裝)居夹、engine(業(yè)務(wù)相關(guān)的類(lèi))败潦、ui(自定義控件)本冲、utils(公用方法)、interfaces(真正意義上的接口劫扒,命名以I作為開(kāi)頭)檬洞、listener(基于Listener的接口,命名以O(shè)n作為開(kāi)頭)
2.為Activity定義新的生命周期
拆分成子方法
3.統(tǒng)一事件編程模型
點(diǎn)擊方法用匿名內(nèi)部類(lèi)實(shí)現(xiàn)
4.實(shí)體化編程
JSON用GSON等第三方類(lèi)解析沟饥,使用fastJSON時(shí)混淆文件要添加:--keepattributes Signature,--keepattributes Annotation
實(shí)體生成器:EntityGenerater
頁(yè)面跳轉(zhuǎn)中使用實(shí)體時(shí)不建議使用全局變量添怔,可能被回收然后App崩潰∠涂酰可把實(shí)體實(shí)現(xiàn)Serializable然后傳送广料。
5.Adapter模板
所有Adapter都繼承自BaseAdapter,從構(gòu)造函數(shù)注入List<>這樣的數(shù)據(jù)集合幼驶。
6.類(lèi)型安全轉(zhuǎn)換函數(shù)
對(duì)于一個(gè)Object類(lèi)型的對(duì)象艾杏,我們對(duì)其直接使用字符串操作函數(shù)toString,當(dāng)其為null時(shí)就會(huì)崩潰盅藻,所以實(shí)現(xiàn)一個(gè)判斷是否為空的轉(zhuǎn)換函數(shù)购桑。
如果長(zhǎng)度不夠,那么招行substring函數(shù)時(shí)萧求,就會(huì)崩潰其兴。所以在使用時(shí)要判斷start和end兩個(gè)參數(shù)是否越界了。
二夸政、Android網(wǎng)絡(luò)底層框架設(shè)計(jì)
1.網(wǎng)絡(luò)低層封裝
不推薦使用AsyncTask來(lái)封裝網(wǎng)絡(luò)底層元旬,因?yàn)榭蓴U(kuò)展性不高。
現(xiàn)在一般使用RxJava+Retrofit
實(shí)例:使用原生的ThreadPoolExecutor+Runnable+Handler
2.App數(shù)據(jù)緩存設(shè)計(jì)
減少調(diào)用次數(shù)守问,然后把獲取的數(shù)據(jù)保存在A(yíng)pp上匀归。
3.MockService
當(dāng)API沒(méi)做好時(shí),自己模擬相關(guān)的的接口實(shí)現(xiàn)
4.用戶(hù)登錄
登錄成功后場(chǎng)景:點(diǎn)擊登錄按鈕耗帕,進(jìn)入登錄頁(yè)面LoginActivity穆端,登錄成功后,直接進(jìn)入個(gè)人中心仿便,這種情況一路執(zhí)行startActivity()就能達(dá)到目的体啰;在頁(yè)面A,想跳轉(zhuǎn)到頁(yè)面B嗽仪,并攜帶一些參數(shù)荒勇,卻發(fā)現(xiàn)沒(méi)有登錄,于是先跳轉(zhuǎn)到登錄頁(yè)闻坚,登錄成功后沽翔,再跳轉(zhuǎn)到B頁(yè)面,同時(shí)仍然帶著那些參數(shù),用setResult實(shí)現(xiàn)仅偎;在頁(yè)面A跨蟹,執(zhí)行某個(gè)操作,卻發(fā)現(xiàn)沒(méi)有登錄橘沥,于是跳轉(zhuǎn)到登錄面窗轩,登錄成功后再回到頁(yè)面A,繼續(xù)執(zhí)行該操作威恼,也是用setResult來(lái)完成回調(diào)品姓。
自動(dòng)登錄,不推薦本地保存用戶(hù)名和密碼箫措,使用Cookie機(jī)制,也叫Token衬潦。登錄成功后斤蔓,會(huì)從服務(wù)器獲取到一個(gè)Cookie,取出來(lái)放在本地文件中即可镀岛。當(dāng)用戶(hù)注銷(xiāo)時(shí)弦牡,要清空本地的Cookie。
Cookie過(guò)期統(tǒng)一處理:加一個(gè)onCookieExpired回調(diào)方法漂羊。
防止黑客刷庫(kù):登錄接口增加第三個(gè)參數(shù)驾锰,比如驗(yàn)證碼;同一IP短時(shí)間內(nèi)頻繁訪(fǎng)問(wèn)時(shí)要求輸入驗(yàn)證碼走越,用WebView實(shí)現(xiàn)椭豫。
5.HTTP頭中的奧妙
請(qǐng)求、時(shí)間校準(zhǔn)旨指、開(kāi)啟gzip壓縮
三赏酥、Android經(jīng)典場(chǎng)景設(shè)計(jì)
1.App圖片緩存設(shè)計(jì)
ImageLoader設(shè)計(jì)原理:在顯示圖片時(shí),先在內(nèi)存中查找谆构;如果沒(méi)有裸扶,就去本地查找;如果還沒(méi)有搬素,就開(kāi)一個(gè)新線(xiàn)程去下載這張圖片呵晨,下載成功會(huì)把圖片同時(shí)緩存到內(nèi)存和本地。對(duì)圖片是軟引用形式熬尺,所有內(nèi)存中圖片會(huì)在內(nèi)存不足的時(shí)候被系統(tǒng)回收摸屠。
Freso原理:設(shè)計(jì)了一個(gè)Image Pipeline的要信,它負(fù)責(zé)先后檢查內(nèi)存猪杭、磁盤(pán)文件餐塘,如果都沒(méi)有就去從網(wǎng)絡(luò)下載圖片。三層緩存:Bitmap緩存皂吮、內(nèi)存緩存戒傻、磁盤(pán)緩存税手。
2.對(duì)網(wǎng)絡(luò)流量進(jìn)行優(yōu)化
通信層面的優(yōu)化:返回的數(shù)據(jù)存在于1KB時(shí)用gzip壓縮;數(shù)據(jù)傳遞遵守JSON協(xié)議需纳,推薦ProtoBuffer協(xié)議芦倒;減少網(wǎng)絡(luò)訪(fǎng)問(wèn)次數(shù);使用TCP長(zhǎng)連接不翩;建立取消網(wǎng)絡(luò)請(qǐng)求機(jī)制兵扬,當(dāng)一個(gè)頁(yè)面沒(méi)有請(qǐng)求完數(shù)據(jù),跳轉(zhuǎn)到另一個(gè)頁(yè)面之前口蝠,把之前的網(wǎng)絡(luò)請(qǐng)求都取消器钟;增加重試機(jī)制。
圖片策略?xún)?yōu)化:確保下載的每張圖妙蔗,都符合ImageView控件的大邪涟浴;低流量模式眉反,降低圖片質(zhì)量昙啄;極速模式,沒(méi)有圖片寸五,只有文字梳凛。
3.城市列表的設(shè)計(jì)
4.App與HTML5的交互
用PhoneGap太重,把交互操作在底層封裝梳杏,然后給開(kāi)發(fā)人員使用韧拒。
對(duì)于經(jīng)常需要改動(dòng)的頁(yè)面,做成H5頁(yè)面秘狞,在A(yíng)pp中以WebView的形式加載叭莫。H5開(kāi)發(fā)周期短,但慢烁试」统酰可以做兩套頁(yè)面,Native一套减响,H5一套靖诗,在A(yíng)pp中設(shè)置一個(gè)變量,判斷該頁(yè)面用哪個(gè)支示。變量從后臺(tái)中獲取刊橘。
5.消滅全局變量
在內(nèi)存不足時(shí),系統(tǒng)會(huì)回收全局變量颂鸿,會(huì)導(dǎo)致APP崩潰促绵。解決這個(gè)問(wèn)題,使用序列化技術(shù)。
對(duì)社交和電商類(lèi)App而言败晴,頁(yè)面繁多浓冒,沒(méi)必要保存頁(yè)面狀態(tài),直接讓頁(yè)面重新執(zhí)行一遍onCreate方法尖坤,丟失的數(shù)據(jù)讓用戶(hù)重新操作稳懒。使用MVVM設(shè)計(jì)模式,可以把ViewModel序列化到本地慢味,用以保存頁(yè)面场梆。
SharedPreferences中存放的變量數(shù)量應(yīng)嚴(yán)格控制。
User是唯一例外的全局變量:序列化到本地纯路,避免崩潰
四或油、Android命名規(guī)劃和編碼規(guī)范
1.Android命名規(guī)范
Activity、Adapter感昼、Entity都為結(jié)尾装哆,控件縮寫(xiě),常量命名等
2.Android編碼規(guī)范
分門(mén)另類(lèi)存放各種類(lèi)定嗓,Layout中使用的常量定義在Strings.xml中,Layout控件字體大小萍桌,定義在dimens.xml中宵溅,拆分onCreate,JSON解析用第三方庫(kù)上炎,頁(yè)面?zhèn)髦涤肐ntent恃逻,控件事件用匿名內(nèi)部類(lèi),Activity中不要嵌套內(nèi)部類(lèi)藕施,實(shí)體不要在不同模塊間共享寇损,節(jié)省內(nèi)存,使用ArrayList而不是HashMap裳食,圖片處理用第三方矛市,盡量使用ApplicationContext代替Context,數(shù)據(jù)類(lèi)型轉(zhuǎn)換一定要校驗(yàn)诲祸,使用常量代替枚舉浊吏。
3.統(tǒng)一代碼格式
團(tuán)隊(duì)中所有人使用同一種代碼格式
五、Crash異常收集與統(tǒng)計(jì)
1.異常收集
設(shè)計(jì)一個(gè)CrashHandler類(lèi)救氯,繼承自UncaughtExceptionHandler找田,處理未捕獲的異常∽藕基本關(guān)鍵方法就handleException墩衙,做三件事:發(fā)錯(cuò)誤日志到服務(wù)器,給用戶(hù)崩潰前的友好提示,把錯(cuò)誤日志記錄到SD卡漆改。
2.異常收集與統(tǒng)計(jì)
要么記錄到第三方平臺(tái)心铃,要么記錄到自己的數(shù)據(jù)庫(kù)中然后處理
六、Crash異常分析
1.Java語(yǔ)法相關(guān)的異常
空指針NullPointException籽懦、角標(biāo)越界IndexOutOfBoundsException/StringIndexOutOfBoundsException/ArrayIndexOutOfBoundsException于个、試圖調(diào)用一個(gè)空對(duì)象的方法、類(lèi)型轉(zhuǎn)換異常暮顺、數(shù)字轉(zhuǎn)換錯(cuò)誤厅篓,聲明數(shù)組長(zhǎng)度為-1、遍歷集合同時(shí)刪除其中元素捶码、比較器使用不當(dāng)羽氮、當(dāng)除數(shù)為0、不能隨便使用的asList惫恼、又有類(lèi)找不到了(一):ClassNotFoundException档押、又有類(lèi)找不到了(二):NoClassDefFoundError
2.Activity相關(guān)的異常
找不到Activity、不能實(shí)例化Activity祈纯、找不到Service令宿、不能啟動(dòng)BroadcastReceiver、startActivityForResult不能回傳腕窥、猴急的Fragment
3.序列化相關(guān)的異常
實(shí)體對(duì)象不支持序列化粒没、序列化時(shí)未指定ClassLoader、反序列化時(shí)發(fā)現(xiàn)類(lèi)找不到:被ProGuard混淆導(dǎo)致的崩潰簇爆、反序列化時(shí)發(fā)現(xiàn)類(lèi)找不到:傳入畸形數(shù)據(jù)癞松、反序列化時(shí)出錯(cuò)
4.列表相關(guān)的異常
Adapter數(shù)據(jù)源變化但是沒(méi)通知ListView、ListView滾動(dòng)時(shí)點(diǎn)擊刷新按鈕后崩潰入蛆、AbsListView的obtainView返回空指針响蓉、Adapter數(shù)據(jù)源變化但是沒(méi)調(diào)用notifyDataSetChanged
5.窗體相關(guān)的異常
窗口句柄泄露、View not attached to window manager哨毁、窗體在不恰當(dāng) 的時(shí)候獲取了焦點(diǎn)枫甲、token null is not for an application、permission denied for this window type挑庶、is your activity running言秸、添加窗體失敗、AlertDialog.resolveDialogTheme迎捺、The specified child already has a parent举畸、子線(xiàn)程不能修改UI、不能在子線(xiàn)程操作AlertDialog和Toast
6.資源相關(guān)的異常
Resources$NotFoundException凳枝、StackOverFlowError抄沮、UnsatisfiedLinkError跋核、InfiateException之FileNotFoundException、InfiateException之缺少構(gòu)造器叛买、InfiateException之style與android:textStyle的區(qū)別砂代、TransactionTooLargeException
7.系統(tǒng)碎片化相關(guān)的異常
NoSuchMethodError、RemoteViews率挣、pointerIndex out of range刻伊、SecurityException之一:Intent中圖片太大、SecurityException之二:動(dòng)態(tài)加載其他apk的activity椒功、之三:No permission to modify thread捶箱、view的getDrawingCache()返回null、DeadObjectException动漾、Android 2.1 不支持SSL丁屎、ViewFlipper引發(fā)的血案、ActivityNotFundException旱眯、Android 2.2 不支持xlargeScreens晨川、Package manager has died、SpannableString與富文本字符串删豺、Can not perform this action agter onSaveInstanceState共虑、Service Intent must be explicit
8.SQLite相關(guān)的異常
No transaction is active、忘記關(guān)閉Cursor呀页、數(shù)據(jù)庫(kù)被鎖定看蚜、試圖再打開(kāi)已經(jīng)關(guān)閉的對(duì)象、文件加密了或無(wú)數(shù)據(jù)庫(kù)赔桌、WebView中SQLLite緩存導(dǎo)致的崩潰、磁盤(pán)讀寫(xiě)錯(cuò)誤渴逻、android_metadate表不存在疾党、android_metadata表中l(wèi)ocale字段、數(shù)據(jù)庫(kù)或磁盤(pán)滿(mǎn)了
9.不明覺(jué)厲的異常
內(nèi)存溢出惨奕、Verify Failed
10.其他情況的異常
TimeoutException雪位、JSON解析異常、JSONArray在初始化時(shí)為空梨撞、第三方SDK拋出的Crash雹洗、兩個(gè)不同類(lèi)型的View有相同的id、LayoutInflater.from().inflate()使用不當(dāng)導(dǎo)致的崩潰卧波、ViewGroup中的玄機(jī)时肿、Monkey點(diǎn)擊過(guò)快導(dǎo)致的崩潰、圖片寬高為0港粱、不能重復(fù)添加組件