Android專案重構之路:實現篇
轉載自[Keegan小鋼](http://keeganlee.me/)
前兩篇文章Android專案重構之路:架構篇和Android專案重構之路:介面篇已經講了我的專案開始搭建時的架構設計和介面設計,這篇就講講具體怎麼實現的,以實現最小化可用產品(MVP)的目標,用最簡單的方式來搭建架構和實現程式碼。
IDE採用Android Studio,Demo實現的功能為使用者註冊、登入和展示一個券列表,資料採用我們現有專案的測試資料,介面也是我們專案中的測試介面。
專案搭建
根據架構篇所講的,將專案分為了四個層級:模型層、介面層、核心層、介面層。四個層級之間的關係如下圖所示:
實現上,在Android Studio分為了相應的四個模組(Module):model、api、core、app。
model為模型層,api為介面層,core為核心層,app為介面層。
model、api、core這三個模組的型別為library,app模組的型別為application。
四個模組之間的依賴設定為:model沒有任何依賴,介面層依賴了模型層,核心層依賴了模型層和介面層,介面層依賴了核心層和模型層。
專案搭建的步驟如下:
-
建立新專案,專案名稱為KAndroid,包名為com.keegan.kandroid。預設已建立了app模組,檢視下app模組下的build.gradle,會看到第一行為:
apply plugin: 'com.android.application'
這行表明了app模組是application型別的。
-
分別新建模組model、api、core,Module Type都選為Android Library,在Add an activity to module頁面選擇Add No Activity,這三個模組做為庫使用,並不需要介面。建立完之後,檢視相應模組的build.gradle,會看到第一行為:
apply plugin: 'com.android.library'
-
建立模組之間的依賴關係。有兩種方法可以設定:
第一種:通過右鍵模組,然後Open Module Settings,選擇模組的Dependencies,點選左下方的加號,選擇Module dependency,最後選擇要依賴的模組,下圖為api模組添加了model依賴;第二種:直接在模組的build.gradle設定。開啟build.gradle,在最後的dependencies一項裡面新增新的一行:compile project(':ModuleName'),比如app模組新增對model模組和core模組依賴之後的dependencies如下:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:22.0.0' compile project(':model') compile project(':core') }
通過上面兩種方式的任意一種,建立了模組之間的依賴關係之後,每個模組的build.gradle的dependencies項的結果將會如下:
model:dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:22.0.0' }
api:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:22.0.0' compile project(':model') }
core:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:22.0.0' compile project(':model') compile project(':api') }
app:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:22.0.0' compile project(':model') compile project(':core') }
建立業務物件模型
業務物件模型統一存放於model模組,是對業務資料的封裝,大部分都是從介面傳過來的物件,因此,其屬性也與介面傳回的物件屬性相一致。在這個Demo裡,只有一個業務物件模型,封裝了券的基本資訊,以下是該實體類的程式碼:
/** * 券的業務模型類,封裝了券的基本資訊。 * 券分為了三種類型:現金券、抵扣券、折扣券。 * 現金券是擁有固定面值的券,有固定的售價; * 抵扣券是滿足一定金額後可以抵扣的券,比如滿100減10元; * 折扣券是可以打折的券。 * * @version 1.0 建立時間:15/6/21 */ public class CouponBO implements Serializable { private static final long serialVersionUID = -8022957276104379230L; private int id; // 券id private String name; // 券名稱 private String introduce; // 券簡介 private int modelType; // 券型別,1為現金券,2為抵扣券,3為折扣券 private double faceValue; // 現金券的面值 private double estimateAmount; // 現金券的售價 private double debitAmount; // 抵扣券的抵扣金額 private double discount; // 折扣券的折扣率(0-100) private double miniAmount; // 抵扣券和折扣券的最小使用金額 // TODO 所有屬性的getter和setter }
介面層的封裝
在這個Demo裡,提供了4個介面:一個傳送驗證碼的介面、一個註冊介面、一個登入介面、一個獲取券列表的介面。這4個介面具體如下:
-
傳送驗證碼介面
URL:http://uat.b.quancome.com/platform/api
引數:引數名 描述 型別 appKey ANDROID_KCOUPON String method service.sendSmsCode4Register String phoneNum 手機號碼 String 輸出樣例:
{ "event": "0", "msg": "success" }
-
註冊介面
URL:http://uat.b.quancome.com/platform/api
引數:引數名 描述 型別 appKey ANDROID_KCOUPON String method customer.registerByPhone String phoneNum 手機號碼 String code 驗證碼 String password MD5加密密碼 String 輸出樣例:
{ "event": "0", "msg": "success" }
-
登入介面
URL:http://uat.b.quancome.com/platform/api
其他引數:引數名 描述 型別 appKey ANDROID_KCOUPON String method customer.loginByApp String loginName 登入名(手機號) String password MD5加密密碼 String imei 手機imei串號 String loginOS 系統,android為1 int 輸出樣例:
{ "event": "0", "msg": "success" }
-
券列表
URL:http://uat.b.quancome.com/platform/api
其他引數:引數名 描述 型別 appKey ANDROID_KCOUPON String method issue.listNewCoupon String currentPage 當前頁數 int pageSize 每頁顯示數量 int 輸出樣例:
{ "event": "0", "msg": "success", "maxCount": 125, "maxPage": 7, "currentPage": 1, "pageSize": 20, "objList":[ {"id": 1, "name": "測試現金券", "modelType": 1, ...}, {...}, ... ]}
在架構篇已經講過,介面返回的json資料有三種固定結構:
{"event": "0", "msg": "success"} {"event": "0", "msg": "success", "obj":{...}} {"event": "0", "msg": "success", "objList":[{...}, {...}], "currentPage": 1, "pageSize": 20, "maxCount": 2, "maxPage": 1}
因此可以封裝成實體類,程式碼如下:
public class ApiResponse<T> { private String event; // 返回碼,0為成功 private String msg; // 返回資訊 private T obj; // 單個物件 private T objList; // 陣列物件 private int currentPage; // 當前頁數 private int pageSize; // 每頁顯示數量 private int maxCount; // 總條數 private int maxPage; // 總頁數 // 建構函式,初始化code和msg public ApiResponse(String event, String msg) { this.event = event; this.msg = msg; } // 判斷結果是否成功 public boolean isSuccess() { return event.equals("0"); } // TODO 所有屬性的getter和setter }
上面4個介面,URL和appKey都是一樣的,用來區別不同介面的則是method欄位,因此,URL和appKey可以統一定義,method則根據不同介面定義不同常量。而除去appKey和method,剩下的引數才是每個介面需要定義的引數。因此,對上面4個介面的定義如下:
public interface Api { // 傳送驗證碼 public final static String SEND_SMS_CODE = "service.sendSmsCode4Register"; // 註冊 public final static String REGISTER = "customer.registerByPhone"; // 登入 public final static String LOGIN = "customer.loginByApp"; // 券列表 public final static String LIST_COUPON = "issue.listNewCoupon"; /** * 傳送驗證碼 * * @param phoneNum 手機號碼 * @return 成功時返回:{ "event": "0", "msg":"success" } */ public ApiResponse<Void> sendSmsCode4Register(String phoneNum); /** * 註冊 * * @param phoneNum 手機號碼 * @param code 驗證碼 * @param password MD5加密的密碼 * @return 成功時返回:{ "event": "0", "msg":"success" } */ public ApiResponse<Void> registerByPhone(String phoneNum, String code, String password); /** * 登入 * * @param loginName 登入名(手機號) * @param password MD5加密的密碼 * @param imei 手機IMEI串號 * @param loginOS Android為1 * @return 成功時返回:{ "event": "0", "msg":"success" } */ public ApiResponse<Void> loginByApp(String loginName, String password, String imei, int loginOS); /** * 券列表 * * @param currentPage 當前頁數 * @param pageSize 每頁顯示數量 * @return 成功時返回:{ "event": "0", "msg":"success", "objList":[...] } */ public ApiResponse<List<CouponBO>> listNewCoupon(int currentPage, int pageSize); }
Api的實現類則是ApiImpl了,實現類需要封裝好請求資料並向伺服器發起請求,並將響應結果的資料轉為ApiResonse返回。而向伺服器傳送請求並將響應結果返回的處理則封裝到http引擎類去處理。另外,這裡引用了gson將json轉為物件。ApiImpl的實現程式碼如下:
public class ApiImpl implements Api {相關推薦
Android專案重構之路:實現篇
轉載自[Keegan小鋼](http://keeganlee.me/) 前兩篇文章Android專案重構之路:架構篇和Android專案重構之路:介面篇已經講了我的專案開始搭建時的架構設計和介面設計,這篇就講講具體怎麼實現的,以實現最小化可用產品(MVP)的
Android專案重構之路:實現篇 讀後思考
由於現在所在的公司比較小,android studio是沒有使用的,不過在看著那篇文章之後,對於其中的模組原理有了一個基礎的瞭解,原本對於網上分享的一些在android studio專案中介面特效的程式碼我是無法一下子理解的,現在,我理解了,一邊實踐一邊學習的確是一個很
Android開發學習之路--圖表實現(achartengine/MPAndroidChart)之初體驗
bundle 喜歡 嵌入式linux Y軸 tid ren sca ref java代碼 ??已經有一段時間沒有更新博客了,在上周離開工作了4年的公司,從此不再安安穩穩地工作了。很多其它的是接受挑戰和實現自身價值的提高。離開了嵌入式linux,從此擁抱移
重構之路第三篇——重新組織數據
chan direction hang rate state elf with bsp 類型 本篇目錄: 1 Self Encapsulate Field(自封裝字段) 2 Replace Data Value with Object(以對象取代數據值) 3 Change
重構之路第六篇——處理概括關系
取代 interface face down 函數 orm 塑造 tor 本體 1 Pull up Field(字段上移) 2 Pull up Method(函數上移) 3 Pull up Constructor Body(構造函數本體上移) 4 Push Down Met
Android MVP 實踐之路(理解篇)
一.簡單介紹下MVP 1.什麼是mvp? 簡稱:MVP 全稱:Model-View-Presenter ;MVP 是從經典的模式MVC演變而來,它們的基本思想有相通的地方:Controller/Presenter負責邏輯的處理,Model提供資料,View負責顯示。 2.mv
Salesforce學習之路-developer篇(三)利用Visualforce Page實現頁面的動態重新整理功能
Visualforce是一個Web開發框架,允許開發人員構建可以在Lightning平臺上本地託管的自定義使用者介面。其框架包含:前端的介面設計,使用的類似於HTML的標記語言;以及後端的控制器,使用類似於Java的Apex語言。 哪些版本支援Visualforce? 眾所周知,Salesforce分為多個版
Django之路 - 實現登錄隨機驗證碼
短信祝福 python 中間件 程序 檢測 登錄驗證碼是每個網站登錄時的基本標配,網上也有很多相應的文章, 但是從生成驗證碼到 應用到自己的網站上的全步驟,並沒有看到很多, 為了節約大家的時間,我把整體步驟寫下來, 即拿即用哈 1. 生成隨機驗證碼 隨機驗證碼代碼 2. 如何應用到你的dj
Android開發學習之路--異步消息Handler,Message,Looper和AsyncTask之初體驗
被調用 project 輸入 gettext npos article app sso 音樂播放 在簡易音樂播放器中。用了Handler。也沒有過多地去研究學習,這裏再學習下android下的異步消息處理機制。這裏用了Handler主要是在線程中不能更新UI
python之路-基礎篇2
python10、if else 流程判斷舉例說明1:import getpass #引用getpass這個模塊 _username = "kk" _password = "123456" username = input("username:") password = getpass.getpass("p
python之路-基礎篇4
python模塊模塊分兩種1、標準模塊(庫) 直接導入就可以使用2、第三方模塊(庫) 必須下載安裝才可以使用模塊又可以叫做庫初始兩個標準模塊:1、sys模塊例子:import sys #導入sys模塊 print (sys.path) 結果: [‘C:\\Users\\kk\\Documents
python之路-基礎篇3
python作業:1、每周寫一篇博客2、編寫登錄接口 輸入用戶名密碼 認證成功後顯示歡迎信息 輸錯三次後鎖定3、多級菜單 三級菜單 可依次選擇進入各子菜單 所需新知識點:列表、字典python之路-基礎篇3
菜鳥的Xamarin.Forms前行之路——實現按鈕的字體圖標(可擴展)
方法 所有 blank render 背景圖片 list cer 元素 ren 在實際的APP中,帶有圖標的按鈕用到地方還是蠻多的,字體圖標往往能更快更生動的傳達信息,並且相對於背景圖片,字體圖標也有著絕對的優勢,所以實現按鈕的字體圖標是值得嘗試的. 實現方法:各平臺自定義
python之路入門篇
邏輯判斷 入門 查看 就是 world 千萬 發布 數值計算 化運維 一、 Python介紹 python的創始人為吉多·範羅蘇姆(Guido van Rossum)。1989年的聖誕節期間,Guido開始寫能夠解釋Python語言語法的解釋器。Python這個
Android架構師之路 網絡層架構設計與實戰
安卓 第1章 課前須知介紹如何去學習,課程適合的人群、怎麽才能融會貫通第2章 主流網絡框架分析常用網絡框架介紹,分析volley 、Android-async-http、Afinal框架、xUtils、okhttp、retrofit、優有點缺點、教會選擇一個框架的標準是什麽?第3章 http協議詳解介紹htt
Python人工智能之路 - 第一篇 : 你得會點兒Python基礎
序列 但是 入參 而不是 username 定義函數 json序列化 並且 color Python 號稱是最接近人工智能的語言,因為它的動態便捷性和靈活的三方擴展,成就了它在人工智能領域的豐碑 走進Python,靠近人工智能 一.編程語言Python的基礎 之 "淺入淺出
python學習之路 實現簡單的計算機功能。
內部 表達式 remove 符號 加減 per multipl 計算機 print 計算器的主要思維是: 1、對輸入的表達式去除其空格 2、判斷用戶輸入的表達式中的括號是否合法 3、每次找到這個表達式只有一對括號的表達式(就是這找到的表達式內部沒有括號) 4、對找到的只有一
Python學習之路基礎篇--09Python基礎,初識函數
是什麽 上一個 def 不常用 *args none 它的 動態參數 ... 函數可以分為內置函數 和 自定義函數。這次關註的主要是自定義函數。定義函數之後,就可以在任何需要它的地方調用。 1 返回值的重要性 返回值的3種情況 沒有返回值 ---- 返回Non
Python學習之路 拓展篇
包含 鏈路 寫上 機器 就是 mac地址 方式 中心 無法定位 1.數據概述: 2.機器數與真值: 3.原碼,反碼及補碼: 總結:正數的反碼,補碼都是其本身。負數的反碼是在
python之路 第二篇 數據類型詳解及其方法
字符 引號 print 成員 移除 join att pri str 字符串 #作用:描述名字,性別,國籍,地址等信息#定義:在單引號\雙引號\三引號內,由一串字符組成 name=‘Matthew‘ #優先掌握的操作: #1、按索引取值(正向取+反向取) :只能取 #2