SDK的外观类
在使用一些SDK或者三方库都会有一个外观类比如Bmob,AVOSCloud,或者ImageLoader,Retrofit,EventBus等,他们的职责基本是全局配置,具体有:
- 配置全局的AppKey或AppId;
多见于SDK厂商,平台申请下发,用于唯一标识你的App,当然配置后SDK需要组好保存的操作比如加密保存防止泄露;
- 配置全局开关;
比如配置全局Debug日志开关,开启后SDK即把请求过程中的数据打到Logcat中,或者如LeankCanary设置Debug/Release模式;
其他全局开关,如PushSDK的startWork()方法,统计SDK设置上报域名或数据上报策略等;
- 其他可默认配置的信息;
相对于Appkey的必须配置,SDK中的其他配置信息可以是默认配置的,开发者需要的时候再单独配置,满足定制化的需求,比如Bmob的一些关于文件上传快大小的配置,虽然一般是在一个配置类(参数传到Builder内部类)中,不过也单独放在这里;
BmobConfig config =new BmobConfig.Builder(this) //设置appkey .setApplicationId("Your Application ID") //请求超时时间(单位为秒):默认15s .setConnectTimeout(30) //文件分片上传时每片的大小(单位字节),默认512*1024 .setUploadBlockSize(1024*1024) //文件的过期时间(单位为秒):默认1800s .setFileExpiration(2500) .build(); Bmob.initialize(config);
类似的例子在OkHttpClient中也异曲同工;
- 获取单例;
笔者在看到一个封装欠妥的SDK看到的,通过该类直接获取业务对象并调用业务方法,类似伪代码如下:
public class CloudCard{ // getInstance() 单例实现 // resetDomain(String newDomain) // applyCard(Card card) // init() // getQrCode() // clean() // update(Card card) // ... }
在笔者看来,SDK的外观类和业务对象应该抽离开,如果杂糅在一个类中,后续的业务方法新增会导致外观类频频修改,类的职责很不单一;同时,如果较为核心的业务方法较大程度暴露在改类中也较为危险,没有中间层不好隔离SDK内部修改保证对外的一致性和向下兼容;
- 封装SDK内部不同模块并提供统一的接口给开发者;
这个其实就是外观模式的基本实现目的,SDK里面有一些不同模块,甚至是一些较细粒度的Api,这些Api不该直接裸露给开发者调用,不管是出于开发者很多时候仅仅需要一个较粗粒度的业务功能定义(比如启动生成云码但具体的内部交互其实开发者不用或者不想干预)还是出于尽量降低开发者使用SDK的心智负担的考虑;当然,如果层次较高的开发者想调一些较细粒度的Api那么SDK可以提供一些Hook钩子方法供开发者调用,SDK很多时候需要封装Api并对外提供接口,中间做的转化需要结合具体业务而定;
- 获取全局Context;
上述几点可能很多用过厂商SDK开发者能很好理解,但是这一点可能你会突然觉得好像是,想起来了,但是不知具体功用,笔者的理解是:
- Context在Android中的设计定位在于上帝组件,有着上帝视角,具体说就是系统的获取资源,服务等都能通过该组件得到;
-
SDK获取全局Context的目的可能有:
方便调用Android其他组件,比如SDK内部也要用到Service,SharePerference做简单缓存等;
一般SDK传Context的时机是初始化,比如在Application中的onCreate()方法中:
Bmob.initialize(this, "Your Application ID");
一般建议在Application中而不是Activity的onCreate()方法中调用初始化,因为Application的生命周期和整个应用是同步的,非全局的Context容易导致使用过程中为空导致的SDK报错,这点在做技术支持和工单系统中就屡见不鲜;
有一个读者可能忽略的点,一些SDK中的异步回调方法都是帮开发者做好了线程切换(考虑到很多开发者拿到结果之后直接去更新UI和toast操作,至于为什么需要切换和子线程更新UI或toast等笔者不赘述),笔者可以验证一下;而之前看到一个知名SDK的实现是在SDK初始化时获取到主线程的Handler,后续用该Handler去做线程的切换,具体可以看下SDK回调方法在哪个线程;
简单实现
比如暂定为Cmob类,初步代码如下,较为简单:
public final class Cmob {
/**
* SDK初始化操作
* @param context 全局Context
* @param appKey 平台下发的appKey
*/
public static void initialize(Context context,String appKey){
// TODO:暂定操作
// 1. 报错Context
// 2. 加密保存appKey
}
/**
* 初始化BmobSDK,允许设置BmobConfig
*
* @param config 配置类包含:超时时间、上传文件的分块大小及文件的超时时间等
*/
public static void initialize(BmobConfig config) {
// TODO:
}
// 一些获取全局配置信息的方法,其他同理:
/**
* 获取当前的上传文件分块大小
*
* @return
*/
public static int getFileBlockSize() {
// TODO:
}
/**
* 数据迁移需要调用的重置域名方法
* @param newDomain 新域名
*/
public static void resetDomain(String newDomain){
// TODO: 对URLManager中域名等信息的变更
}
/**
* 获取当前配置的超时时间
*
* @return
*/
public static long getConnectTimeout() {
// TODO:
}
// 一些供内部调用的方法
/**
* 检查Context是否为空,并及早在开发阶段报错给开发者
*/
private static void checkContext() {
// TODO:
}
}
在下一篇中,我将讲解数据基类CmobObject的建模和设计;