1. 程式人生 > >Android專案重構之路:實現篇

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沒有任何依賴,介面層依賴了模型層,核心層依賴了模型層和介面層,介面層依賴了核心層和模型層。
專案搭建的步驟如下:

  1. 建立新專案,專案名稱為KAndroid,包名為com.keegan.kandroid。預設已建立了app模組,檢視下app模組下的build.gradle,會看到第一行為:

    apply plugin: 'com.android.application'
    

    這行表明了app模組是application型別的。

  2. 分別新建模組model、api、core,Module Type都選為Android Library,在Add an activity to module頁面選擇Add No Activity,這三個模組做為庫使用,並不需要介面。建立完之後,檢視相應模組的build.gradle,會看到第一行為:

    apply plugin: 'com.android.library'
    
  3. 建立模組之間的依賴關係。有兩種方法可以設定:
    第一種:通過右鍵模組,然後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