Android应用内社区SDK技术架构浅析
应用内微社区是什么 ?
图1 | 图2 雪球财经应用内社区 |
一、以友盟微社区为例
简单来说,友盟的微社区就是一款帮助开发者在应用中快速搭建一个社区( 类似于新浪微博、朋友圈 )的产品。在很多应用中,开发商往往都会需要一个用户之间以及用户与开发商之间互动的社区,用户往往又会在社区里选择赞、评论、关注、转发、发帖等各种互动方式。但是开发一个社交系统可不是那么容易的一件事,复杂的用户关系、消息流、服务器架构等等都是棘手的问题,更重要的是大家都是重复地劳动!每个开发商需要这些功能的时候都需要从头开始构建这么一个开发成本很高的社交系统。友盟为了解决这些问题推出了友盟微社区SDK (内测阶段) 这款产品,它可以让你以SDK集成的方式快速的构建一个社区,大大提高了生产力和用户活跃度。
1.1 友盟微社区的特点
- UI开源;
- 可作为SDK嵌入已有应用 ;
- 可一键生成独立APP;
- 账户系统、图片加载等核心组件可定制;
- 先进的anti-spam,自动肃清垃圾信息;
如图3、图4是云图TV的集成效果。
图3 | 图4 |
二、技术架构
从项目结构上来说,友盟微社区SDK可以简单分为如下三层:
UI层 (开源)
UI层对外开放,目的是让用户能够定制友盟微社区的UI效果,使微社区SDK能够很自然的融入到用户的App中。业务逻辑层
业务逻辑层会通过一个统一的接口向UI层提供数据数据请求等功能,比如获取缓存的feed、好友列表等,因此业务逻辑层对于用户来说是一个数据操作接口,通过这个接口用户能够与SDK 核心层进行一些数据方面的操作。核心层
核心层则包含了友盟SDK的核心系统抽象,比如账户系统、推送、数据库、网络操作等,这一层对外封闭,用户可以通过一些接口与核心层进行交互。而核心层定义的抽象使得用户可以很方便的实现定制化,即自己实现抽象接口,然后将具体的实现注入到友盟微社区中,从而使自己的子系统替换掉微社区中的默认实现 (下文会给出示例 )。
如图2-1所示,友盟微社区SDK层次分明,通过这三个层次的隔离,使得用户既可以自定义最外层的UI效果,也对外隐藏了业务逻辑层、核心层的实现细节。而核心层定义的子系统抽象,使得用户可以注入自己的实现,保证了整个微社区SDK的灵活性、扩展性。
图 2-1 洋葱结构图
简单来说,就是用户在UI层通过逻辑层暴露的通用接口来操作友盟微社区SDK,从封闭的核心系统中获取、存储数据以及其他的相关操作。层次结构如图 2-2所示。
图 2-2 层次结构图
图2-1、2-2都显示了友盟微社区SDK是通过不同的层次来分离职责,是一个较为典型的架构形式。对于用户来说,最关心的莫过于可定制化。UI层开放源码,自然可以通过修改代码来实现。其他的定制化用户就需要依赖注入来实现。友盟微社区SDK内部依赖于抽象,而不依赖于具体实现,并且用户可以注入具体实现。也就是说用户可以根据我们的抽象接口实现自己的子系统,然后注入到SDK内部,SDK此时就会使用用户注入的实现,这样就达到了子系统替换的效果,也就是我们说的定制化。
三、友盟微社区的定制化
3.1 如何满足定制化 ?
那么如何来实现定制化呢? 友盟微社区SDK内部定义了一些抽象,比如Loginable、Pushable、ImageLoader来分别代表登录系统接口、推送接口、图片加载接口,每种接口都有一个SDKManager来进行管理。比如管理登录子系统的就是LoginSDKManager,用户可以往这个Manager里面添加、移除具体的登录系统实现,然后通过useThis函数来指定使用某个具体的实现(SDK Manager里面可能有多个实现 )。结构图如图 3-1所示 。
图 3-1
SDK Manager是一个泛型类,类型T就代表了接口类型,比如上述的Loginable等。通过泛型我们就可以将这些通用的添加、移除实现等操作抽象化,避免重复代码。代码如下所示:
public abstract class SDKManager<T> {
// 泛型Map
private Map<String, T> mImplMap = new HashMap<String, T>();
// 要使用的实现的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);
}
public void addAndUse(T impl) {
if (impl == null) {
return;
}
mCurrentKey = impl.getClass().getSimpleName();
mImplMap.put(mCurrentKey, impl);
}
}
代码很简单,就是在SDK Manager内部维护了一个Map,key是用户为这个实现指定的一个字符串值,value就是具体的实现。用户可以通过这个key来移除实现,更常用的是我们需要调用useThis(String key)接口来指定使用某个具体的实现。
我们并没有直接使用SDKManager,因为它是一个抽象泛型类,因此我们定义了一些子类来对不同的实现进行管理,这些子类都是单例类,例如LoginSDKManager,代码如下所示。
public final class LoginSDKManager extends SDKManager<Loginable> {
// 单例对象
static LoginSDKManager sInstance = new LoginSDKManager();
private LoginSDKManager() {
}
// 获取单例对象
public static LoginSDKManager getInstance() {
return sInstance;
}
}
在用户需要对登录系统进行管理时,通过LoginSDKManager.getInstance()
就可以获取到负责管理登录系统的SDK Manager,此时用户可以通过addImpl(String key, T impl)
、useThis(String key)
等接口对登录系统进行管理,这就就可以灵活使用用户自定义的子系统。
3.2 示例
下面还是以一个示例来说明问题吧。
在与用户沟通的过程中,我们发现登录模块是用户自定义概率最高的子系统。通常情况下,用户可能有自己的账户系统或者使用了第三方登录( 比如友盟社会化组件 ),此时用户就不需要友盟微社区SDK中附带的登录实现,完全依赖自己的账户系统或者其他第三方登录SDK来实现一个登录系统。下面我们就以实现登录系统 ( 其他子系统的自定义原理一样 ) 来演示自定义过程。
在开始之前,我们需要对登录的抽象接口Loginable进行了解。代码如下所示:
public interface Loginable {
public void login();
public void logout();
public boolean isLogined();
}
- login() : 登录函数,用户需要在登录成功后将用户信息回调给友盟微社区SDK (具体过程可以参考友盟微社区集成文档);
- logout() : 登出函数,注销用户的登录即可;
- isLogined() : 用户是否登录,返回true表示已登录,否则为未登录。
微社区SDK内部通过抽象了几个简单接口来定义登录模块的功能,用户通过实现这几个函数即可定制自己的登录系统,最后将实现诸如到友盟微社区SDK即可。例如,如果你的应用中已经有了自己的账户系统逻辑,你可以在Loginable的几个函数中通过调用你的账户系统逻辑实现这几个功能;如果你使用了友盟社会化组件 ( 注意 : 虽然友盟微社区SDK的登录模块使用的是友盟社会化组件,但它们的jar文件并不是一致的,因此不管你使用其中哪一个实现,都需要更新所有与登录相关的jar文件。 ),那么你可以通过友盟社会化组件的登录、登出功能实现对应的功能,例如你可以在login()
函数中调用UMSocialService
对象的doOauthVerify(Context context , SHARE_MEDIA platform , UMAuthListener listener)
接口来实现登录。一句话概括就是 : 自定义一个实现了Loginable接口的类,在这个类的各个函数中调用你原有的登录、登出、判断是否已登录的函数来实现对应的功能。实现了登录类之后,通过LoginSDKManager的addImpl(String key, Loginable impl)
来将该实现注入到友盟微社区SDK中,最后通过LoginSDKManager的useThis(String key)
函数来指定要使用的登录实现,这个key就是addImpl(String key, Loginable impl)
中设置的key。
自定义登录类示例代码如下 :
/**
* 友盟社会化组件的登录实现,这里可以替换成自己的账户系统、第三方登录等,实现几个接口函数即可。
*/
public class SocialLoginImpl implements Loginable {
@Override
public void login() {
// 登录的具体实现,可以调用你自己的登录代码或者第三方SDK的登录函数
}
@Override
public void logout() {
// 登出的具体实现,可以调用你自己的登录代码或者第三方SDK的登录函数
}
@Override
public boolean isLogined() {
// 检测是否登录
return true /* 代码省略 */;
}
}
注入登录实现 :
// 登录系统管理器
LoginSDKManager loginMgr = LoginSDKManager.getInstance() ;
// key
String clzKey = SocialLoginImpl.class.getName() ;
// 注入实现
loginMgr.addImpl(clzKey, new SocialLoginImpl());
// 指定使用的具体实现
loginMgr.useThis(clzKey);
为了更简单,这个过程被我们封装到一个函数中,使用的代码最后简化为 :
// 一行代码搞定!这个函数封装了上述所有的代码。
LoginSDKManager.getInstance().addAndUse(new SocialLoginImpl()) ;
通过这几步,登录系统就注入到微社区SDK中了。当友盟微社区需要登录时,微社区SDK就会通过LoginSDKManager的getCurrentImpl()函数获取用户指定的登录实现,然后触发对应的login()
函数,此时就会执行你的登录流程。登录成功之后,通过login()
函数的回调listener ( 这个简单示例中没有给出该listener, 具体可参考 友盟微社区使用已有账户系统 ) 将用户信息传回给友盟SDK,就完成了整个登录过程。
杂谈
目前友盟微社区SDK还处在内测阶段 ( 友盟微社区内测申请地址,不过已经可以投入使用。已经有一部分集成了友盟微社区的App上线,并且运行良好。希望本文能有开发第三方Android SDK的同学一些帮助,让开发中的坑更少一些。