手把手教你搭建android模块化项目框架(十三)——优雅的处理渠道与rom差异

你是否厌倦了这种写法?

kotlin 复制代码
when{
    isXiaomi()->xxxx
    isVivo()->xxxxx
    is.......
}

亦或是这样的代码?

kotlin 复制代码
    if(isXiaomi){
        xxxxx
    }else if(isVivo){
        xxxxxxx
    }else if....

那么今天,我将带你实现不一样的渠道、rom差异。

废话不多,先看效果~

我们这里随便举个例子,不同平台打印不同的log,可以看到,在华为手机上打印出了current is Huawei,而在三星手机上打印出了Samsung,创建简单易懂,无需处理繁杂的条件判断~

kotlin 复制代码
//自动创建代理类
private val logProxy by lazy {
    getPlatformProxy<ILogPlatformAction>()
}

fun setup(){
    logProxy.log()
}

打印结果: 
华为-> current is Huawei
三星-> current is Samsung

那么接下来,我们进入编码教程环节吧~

前几天我们在# 手把手教你搭建android模块化项目框架(九)小试牛刀------优雅的登录方案中简单介绍过SPI,今天我抛砖引玉,继续使用SPI、autoservice实现差异化代理~这里我以不同rom为例,渠道区分同理,一看就懂~~

首先我们在core_tool模块中创建统一平台区分接口~

所有功能实现要基于此接口

kotlin 复制代码
interface IPlatformAction

然后创建功能区分接口,这里我们以打印log为例

kotlin 复制代码
interface ILogPlatformAction : IPlatformAction {
    fun log()
}

之后创建实现类,这里我们只区分华为、三星手机,笔者手里就只有这两个品牌的手机

kotlin 复制代码
增强健壮性,我们创建一个默认的实现类,避免某些型号没有实现类时出现问题
@AutoService(ILogPlatformAction::class)
open class DefaultLogAction : ILogPlatformAction {
    override fun log() {
        Log.v("ssssss", "current is Default")
    }
}

我们的平台实现类基于default实现即可,例如我们接口功能中有10个方法,只有两个平台需要区分时,可以简化很多代码
@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")
    }
}

然后我们怎么区分各个平台差异呢?

我们知道,SPI代理创建对象是根据接口查找实现类,这里我们为了简化使用,写一个扩展方法协助查询实现类即可~

这里我们偷下懒,直接使用类名字做区分,例如华为的实现类我们一定带上huawei,三星的同理,但是要注意,如此写法一定要确保我们的实现类名称不被混淆!!

如果不想使用类名区分或者团队人员经常变动的情况下,这里我推荐在IPlatformAction类中添加platName,并且在每个rom的实现类中写入名称,以便ServiceLoader获取实现类时判断使用

代码如下

kotlin 复制代码
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 {
            如果没有找到实现类,
            it?.getSimpleNameLowerCase()?.contains(RuntimeUtil.PLATFORM_DEFAULT) == true
            如果使用platName方式
            //it?.platName == RuntimeUtil.PLATFORM_DEFAULT
        } as T
    }
}

然后是RuntimeUtil的参考代码

kotlin 复制代码
这个参考代码,包括git上的,一定不要拿来直接用,我都是乱写的判断条件,不一定准确判断各个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
        }
    }
}

如此我们便达到了文章开头的使用方式

下面我们总结一下实现方式

  1. 创建IPlatformAction接口,可以处理统一事务或处理混淆不想使用类名时添加platformname字段区分查询实现类
  2. 创建各个差异功能的接口,继承至IPlatformAction接口,例如ILogPlatformAction
  3. 创建各个rom差异化实现类,继承ILogPlatformAction
  4. 创建getPlatformProxy()扩展方法,达到自动创建动态代理的效果

完成以上4步,即可达到文章开头的效果啦~

完整项目地址:传送门

相关推荐
AirDroid_cn5 分钟前
OPPO手机怎样被其他手机远程控制?两台OPPO手机如何相互远程控制?
android·windows·ios·智能手机·iphone·远程工作·远程控制
尊治6 分钟前
手机电工仿真软件更新了
android
秋千码途30 分钟前
小架构step系列07:查找日志配置文件
spring boot·后端·架构
Ashlee_code3 小时前
什么是Web3?金融解决方案
开发语言·金融·架构·eclipse·web3·区块链·php
xiangzhihong83 小时前
使用Universal Links与Android App Links实现网页无缝跳转至应用
android·ios
车载应用猿4 小时前
基于Android14的CarService 启动流程分析
android
没有了遇见4 小时前
Android 渐变色实现总结
android
WebInfra5 小时前
如何在程序中嵌入有大量字符串的 HashMap
算法·设计模式·架构
森焱森6 小时前
APM与ChibiOS系统
c语言·单片机·算法·架构·无人机
雨白7 小时前
Jetpack系列(四):精通WorkManager,让后台任务不再失控
android·android jetpack