在不久之前分享一篇《App 组件化/模块化之路——如何封装网络请求框架》文章介绍了我在项目中封装网络请求框架的思路。开发一个 App 会涉及到很多网络请求 API ,例如登录注册接口、用户信息接口、业务列表请求接口等等。而本文介绍的是如何模块化设计这些接口,使得项目中更好地复用代码。当然这仅仅是一家之言,欢迎留言拍砖。

问题

网络请求中最常见的莫过于用户授权登录模块了。现在以此模块为例,大概有以下接口

  • 登录 sign_in
  • 注册 sign_up
  • 找回密码 find_password
  • 获取短信验证码 getvalidatecode
  • 获取用户信息 user_info
  • 修改用户信息 edit_user
  • 绑定手机号 bind_phone

假设一个 App 中有这些接口,那么如何设计这些接口呢?按照我们之前设计的网络请求框架就是把每一个具体的 API (例如登录接口) 写一个 Request 类。

public class SignInRequest extends BaseTextRequest<SignInResult> {

    public SimpleTextRequest(Context context, Map<String, String> params) {
super(context);
addParams(params);
} @Override
public String getUrl() {
return "https://api.angrycode.net/signin";
} @Override
public HttpMethod getHttpMethod() {
return HttpMethod.POST;
} @Override
protected SignInResult onRequestFinish(String result) {
return SignInResult.parse(result);
} @Override
protected SignInResult onRequestError(int code, String message) {
return new SignInResult(code,message);
}
}

类似的注册接口对应一个 SignUpRequest 类,于是这样有多少个接口就又多少个 Request 类。

如果你的 App 业务比较复杂,那么 Request 类数目就会暴增,这时候如何组织管理这些 Request 类就是一个问题了。

接口模块化

思路其实也简单。不错,为了让你的接口更好的复用,我们把整个模块相关的接口进行整体设计。对外统一接口和回调方法。我们来看代码。

AuthContract

/**
* Created by wecodexyz@gmail.com on 2017/10/14 下午5:57.
* GitHub - https://github.com/wecodexyz
* Description:
*/ public interface AuthContract { interface Presenter {
/**
* 登录:/api/1.0/user/sign/in
*
* @param account 手机或邮箱
* @param password 登录密码
* @param type 类型:0-普通登录、1-邮箱登录、2-手机登录
*/
void signIn(String account, String password, @SignInType int type); /**
* 注册:/api/1.0/user/sign/up * @param nick_name 用户昵称 optional
* @param signature 用户签名 optional
*/
void signUp(String account, String password, @SignInType int type, String code, String nick_name, String signature);
/**
* 登出:/api/1.0/user/sign/out
*/
void signOut();
/**
* 修改密码:/api/1.0/user/password/update
*
* @param old_password 原密码
* @param new_password 新密码
*/
void updatePassword(String old_password, String new_password);
/**
* 手机绑定:/api/1.0/user/phone/bind
*/
void bindPhone(String phone, String code, String password);
/**
* 手机解绑:/api/1.0/user/phone/unbind
*/
void unbindPhone(String phone, String code);
/**
* 获取个人资料:/api/1.0/user/profile
*/
void profile(); } interface View {
/**
* 注册结果
*
* @param signInResult
*/
void onSignUpFinish(SignInResult signInResult);
/**
* 登录结果
*
* @param signInResult
*/
void onSignInFinish(SignInResult signInResult);
/**
* 手机绑定结果
*
* @param result
*/
void onBindPhoneFinish(APIResult result);
/**
* 获取个人资料
*
* @param result
*/
void onRequestProfileFinish(ProfileResult result);
/**
* 获取个人资料
*
* @param result 更新结果
*/
void onUpdateProfileFinish(APIResult result);
/**
* 出错回调
*
* @param code
* @param msg
*/
void onError(int code, String msg); void onFinish(); void onBegin(); } }

首先,根据 API 设计 Contract 接口,在这里定义接口请求方法和回调方法。例如我们这个登录模块,就可以定义一个 AuthContract 协议接口,在这个 Contract里面又管理着 PresenterView 接口,分别代表具体 API 请求方法和数据回调方法。其中在 View 接口中定义了几个通用的回调 onBegin, onFinish, onError,分别代表请求开始、结束、出错等几种状态,其它方法就是具体 API 返回的数据回调了。

这个 Contract 接口设计思路是源于googlesamples/android-architecture 。这样的好处我认为就是很好的管理这个模块中的众多的接口和回调方法,而维护者一看就一目了然,非常清晰。

然后,实现一个 Contract 接口中的 View 接口。其实是空实现。

AuthCallback

/**
* Created by wecodexyz@gmail.com on 2017/10/14 下午6:53.
* GitHub - https://github.com/wecodexyz
* Description: 授权登录以及用户相关接口回调类
*/ public class AuthCallback implements AuthContract.View {
@Override
public void onSignUpFinish(SignInResult signInResult) { } @Override
public void onSignInFinish(SignInResult signInResult) { } @Override
public void onSignOutFinish(APIResult result) { } @Override
public void onUpdatePasswordFinish(APIResult result) { } @Override
public void onBindPhoneFinish(APIResult result) { } @Override
public void onUnbindPhoneFinish(APIResult result) { } @Override
public void onRequestProfileFinish(ProfileResult result) { } @Override
public void onUpdateProfileFinish(APIResult result) { } @Override
public void onError(int code, String msg) { } @Override
public void onFinish(){ } @Override
public void onBegin(){ }
}

为什么要提供一个空实现的类呢?其实为了方便使用。想想你使用过的 WebViewChrome 的接口回调。

最后,我们实现 Contract 中的 Presenter 接口了。这个就是我们这个模块化接口的核心类了。

AuthManager

/**
* Created by wecodexyz@gmail.com on 2017/10/14 下午6:55.
* GitHub - https://github.com/wecodexyz
* Description:
*/ public class AuthManager implements AuthContract.Presenter { private Context mContext; private List<AuthCallback> mAuthCallbacks; private SignInResult mSignInResult; private AuthDBHelper mAuthDBHelper; private AuthManager() {
} private static class Holder {
private static final AuthManager INSTANCE = new AuthManager();
} public static AuthManager get() {
return Holder.INSTANCE;
} /**
* 在Application中进行初始化
*
* @param context application context
*/
public void init(Context context) {
mContext = context.getApplicationContext();
//获取本地登录信息
mAuthDBHelper = new AuthDBHelper(mContext); mSignInResult = mAuthDBHelper.loadSignInFromCache();
} /**
* 是否已登录授权
*
* @return
*/
public boolean isAuth() {
return mSignInResult != null && mSignInResult.isStatus();
}
public void registerCallback(AuthCallback authCallback) {
if (mAuthCallbacks == null) {
mAuthCallbacks = new ArrayList<>();
}
mAuthCallbacks.add(authCallback);
} public void unregisterCallback(AuthCallback authCallback) {
mAuthCallbacks.remove(authCallback);
} public void clearCallbacks() {
if (mAuthCallbacks == null) {
return;
}
mAuthCallbacks.clear();
}
@Override
public void signIn(String account, String password, @AuthContract.SignInType int type) {
HashMap<String, String> params = new HashMap<>();
if (!TextUtils.isEmpty(account)) {
params.put("account", account);
}
if (!TextUtils.isEmpty(password)) {
params.put("password", password);
}
params.put("type", String.valueOf(type)); SignInRequest request = new SignInRequest(mContext);
request.addParams(params);
request.request()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Consumer<Subscription>() {
@Override
public void accept(@NonNull Subscription subscription) throws Exception {
for (AuthCallback callback : mCallbacks) {
callback.onBegin();
}
}
})
.doFinally(new Action() {
@Override
public void run() throws Exception {
for (AuthCallback callback : mCallbacks) {
callback.onFinish();
}
}
})
.doAfterNext(new Consumer<SignInResult>() {
@Override
public void accept(@NonNull SignInResult signInResult) throws Exception {
mAuthDBHelper.cacheSignIn(signInResult);
}
})
.subscribe(new Consumer<SignInResult>() {
@Override
public void accept(@NonNull SignInResult signInResult) throws Exception {
if (signInResult.isStatus()) {
mSignInResult = signInResult;
UserInfo.fromSigninResult(mSignInResult);
}
for (AuthCallback callback : mAuthCallbacks) {
callback.onSignInFinish(signInResult);
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
LogUtils.e("sign in error -> " + throwable);
callbackError(110, "sign in error");
}
});
}
//接口太多这里只列举signup接口,其中接口类似... }

AuthManager 这个类设计单例模式。除了具体 API 实现接口还有以下几个方法

  • init 全局初始化方法。主要是为了保存 Application 上下文,因为接口请求会使用到。
  • registerCallback 注册回调。哪里使用,就哪里注册
  • unregisterCallback 取消注册回调。与上面方法对应使用,避免页面内存泄露
  • clearCallbacks 清除所有回调。

具体的 API 实现中,我这里就使用了之前网络框架中的代码 SignInRequest。

AuthManager 中还有一个 AuthDBHelper 类,这个是用户信息的缓存类。只要用户登录过了,那么下次就是直接取缓存中的登录信息就可以了。

整体结构

预览以下整体的结构

与用户相关的API都放在此模块中进行管理,而其它模块进行使用就很方便了。

首先,在Application中进行初始化

@Override
public void onCreate() {
super.onCreate();
AuthManager.get().init(this);
}

这个用法是不是与其它第三方 SDK 的使用类似呢?可以感受一下,其实这个也是之前提到的 SDK 设计思路。

然后在需要调用接口的页面中,如LoginFragment

AuthCallback mAuthCallback = new AuthCallback() {
@Override
public void onError(int code, String msg) {
//请求出错
} @Override
public void onBegin(){
//请求开始
} @Override
public void onFinish(){
//请求结束
} @Override
public void onSignInFinish(SignInResult signInResult) {
super.onSignInFinish(signInResult);
if (signInResult.isStatus()) {
//登录成功
}
}
};
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
AuthManager.get().registerCallback(mAuthCallback);
}
@Override
public void onDestroyView() {
super.onDestroyView();
AuthManager.get().unregisterCallback(mAuthCallback);
}

这样用起来是不是很方便呢?

目前在项中中除了 API 可以这样设计之外,还有其它一个功能只要各个模块都有可能经常使用到的都可以使用这样的思路。

例如,我的 App 里很多页面都会用到获取本地音乐或者视频的列表。同样地,有以下几个类。

微信关注我们,可以获取更多

App 组件化/模块化之路——使用SDK的思路进行模块化设计接口的更多相关文章

  1. App 组件化/模块化之路——构建开发架构思路

    App 组件化/模块化开发架构思路 随着业务的发展 App 开发技术也越来越成熟,对开发者来说 App 代码量也迅速地增长到一个数量级.对于如何架构 App 已经每个开发者面临的实际问题.好的架构可以 ...

  2. App 组件化/模块化之路——如何封装网络请求框架

    App 组件化/模块化之路——如何封装网络请求框架 在 App 开发中网络请求是每个开发者必备的开发库,也出现了许多优秀开源的网络请求库.例如 okhttp retrofit android-asyn ...

  3. App 组件化/模块化之路——Android 框架组件(Android Architecture Components)使用指南

    面对越来越复杂的 App 需求,Google 官方发布了Android 框架组件库(Android Architecture Components ).为开发者更好的开发 App 提供了非常好的样本. ...

  4. 得到、微信、美团、爱奇艺APP组件化架构实践

    一.背景 随着项目逐渐扩展,业务功能越来越多,代码量越来越多,开发人员数量也越来越多.此过程中,你是否有过以下烦恼? 项目模块多且复杂,编译一次要5分钟甚至10分钟?太慢不能忍? 改了一行代码 或只调 ...

  5. App 组件化/模块化之路——Repository 模式

    什么是 Repository 模式 Repository 这个词直译过来仓库.仓储的意思.这个意思其实也能反应出 Repository 模式作用.App 开发中少不了对数据的操作,数据的来源可能有很多 ...

  6. JavaScript 组件化开发之路(一)

    *:first-child{margin-top: 0 !important}.markdown-body>*:last-child{margin-bottom: 0 !important}.m ...

  7. Android 组件化/模块化之路——在展示层搭建MVP结构

    Android 组件化/模块化之路——在展示层搭建MVP结构 什么是MVP Model–View–Presenter (MVP) 源于 Model–View–Controller (MVC) 的结构设 ...

  8. Android组件化、模块化、插件化

    组件:指的是单一的功能组件,如地图组件(MapSDK).扫码组件(QRCode).支付组件(AnjukePay).路由组件(Router)等等: 模块:指的是独立的业务模块,如新房模块(NewHous ...

  9. Android的组件化和模块化

    Android随着业务的增多,而且后续新的需求的增加,代码的修改会变得非常频繁 然后最近在看组件化和模块化 公司的业务没有那么大,所以这种方式我并没有采取 但是还是需要了解下他的使用机制 还有优缺点之 ...

随机推荐

  1. Shiro第一篇【Shiro的基础知识、回顾URL拦截】

    Shiro基础知识 在学习Shiro这个框架之前,首先我们要先了解Shiro需要的基础知识:权限管理 什么是权限管理? 只要有用户参与的系统一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安 ...

  2. MySQL 经典面试题

    MySQL 面试 1 存储过程 什么是存储过程 存储过程是一些编译好的SQL语句 因为系统在调用SQL的时候比较浪费时间,所以之前先将一些基本的额SQL语句代码进行编译(对单表或多表的增删改查),然后 ...

  3. 使用cocos2d脚本生成lua绑定

    这几天要老大要求把DragonBones移到cocos2dx 3.0 里边,并且绑定lua使用接口.因为刚学lua,使用的引擎也刚从2.2改为3.0,各种不熟悉,折腾了好几天才弄完,有空了总结一下 这 ...

  4. 关于数据库中datareader的用法

    1.C#中提供的DataReader可以从数据库中每次提取一条数据. using System; using System.Collections.Generic; using System.Comp ...

  5. HAProxy安装文档

    HAProxy安装文档 [toc][TOC] 一.环境说明 系统环境:CentOS Linux release 7.2.1511 (Core) 系统内核:3.10.0-327.el7.x86_64 软 ...

  6. windows访问控制列表 --ACL(Access Control List)

    1.定义 ACL是一个windows中的表示用户(组)权限的列表. Access Control List(ACL) Access Control Entry(ACE) ... 2.分类 ACL分为两 ...

  7. Python接口自动化——soap协议传参的类型是ns0类型的要创建工厂方法纪要

    1:在Python接口自动化中,对于soap协议的xml的请求我们可以使用Suds Client来实现,其soap协议传参的类型基本上是有2种: 第一种是传参,不需要再创建啥, 第二种就是ns0类型的 ...

  8. mysql explain 分析sql语句

    鉴于最近做的事情,需要解决慢sql的问题,现补充一点sql语句性能分析之explain的使用方式! 综合返回数据情况,分析各个参数,可以了解sql 使用方法:explain  + sql语句 如 :e ...

  9. Javascript 面向对象编程—封装

      前  言 Javascript是一种基于对象(object-based)的语言,你遇到的所有东西几乎都是对象.但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类) ...

  10. PE格式第五讲,手工添加节表

    PE格式第五讲,手工添加节表 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 首先我们要用汇编编写一段汇编代码,用来生成 ...