你是否厭倦了這種寫法?
when{
isXiaomi()->xxxx
isVivo()->xxxxx
is.......
}
亦或是這樣的代碼?
if(isXiaomi){
xxxxx
}else if(isVivo){
xxxxxxx
}else if....
那么今天,我將帶你實(shí)現(xiàn)不一樣的渠道、rom差異。
廢話不多,先看效果~
我們這里隨便舉個例子,不同平臺打印不同的log,可以看到,在華為手機(jī)上打印出了current is Huawei,而在三星手機(jī)上打印出了Samsung,創(chuàng)建簡單易懂,無需處理繁雜的條件判斷~
//自動創(chuàng)建代理類
private val logProxy by lazy {
getPlatformProxy<ILogPlatformAction>()
}
fun setup(){
logProxy.log()
}
打印結(jié)果:
華為-> current is Huawei
三星-> current is Samsung
那么接下來,我們進(jìn)入編碼教程環(huán)節(jié)吧~
前幾天我們在# 手把手教你搭建android模塊化項(xiàng)目框架(九)小試牛刀——優(yōu)雅的登錄方案中簡單介紹過SPI,今天我拋磚引玉,繼續(xù)使用SPI、autoservice實(shí)現(xiàn)差異化代理這里我以不同rom為例,渠道區(qū)分同理,一看就懂~
首先我們在core_tool模塊中創(chuàng)建統(tǒng)一平臺區(qū)分接口~
所有功能實(shí)現(xiàn)要基于此接口
interface IPlatformAction
然后創(chuàng)建功能區(qū)分接口,這里我們以打印log為例
interface ILogPlatformAction : IPlatformAction {
fun log()
}
之后創(chuàng)建實(shí)現(xiàn)類,這里我們只區(qū)分華為、三星手機(jī),筆者手里就只有這兩個品牌的手機(jī)
增強(qiáng)健壯性,我們創(chuàng)建一個默認(rèn)的實(shí)現(xiàn)類,避免某些型號沒有實(shí)現(xiàn)類時(shí)出現(xiàn)問題
@AutoService(ILogPlatformAction::class)
open class DefaultLogAction : ILogPlatformAction {
override fun log() {
Log.v("ssssss", "current is Default")
}
}
我們的平臺實(shí)現(xiàn)類基于default實(shí)現(xiàn)即可,例如我們接口功能中有10個方法,只有兩個平臺需要區(qū)分時(shí),可以簡化很多代碼
@AutoService(ILogPlatformAction::class)
class SamsungLogAction : DefaultLogAction() {
override fun log() {
Log.v("ssssss", "current is Samsung")
}
}
@AutoService(ILogPlatformAction::class)
class HuaweiLogAction : DefaultLogAction() {
override fun log() {
Log.v("ssssss", "current is Huawei")
}
}
然后我們怎么區(qū)分各個平臺差異呢?
我們知道,SPI代理創(chuàng)建對象是根據(jù)接口查找實(shí)現(xiàn)類,這里我們?yōu)榱撕喕褂茫瑢懸粋€擴(kuò)展方法協(xié)助查詢實(shí)現(xiàn)類即可~
這里我們偷下懶,直接使用類名字做區(qū)分,例如華為的實(shí)現(xiàn)類我們一定帶上huawei,三星的同理,但是要注意,如此寫法一定要確保我們的實(shí)現(xiàn)類名稱不被混淆!!
如果不想使用類名區(qū)分或者團(tuán)隊(duì)人員經(jīng)常變動的情況下,這里我推薦在IPlatformAction類中添加platName,并且在每個rom的實(shí)現(xiàn)類中寫入名稱,以便ServiceLoader獲取實(shí)現(xiàn)類時(shí)判斷使用
代碼如下
inline fun <reified T> getPlatformProxy(): T {
val implList = ServiceLoader.load(T::class.java).toList()
return runCatching {
implList.find {
這里的RuntimeUtil.platName可以自己獲取一下,判斷rom的代碼還是要有的,不過僅僅使用一次即可,下面我會給出參考代碼
it?.getSimpleNameLowerCase()?.contains(RuntimeUtil.platName) == true
如果使用platName方式
//it?.platName == RuntimeUtil.platName
} as T
}.getOrElse {
implList.find {
如果沒有找到實(shí)現(xiàn)類,
it?.getSimpleNameLowerCase()?.contains(RuntimeUtil.PLATFORM_DEFAULT) == true
如果使用platName方式
//it?.platName == RuntimeUtil.PLATFORM_DEFAULT
} as T
}
}
然后是RuntimeUtil的參考代碼
這個參考代碼,包括git上的,一定不要拿來直接用,我都是亂寫的判斷條件,不一定準(zhǔn)確判斷各個rom的差異
object RuntimeUtil {
private const val PLATFORM_XIAOMI = "xiaomi"
private const val PLATFORM_HUAWEI = "huawei"
private const val PLATFORM_VIVO = "vivo"
private const val PLATFORM_OPPO = "oppo"
private const val PLATFORM_SAMSUNG = "samsung"
const val PLATFORM_DEFAULT = "default"
private val manufacturer by lazy { Build.MANUFACTURER.lowercase() }
val platName by lazy {
when {
isMIUI() -> PLATFORM_XIAOMI
isSamsung() -> PLATFORM_SAMSUNG
isVivo() -> PLATFORM_VIVO
isOppo() -> PLATFORM_OPPO
isHuawei() -> PLATFORM_HUAWEI
else -> PLATFORM_DEFAULT
}
}
}
如此我們便達(dá)到了文章開頭的使用方式
下面我們總結(jié)一下實(shí)現(xiàn)方式
- 創(chuàng)建IPlatformAction接口,可以處理統(tǒng)一事務(wù)或處理混淆不想使用類名時(shí)添加platformname字段區(qū)分查詢實(shí)現(xiàn)類
- 創(chuàng)建各個差異功能的接口,繼承至IPlatformAction接口,例如ILogPlatformAction
- 創(chuàng)建各個rom差異化實(shí)現(xiàn)類,繼承ILogPlatformAction
- 創(chuàng)建getPlatformProxy()擴(kuò)展方法,達(dá)到自動創(chuàng)建動態(tài)代理的效果
完成以上4步,即可達(dá)到文章開頭的效果啦~