Categories
程式開發

Android模塊化:面向接口編程


一、概述

隨著業務的發展,工程的逐漸增大與開發人員增多,很多工程都走向了模塊化、組件化、插件化道路,來方便大家的合作開發與降低業務之間的耦合度。現在就和大家談談模塊化的交互問題,首先看下模塊化的幾個優勢。

模塊化的優勢

  1. 結構清晰:業務獨立,代碼實現分離,不會攪在一起。
  2. 便於協作:每個開發同學只要自己負責的模塊,沒有太多的耦合。
  3. 便於維護:各模塊管理自己的代碼、佈局、資源,主工程可以方便添加與移除。

特點:高內聚、低耦合。

常見的模塊化實現方式有兩種

  1. 業務 Module 都放到同一個工程裡。
  2. 每個業務 Module 都是一個獨立的工程。

如圖:

Android模塊化:面向接口編程 1

Android模塊化:面向接口編程 2

模塊的劃分

模塊可分為多種類型,一般分為:三方的基礎SDK (網絡請求,地圖導航,推送等);自己平台的通用功能(網絡請求的能力封裝、圖片加載能力封裝、權限設置、UI組件等);業務模塊的拆分(登錄、交易、會員、硬件等)。

Android模塊化:面向接口編程 3

模塊間通信

雖然功能已經按模塊拆分,但是模塊間通信也是多種形勢,如果處理不好模塊之間耦合嚴重維護成本增大。常見模塊問通信有:直接依賴、事件或廣播通信、路由通信、面向接口通信,下面就對比下幾種通信優勢。

二、實現方案

直接依賴

Android模塊化:面向接口編程 4

這種方式實現簡單,但是耦合太嚴重,不方便維護與開發,當工程逐漸增大模塊逐漸增多,依賴關係會非常複雜,不推薦這種方式。

事件或廣播通信

EventBus:我們非常熟悉的事件總線型的通信框架,非常靈活,採用註解方式實現,但是難以追溯事件。

廣播:安卓的四大組件之一,在一個模塊中發送廣播設置數據,在另一個模塊中註冊廣播接收數據,使用廣播進行數據傳遞方式廣播相對於其他的方式而言消耗資源較多。

總結:BroadcastReceiver、EventBus,非常靈活,模塊之間沒有任何的耦合,但是代碼的可讀性差,難以追溯事件,不是很推薦。

路由通信

模塊與模塊之間不存在依賴關係,而是各自運作,簡單的來說就是映射關係的路由通信,也是目前比較主流的一種方案,比較常用的開源框架是阿里的ARouter。

ARouter典型應用

從外部URL映射到內部頁面,以及參數傳遞與解析跨模塊頁面跳轉,模塊間解耦攔截跳轉過程處理登陸、埋點等邏輯跨模塊API調用,通過控制反轉來做組件解耦。

面向接口通信

以上幾種方式只是簡單的介紹,下面就具體說下通過接口解耦通信的方式,首先先看幾個問題。

什麼是面向接口編程?

接口大家都很熟悉,這裡所說的面向接口編程,並不只是所謂的 java 中的 interface,而是指超類型,可以是接口也可以是抽像類。

面向接口比面向對象編程是更先進一步編程思想,而是附屬於面向對象編程的體系,屬於其中一部分,它是面向對象編程體系中的思想精髓之一。面向接口編程它的核心思想是將抽象與實現分離,從組件的級別來設計代碼,達到高內聚低耦合的目的。面向接口編程方法是,先定義底層接口模塊,也就是 通信的協議與功能約定,是提供方實現對應的功能與能力。在架構中層次分明,不需要關注具體實現,開發中可以通過接口快速制定協議,與提供能力api,對於上層通過接口顯露能力,對於下層只需要依賴接口層相當於依賴api。

面向接口編程的好處?

靈活性高沒有依賴具體的實體,實現層可以任意的更改與切換。在模塊化中可以相互依賴service(接口層)或依賴多個。

‍在模塊化中的使用下面對於接口(interface)或api層統稱為service,其含義為服務提供者。

Android模塊化:面向接口編程 5

對於,每一個 module 都一個獨立的工程結構,每個 module 都有自己的 Service ,來統一暴露當前 module 所擁有能力與向外提供的服務。

Android模塊化:面向接口編程 6

對於 module 是在同一個工程裡的項目結構,service 可以放到統一的一個 Module 下,我們統稱為 Mediator,這樣做的目的是為了減少 Module 創建與維護。假設你的工程有20個業務 Module 如果都同時增加一個 service 層就會造成 Module 數量翻一倍。由於這裡存入的都一些接口類,也是每個業務 Module 向外提供的服務其體量不會太大,這裡並只是一個建議並沒有標準的做法。

當然也有更複雜的設計,一個 Module 又分不同的 service 實現如圖,這裡不在展開細說。

Android模塊化:面向接口編程 7

實際工程中使用與設計

在實際項目中有很多項目都同時開發兩版本Pad與Phone,有的是兩獨立的工程,有的是在同一個工程內用 flavor 切換不同的工程,下面我就以通過 flavor 切換的工程結構舉例。

先看下工程的包的結構圖:

Android模塊化:面向接口編程 8

可以看到 module 結構是分為三個部分,common, pad, phone, 如果每個service 都獨立將增加3倍的 Module 數量。

Android模塊化:面向接口編程 9

使用一個 Mediator Module 統一管理這這些 service 就很好控制了 module 數量。

Service 創建

在 module_mediator 業務 module 下 common,pad、pone 下分別創建ICommonService, IService(pad), IService(phone)。 ICommonService:公共服務。 IService(pad):pad服務並繼承CommonService。 IService(phone):phone服務並繼承CommonService。

Android模塊化:面向接口編程 10

注:這里為什麼不用,PadService與PhoneService,是因為pad與phone版本同時只會存在一個,使用方只需要關心你提供的Service不用在區分版本,而且這裡是一個繼承關係也可以獲取到共用的部分。

Service 實現

依賴 Mediator :

Android模塊化:面向接口編程 11

在業務 commonpadphone module 下分別實現,ICommonService, Service(pad), IService(phone) ,在 common module 創建 CommonServiceImpl 實現 ICommonService,在 pad、phone module 分別創建 ServiceImpl 對應實現 IService 並繼承與 CommonServiceImpl。

Android模塊化:面向接口編程 12

Service 註冊

註冊的方式有一般是通過代碼用去註冊,或通過註解進行註冊。可以在 Application 註冊也可以在業務 Module 下自己註冊,如果使用註解則可以自動註冊,具體要看項目怎樣實現。例:

Android模塊化:面向接口編程 13

Android模塊化:面向接口編程 14

解釋下 MediatorServiceFacator,它只是一個服務工廠也是一個接口類,作用是負責管理各業務方的 Service 主要功能是註冊與獲取 Service。上面的代碼就是往裡註冊了一個會員的 Service。

可以看出這個函數只有兩個參數,一個是接口class一個是實現類class,第一個參數cls:它會作為 key 來使用,第二個參數implClass:它會作為 value 來使用。

Service 使用

通過 MediatorServiceFacator 懶加載獲取service對象,如果業務方沒有註冊則獲取一個空的對象。

註冊有 service 沒有使用時是不會創建的,如果使用過則會緩存下來,下次調用則直接返回。 (第一次是通過反射創建)例:

  1. 在 mediator 模塊下會員 CommonService 中 定義了一個模糊查詢會員的方法。

Android模塊化:面向接口編程 15

  1. 在會員模塊下 common 中實現了該功能。

Android模塊化:面向接口編程 16

  1. 在會員模塊下 pad 中繼承了這個實現。

Android模塊化:面向接口編程 17

  1. 在其他模塊 pad 下使用這個功能。

Android模塊化:面向接口編程 18

可以看到獲取 Service 只要傳對應接口就即可,對於使用方是不用關心實現方,在開發過程中只要先定義好接口,合作的同學就可以進入正常開發了。細心的同學可以看出,返回的數據類型也是一個接口類,為什麼不直接返回一個普通 java 類呢?主要原因是通過接口方法達到雙方 api 約定,例如 getName() :String 方法是通過方法名返回值達到約定效果,這樣不依賴具體實現。

從上面的例子可以看出主要分為三個部分:1、定義接口。 2、提供方實現接口。 3、使用方都通過服務工廠獲取服務使用。對於使用者來是很簡單的,不需要關心實現,通過接口可以直接獲取到實現,並且獲取到結果可以直接使用,不需要做序列化處理。

有了路由通信我們為什麼還使用面向接口編程?

路由模式雖然很好的解決了耦合的問題,但他的方法調用都是靜態的,對於傳參與返回值只能是基本類型,如果是對象需要做序列化與反序列化處理,對性能有一定影響。類似在調後台接口一樣,同時降低了代碼的可讀性, 對於 app 而言所有 Module 都是在同一個應用下,沒有必要做這些序列化操作。

對於復雜業務不好處理,例如一個業務需要多次通信,路由模式則不好處理,而通過接口通信則可以容易解決。例如:一個讀卡的操作,業務方需要對它有開啟、關閉、暫停等多個狀態的操作。通過接口則可以直接返回一個讀卡的 service 控制器, 這樣可以直接進行相應的控制操作。

Android模塊化:面向接口編程 19

從上面代碼中可以看出,上層回調結果的同時並回調了一個控制接口,這樣就提供使用方一個反向操作的能力。

三、總結

通過路由通信,可以很好的解決模塊間耦合,但拿不到對象無法持續交互,並且需要序列化,而通過接口通信,則很好地解決了這一點,並且代碼可讀性比較高。而接口通信會有一定的耦合性,也就是依賴了提供方的 service 層相當 api,但對於收益來說還是值得的方式。

在架構模式中,沒有最好的模式只有比較合適自己項目的模式,希望大家都可以活學活用,謝謝。

作者介紹

本文轉載自公眾號有贊coder(ID:youzan_coder)。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MzAxOTY5MDMxNA==&mid=2455760523&idx=1&sn=9216154849b70e900f48fd5013ecf422&chksm=8c6868aebb1fe1b8b7b25833f65c12206e27be127d37eabd143332f3c489ccbf9b3e4b632d2f&scene=27#wechat_redirect