一、重構,夜未眠
1.重新規劃Android項目結構
第一步,建立AndroidLib類庫,將與業務無關的邏輯轉移到AndroidLib。activity(無業務無關的Activity基類)、net(網絡底層封裝)、cache(緩存數據和圖片的相關處理)、ui(自定義控件)、utils(與業務無關的公用方法,比如對SharedPreferences的封裝)
第二步,將主項目中的類分門別類地進行劃分,放置在各種包中。activity(將不同模塊的Activity劃分到不同的包下)、adapter(所有適配器放在一起)、entity(所有的實體都放在一起)、db(SQLite相關邏輯的封裝)、engine(業務相關的類)、ui(自定義控件)、utils(公用方法)、interfaces(真正意義上的接口,命名以I作為開頭)、listener(基于Listener的接口,命名以On作為開頭)
2.為Activity定義新的生命周期
拆分成子方法
3.統一事件編程模型
點擊方法用匿名內部類實現
4.實體化編程
JSON用GSON等第三方類解析,使用fastJSON時混淆文件要添加:--keepattributes Signature,--keepattributes Annotation
實體生成器:EntityGenerater
頁面跳轉中使用實體時不建議使用全局變量,可能被回收然后App崩潰。可把實體實現Serializable然后傳送。
5.Adapter模板
所有Adapter都繼承自BaseAdapter,從構造函數注入List<>這樣的數據集合。
6.類型安全轉換函數
對于一個Object類型的對象,我們對其直接使用字符串操作函數toString,當其為null時就會崩潰,所以實現一個判斷是否為空的轉換函數。
如果長度不夠,那么招行substring函數時,就會崩潰。所以在使用時要判斷start和end兩個參數是否越界了。
二、Android網絡底層框架設計
1.網絡低層封裝
不推薦使用AsyncTask來封裝網絡底層,因為可擴展性不高。
現在一般使用RxJava+Retrofit
實例:使用原生的ThreadPoolExecutor+Runnable+Handler
2.App數據緩存設計
減少調用次數,然后把獲取的數據保存在App上。
3.MockService
當API沒做好時,自己模擬相關的的接口實現
4.用戶登錄
登錄成功后場景:點擊登錄按鈕,進入登錄頁面LoginActivity,登錄成功后,直接進入個人中心,這種情況一路執行startActivity()就能達到目的;在頁面A,想跳轉到頁面B,并攜帶一些參數,卻發現沒有登錄,于是先跳轉到登錄頁,登錄成功后,再跳轉到B頁面,同時仍然帶著那些參數,用setResult實現;在頁面A,執行某個操作,卻發現沒有登錄,于是跳轉到登錄面,登錄成功后再回到頁面A,繼續執行該操作,也是用setResult來完成回調。
自動登錄,不推薦本地保存用戶名和密碼,使用Cookie機制,也叫Token。登錄成功后,會從服務器獲取到一個Cookie,取出來放在本地文件中即可。當用戶注銷時,要清空本地的Cookie。
Cookie過期統一處理:加一個onCookieExpired回調方法。
防止黑客刷庫:登錄接口增加第三個參數,比如驗證碼;同一IP短時間內頻繁訪問時要求輸入驗證碼,用WebView實現。
5.HTTP頭中的奧妙
請求、時間校準、開啟gzip壓縮
三、Android經典場景設計
1.App圖片緩存設計
ImageLoader設計原理:在顯示圖片時,先在內存中查找;如果沒有,就去本地查找;如果還沒有,就開一個新線程去下載這張圖片,下載成功會把圖片同時緩存到內存和本地。對圖片是軟引用形式,所有內存中圖片會在內存不足的時候被系統回收。
Freso原理:設計了一個Image Pipeline的要信,它負責先后檢查內存、磁盤文件,如果都沒有就去從網絡下載圖片。三層緩存:Bitmap緩存、內存緩存、磁盤緩存。
2.對網絡流量進行優化
通信層面的優化:返回的數據存在于1KB時用gzip壓縮;數據傳遞遵守JSON協議,推薦ProtoBuffer協議;減少網絡訪問次數;使用TCP長連接;建立取消網絡請求機制,當一個頁面沒有請求完數據,跳轉到另一個頁面之前,把之前的網絡請求都取消;增加重試機制。
圖片策略優化:確保下載的每張圖,都符合ImageView控件的大小;低流量模式,降低圖片質量;極速模式,沒有圖片,只有文字。
3.城市列表的設計
4.App與HTML5的交互
用PhoneGap太重,把交互操作在底層封裝,然后給開發人員使用。
對于經常需要改動的頁面,做成H5頁面,在App中以WebView的形式加載。H5開發周期短,但慢。可以做兩套頁面,Native一套,H5一套,在App中設置一個變量,判斷該頁面用哪個。變量從后臺中獲取。
5.消滅全局變量
在內存不足時,系統會回收全局變量,會導致APP崩潰。解決這個問題,使用序列化技術。
對社交和電商類App而言,頁面繁多,沒必要保存頁面狀態,直接讓頁面重新執行一遍onCreate方法,丟失的數據讓用戶重新操作。使用MVVM設計模式,可以把ViewModel序列化到本地,用以保存頁面。
SharedPreferences中存放的變量數量應嚴格控制。
User是唯一例外的全局變量:序列化到本地,避免崩潰
四、Android命名規劃和編碼規范
1.Android命名規范
Activity、Adapter、Entity都為結尾,控件縮寫,常量命名等
2.Android編碼規范
分門另類存放各種類,Layout中使用的常量定義在Strings.xml中,Layout控件字體大小,定義在dimens.xml中,拆分onCreate,JSON解析用第三方庫,頁面傳值用Intent,控件事件用匿名內部類,Activity中不要嵌套內部類,實體不要在不同模塊間共享,節省內存,使用ArrayList而不是HashMap,圖片處理用第三方,盡量使用ApplicationContext代替Context,數據類型轉換一定要校驗,使用常量代替枚舉。
3.統一代碼格式
團隊中所有人使用同一種代碼格式
五、Crash異常收集與統計
1.異常收集
設計一個CrashHandler類,繼承自UncaughtExceptionHandler,處理未捕獲的異常。基本關鍵方法就handleException,做三件事:發錯誤日志到服務器,給用戶崩潰前的友好提示,把錯誤日志記錄到SD卡。
2.異常收集與統計
要么記錄到第三方平臺,要么記錄到自己的數據庫中然后處理
六、Crash異常分析
1.Java語法相關的異常
空指針NullPointException、角標越界IndexOutOfBoundsException/StringIndexOutOfBoundsException/ArrayIndexOutOfBoundsException、試圖調用一個空對象的方法、類型轉換異常、數字轉換錯誤,聲明數組長度為-1、遍歷集合同時刪除其中元素、比較器使用不當、當除數為0、不能隨便使用的asList、又有類找不到了(一):ClassNotFoundException、又有類找不到了(二):NoClassDefFoundError
2.Activity相關的異常
找不到Activity、不能實例化Activity、找不到Service、不能啟動BroadcastReceiver、startActivityForResult不能回傳、猴急的Fragment
3.序列化相關的異常
實體對象不支持序列化、序列化時未指定ClassLoader、反序列化時發現類找不到:被ProGuard混淆導致的崩潰、反序列化時發現類找不到:傳入畸形數據、反序列化時出錯
4.列表相關的異常
Adapter數據源變化但是沒通知ListView、ListView滾動時點擊刷新按鈕后崩潰、AbsListView的obtainView返回空指針、Adapter數據源變化但是沒調用notifyDataSetChanged
5.窗體相關的異常
窗口句柄泄露、View not attached to window manager、窗體在不恰當 的時候獲取了焦點、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、子線程不能修改UI、不能在子線程操作AlertDialog和Toast
6.資源相關的異常
Resources$NotFoundException、StackOverFlowError、UnsatisfiedLinkError、InfiateException之FileNotFoundException、InfiateException之缺少構造器、InfiateException之style與android:textStyle的區別、TransactionTooLargeException
7.系統碎片化相關的異常
NoSuchMethodError、RemoteViews、pointerIndex out of range、SecurityException之一:Intent中圖片太大、SecurityException之二:動態加載其他apk的activity、之三:No permission to modify thread、view的getDrawingCache()返回null、DeadObjectException、Android 2.1 不支持SSL、ViewFlipper引發的血案、ActivityNotFundException、Android 2.2 不支持xlargeScreens、Package manager has died、SpannableString與富文本字符串、Can not perform this action agter onSaveInstanceState、Service Intent must be explicit
8.SQLite相關的異常
No transaction is active、忘記關閉Cursor、數據庫被鎖定、試圖再打開已經關閉的對象、文件加密了或無數據庫、WebView中SQLLite緩存導致的崩潰、磁盤讀寫錯誤、android_metadate表不存在、android_metadata表中locale字段、數據庫或磁盤滿了
9.不明覺厲的異常
內存溢出、Verify Failed
10.其他情況的異常
TimeoutException、JSON解析異常、JSONArray在初始化時為空、第三方SDK拋出的Crash、兩個不同類型的View有相同的id、LayoutInflater.from().inflate()使用不當導致的崩潰、ViewGroup中的玄機、Monkey點擊過快導致的崩潰、圖片寬高為0、不能重復添加組件