開發(fā)Android第三方SDK說難不難,但說簡單也不簡單,要開發(fā)一個給很多人使用的第三方SDK,如何在保證穩(wěn)定性的同時,增強(qiáng)SDK的靈活性,讓開發(fā)者能自由定制UI層、替換子系統(tǒng),這是一個值得思考的問題。為了解決這個問題,開發(fā)第三方SDK必須要有良好的應(yīng)用架構(gòu)。本文就分享一下我在開發(fā)友盟微社區(qū)SDK時在架構(gòu)方面的一些想法。
友盟的微社區(qū)是一款幫助開發(fā)者在應(yīng)用中快速搭建一個社區(qū)(類似于新浪微博、朋友圈),目前正在內(nèi)測當(dāng)中。
技術(shù)架構(gòu)
從項目結(jié)構(gòu)上來說,友盟微社區(qū)SDK可以簡單分為如下三層。
UI層(開源)。UI層對外開放,目的是讓用戶能夠定制微社區(qū)的UI效果,使微社區(qū)SDK能夠很自然的融入到用戶的App中。
業(yè)務(wù)邏輯層。業(yè)務(wù)邏輯層會通過一個統(tǒng)一的接口向UI層提供數(shù)據(jù)請求等功能,比如獲取緩存的feed、好友列表等,因此業(yè)務(wù)邏輯層對于用戶來說是一個數(shù)據(jù)操作接口,通過這個接口用戶能夠與SDK核心層進(jìn)行一些數(shù)據(jù)方面的操作。
核心層。核心層則包含了友盟SDK的核心系統(tǒng)抽象,比如賬戶系統(tǒng)、推送、數(shù)據(jù)庫、網(wǎng)絡(luò)操作等,這一層對外封閉,用戶可以通過一些接口與核心層進(jìn)行交互。而核心層定義的抽象使得用戶可以很方便的實現(xiàn)定制化,即自己實現(xiàn)抽象接口,然后將具體的實現(xiàn)注入到微社區(qū)中,從而使自己的子系統(tǒng)替換掉微社區(qū)中的默認(rèn)實現(xiàn)。
如圖1所示,SDK層次非常分明,通過這三個層次的隔離,使得用戶既可以自定義外層的UI效果,也對外隱藏了業(yè)務(wù)邏輯層、核心層的實現(xiàn)細(xì)節(jié)。而核心層定義的子系統(tǒng)抽象,使得用戶可以注入自己的實現(xiàn),保證了整個微社區(qū)SDK的靈活性、擴(kuò)展性。
圖1 洋蔥結(jié)構(gòu)圖
簡單來說,就是用戶在UI層通過邏輯層暴露的通用接口來操作SDK,從封閉的核心系統(tǒng)中獲取、存儲數(shù)據(jù)以及其他的相關(guān)操作。層次結(jié)構(gòu)如圖2所示。
圖2 層次結(jié)構(gòu)圖
圖1、圖2都顯示了SDK是通過不同的層次來分離職責(zé),是一個較為典型的架構(gòu)形式。對于用戶來說,關(guān)心的莫過于可定制化。UI層開放源碼,自然可以通過修改代碼來實現(xiàn)。其他的定制化用戶就需要依賴注入來實現(xiàn)。微社區(qū)SDK內(nèi)部依賴于抽象,而不依賴于具體實現(xiàn),并且用戶可以注入具體實現(xiàn)。也就是說用戶可以根據(jù)我們的抽象接口實現(xiàn)自己的子系統(tǒng),然后注入到SDK內(nèi)部,SDK此時就會使用用戶注入的實現(xiàn),這樣就達(dá)到了子系統(tǒng)替換的效果,也就是我們說的定制化。
友盟微社區(qū)的定制化
如何滿足定制化?
那么如何來實現(xiàn)定制化呢?友盟微社區(qū)SDK內(nèi)部定義了一些抽象,比如Loginable、Pushable、ImageLoader來分別代表登錄系統(tǒng)接口、推送接口、圖片加載接口,每種接口都有一個SDKManager來進(jìn)行管理。比如管理登錄子系統(tǒng)的就是LoginSDKManager,用戶可以往這個Manager里面添加、移除具體的登錄系統(tǒng)實現(xiàn),然后通過useThis函數(shù)來指定使用某個具體的實現(xiàn)(SDK Manager里面可能有多個實現(xiàn))。結(jié)構(gòu)圖如圖3所示。
圖3 LoginSDKManager功能
SDK Manager是一個泛型類,類型T就代表了接口類型,比如上述的Loginable等。通過泛型我們就可以將這些通用的添加、移除實現(xiàn)等操作抽象化,避免重復(fù)代碼。代碼如下所示:
public abstract class SDKManager<T> {
// 泛型Map
private Map<String, T> mImplMap = new HashMap<String, T>();
// 要使用的實現(xiàn)的key
private String mCurrentKey = "";
public void addImpl(String key, T impl) {
mImplMap.put(mCurrentKey, impl);
}
public void removeImpl(String key) {
mImplMap.remove(mCurrentKey);
}
public void useThis(String key) {
mCurrentKey = key;
}
public T getCurrentImpl() {
return mImplMap.get(mCurrentKey);
}
}
代碼很簡單,就是在SDK Manager內(nèi)部維護(hù)了一個Map,key是用戶為這個實現(xiàn)指定的一個字符串值,value就是具體的實現(xiàn)。用戶可以通過這個key來移除實現(xiàn),更常用的是我們需要調(diào)用useThis(String key)接口來指定使用某個具體的實現(xiàn)。
我們并沒有直接使用SDKManager,因為它是一個抽象泛型類,因此我們定義了一些子類來對不同的實現(xiàn)進(jìn)行管理,這些子類都是單例類,例如LoginSDKManager,代碼如下所示。
public final class LoginSDKManager extends SDKManager<Loginable> {
// 單例對象
static LoginSDKManager sInstance = new LoginSDKManager();
private LoginSDKManager() {
}
// 獲取單例對象
public static LoginSDKManager getInstance() {
return sInstance;
}
}
在用戶需要對登錄系統(tǒng)進(jìn)行管理時,通過LoginSDKManager.getInstance()就可以獲取到負(fù)責(zé)管理登錄系統(tǒng)的SDK Manager,此時用戶可以通過addImpl(String key, T impl)、useThis(String key)等接口對登錄系統(tǒng)進(jìn)行管理,這就可以靈活使用用戶自定義的子系統(tǒng)。
示例
下面還是以一個示例來說明問題吧。在與用戶溝通的過程中,我們發(fā)現(xiàn)登錄模塊是用戶自定義概率高的子系統(tǒng)。通常情況下,用戶可能有自己的賬戶系統(tǒng)或者使用了第三方登錄,此時用戶就不需要友盟微社區(qū)SDK中附帶的登錄實現(xiàn),完全依賴自己的賬戶系統(tǒng)或者其他第三方登錄SDK來實現(xiàn)一個登錄系統(tǒng)。下面我們就以實現(xiàn)登錄系統(tǒng)(其他子系統(tǒng)的自定義原理一樣)來演示自定義過程。在開始之前,我們需要對登錄的抽象接口Loginable進(jìn)行了解。代碼如下所示:
public interface Loginable {
public void login();
public void logout();
public boolean isLogined();
}
login():登錄函數(shù),用戶需要在登錄成功后將用戶信息回調(diào)給友盟微社區(qū)SDK(具體過程可以參考友微社區(qū)集成文檔 http://dev.umeng.com/wsq/android/detail-integration#1);
logout():登出函數(shù),注銷用戶的登錄即可;
isLogined():用戶是否登錄,返回true表示已登錄,否則為未登錄。
微社區(qū)SDK內(nèi)部通過抽象了幾個簡單接口來定義登錄模塊的功能,用戶通過實現(xiàn)這幾個函數(shù)即可定制自己的登錄系統(tǒng),后將實現(xiàn)注入到SDK即可。例如,如果你的應(yīng)用中已經(jīng)有了自己的賬戶系統(tǒng)邏輯,你可以在Loginable的幾個函數(shù)中通過調(diào)用你的賬戶系統(tǒng)邏輯實現(xiàn)這幾個功能;如果你使用了友盟社會化組件那么你可以通過該社會化組件的登錄、登出功能實現(xiàn)對應(yīng)的功能,例如你可以在login()函數(shù)中調(diào)用UMSocialService對象的doOauthVerify(Context context、SHARE_MEDIA platform、UMAuthListener listener)接口來實現(xiàn)登錄。
一句話概括就是:自定義一個實現(xiàn)了Loginable接口的類,在這個類的各個函數(shù)中調(diào)用你原有的登錄、登出、判斷是否已登錄的函數(shù)來實現(xiàn)對應(yīng)的功能。實現(xiàn)了登錄類之后,通過LoginSDKManager的addImpl(String key, Loginable impl)來將該實現(xiàn)注入到SDK中,后通過LoginSDKManager的useThis(String key)函數(shù)來指定要使用的登錄實現(xiàn),這個key就是addImpl(String key、Loginable impl)中設(shè)置的key。
自定義登錄類示例代碼如下:
/**
* 友盟社會化組件的登錄實現(xiàn),這里可以替換成自己的賬戶系統(tǒng)、第三方登錄等,實現(xiàn)幾個接口函數(shù)即可。
*/
public class SocialLoginImpl implements Loginable {
@Override
public void login() {
// 登錄的具體實現(xiàn),可以調(diào)用你自己的登錄代碼或者第三方SDK的登錄函數(shù)
}
@Override
public void logout() {
// 登出的具體實現(xiàn),可以調(diào)用你自己的登錄代碼或者第三方SDK的登錄函數(shù)
}
@Override
public boolean isLogined() {
// 檢測是否登錄
return true /* 代碼省略 */;
}
}
**注入登錄實現(xiàn) :**
// 登錄系統(tǒng)管理器
LoginSDKManager loginMgr = LoginSDKManager.getInstance() ;
// key
String clzKey = SocialLoginImpl.class.getName() ;
// 注入實現(xiàn)
loginMgr.addImpl(clzKey, new SocialLoginImpl());
// 指定使用的具體實現(xiàn)
loginMgr.useThis(clzKey);
為了更簡單,這個過程被我們封裝到一個函數(shù)中,使用的代碼后簡化為 :
// 一行代碼搞定!這個函數(shù)封裝了上述所有的代碼。
LoginSDKManager.getInstance().addAndUse(new SocialLoginImpl()) ;
通過這幾步,登錄系統(tǒng)就被替換掉了。當(dāng)微社區(qū)需要登錄時,微社區(qū)SDK就會通過LoginSDKManager獲取當(dāng)前使用的登錄實現(xiàn),然后觸發(fā)login()函數(shù),此時就會執(zhí)行你的登錄代碼了。登錄成功之后,通過login()函數(shù)的回調(diào)listener(這個簡單示例中沒有給出該listener,具體可參考友盟微社區(qū)使用已有賬戶系統(tǒng))將用戶信息傳回給友盟SDK,就完成了整個登錄過程。
本站文章版權(quán)歸原作者及原出處所有 。內(nèi)容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負(fù)責(zé),本站只提供參考并不構(gòu)成任何投資及應(yīng)用建議。本站是一個個人學(xué)習(xí)交流的平臺,網(wǎng)站上部分文章為轉(zhuǎn)載,并不用于任何商業(yè)目的,我們已經(jīng)盡可能的對作者和來源進(jìn)行了通告,但是能力有限或疏忽,造成漏登,請及時聯(lián)系我們,我們將根據(jù)著作權(quán)人的要求,立即更正或者刪除有關(guān)內(nèi)容。本站擁有對此聲明的最終解釋權(quán)。