前言:
现在,在网络方面,安卓主流是采用Retrofit + RxJava2的组合。但是,天天用别人封好的东西,总不是办法。就好像天天去大宝剑,总不如找个漂亮的女朋友好(有人说,有漂亮的女友,谁还去大宝剑。。那就太年轻了,妻不如妾,妾不如妓,妓不如偷,偷不如偷不着)。虽然自己水平有限,但做人如果没有理想,那与咸鱼有什么区别。所以,冒着被喷成狗的风险,我毅然决然的写下这篇博客。毕竟骂不死我的,只会使我更强大。尝试不断的尝试,实践不断的实践,才会进步。。。 如果你觉得网上的其它封装思路过于复杂,可以看看这篇,简单易入手。封装的思路是低藕合,易扩展,易阅读,易维护,易调用。在这思路的基础上,实现了三个解藕:
1、解藕具体的第三方网络库。
2、解藕具体的第三方json解析库。
3、解藕具体的线程切换实现。
好了,开始主题,先从思考一个网络框架能做什么入手。
一、一个网络框架能做什么?(需求是发明之母)
一个网络框架能做什么?即是这个网络框架有哪些行为。为了规范定义网络框架的行为,需要定义一个IHttpManager接口。IHttpManager接口,用于定义网络框架的基本协议。比如,最基本的Get、Post请求,如果项目有需要,可以再添加诸如Put、Delete等其它请求方法。这样,不管用什么第三方库,对于封装了第三方库的类,都必须扩展自这个协议。比如,OkHttp和Retrofit,这两个第三方库来说。需要分别创建两个类OkHttpManager和RetrofitHttpManager,两者都实现IHttpManager接口。有了协议,那接下来,就是如何初始化基本的网络配置。
下面是IHttpManager的定义:
**
* @Author Lyf
* @CreateTime 2018/2/8
* @Description An IHttpManager interface defines What the IHttpManager can do.
* When you change your Http Framework to another, The new one must be implemented IHttpManager.
* If do so, you will change nothing for the rest of project.
**/
public interface IHttpManager {
/**
* @param url A uniform resource locator (URL) with a scheme of either {@code http} or {@code https}.
* @param params A set of params which will be sent to remote server when a request is sent.
* @param responseCallback<T> a response returned by remote server.
*/
<T> void doGet(@NonNull String url, @Nullable ArrayMap<String, Object> params, @Nullable Callback<T> responseCallback);
/**
* @param url A uniform resource locator (URL) with a scheme of either {@code http} or {@code https}.
* @param params A set of params which will be sent to remote server when a request is sent.
* @param responseCallback<T> a response returned by remote server.
*/
<T> void doPost(@NonNull String url, @Nullable ArrayMap<String, Object> params, @Nullable Callback<T> responseCallback);
}
二、如何初始化基本的网络配置?(如何开始)
基本的网络配置,比如超时、缓存、Https的证书配置。这样,就需要一个类,来统一初始化,于是创建了HttpManager。HttpManager采用Builder模式。有一个静态的方法,需要传入一个HttpBuilder类来初始化网络。同时,HttpManager,有一个getHttpManager的静态方法,但返回的不是HttpManager本身,而是一个IHttpManager对象(这个对象是静态的,采用单例实现,确保全局唯一)。因为不同的网络库,都会实现这个接口。所以,替换不同的网络库的时候,只要修改这个IHttpManager所代理的对象即可。这样就解藕了第三方网络库与实际请求。之前,考虑过用工厂模式来解藕。但因为,更换库的可能性比较小,同时,这里也达到解藕的目的。所以,采用工厂模式来获取不同网络库,需要再想。
下面是HttpManager的定义
/**
* @Author Lyf
* @CreateTime 2018/2/8
* @Description A HttpManager class is in charge of Managing Http Framework.
* Such as, sets some settings of http or decides to use which http library.
**/
public final class HttpManager {
// A tag for logging.
private final static String TAG = HttpManager.class.getSimpleName();
// mHttpManager is an IHttpManager instance.
private static IHttpManager mHttpManager;
// Init HttpManager.
public static void initHttpManager(HttpBuilder httpBuilder) {
mHttpManager = new OkHttpManager(httpBuilder);
/*
* Here is the code Shows you how to use Retrofit instead of OkHttp.
* As you can see, What you only have to do is creating another Manager which implements the IHttpManager.
*/
// mHttpManager = new RetrofitManager(httpBuilder);
}
/**
* Returns An IHttpManager instance.
* If the project never initialized the HttpManager, A RuntimeException will be thrown.
*/
public static IHttpManager getHttpManager() {
if (mHttpManager == null) {
throw new RuntimeException("You have to invoke initHttpManager() to init HttpManager before you use it.");
}
return mHttpManager;
}
private HttpManager() {
throw new UnsupportedOperationException("You can't instantiate this class but invoke getHttpManager() after called initHttpManager().");
}
}
三、中间层。简化网络请求,并再次解藕具体项目与第三方网络框架。(如何简化与解藕)
为了简化网络请求和做一层请求拦截,并进步一步解藕xxHttpManager(比如OkHttpManager)与实际项目的耦合。这里抽象出一个BaseHttpUtil类,这是一个抽象类,有一个抽象方法,用于对参数进行签名。实际项目,继承该类,重写签名返回,需要签名,则签名。不需要,只要返回原参即可。这样做的好处是,在你更换第三方库的时候,签名的代码,并不会改动到。并且,任何中间层的拦截处理的代码,都不会改动到。同时,该类提供一些重载get、post的请求方法,因为有些网络请求不一定需要带参数或回调。
下面是BaseHttpUtil的定义
/**
* @Author Lyf
* @CreateTime 2018/1/23
* @Description you can use BaseHttpUtil to do requests directly.
* Note that: You may should rewrite the getSignParams() method to sign your params before doing request.
* Return the original params if you don't need to sign your params.
**/
public abstract class BaseHttpUtil {
private IHttpManager mHttpManager = HttpManager.getHttpManager();
/**
* Add algorithms of sign.
*
* @param params the original requesting params.
* @return The params with sign or the original params if you don't need to sign the params.
*/
protected abstract ArrayMap<String, Object> getSignParams(ArrayMap<String, Object> params);
public <T> void doGet(@NonNull String url, @Nullable ArrayMap<String, Object> params, @Nullable Callback<T> responseCallback) {
mHttpManager.doGet(url, params, responseCallback);
}
public <T> void doPost(@NonNull String url, @Nullable ArrayMap<String, Object> params, @Nullable Callback<T> responseCallback) {
mHttpManager.doPost(url, getSignParams(params), responseCallback);
}
// These methods below do request without params and listen.
public void doGet(@NonNull String url) {
doGet(url, null, null);
}
public void doPost(Object tag, String url) {
doPost(url, null, null);
}
public void doPut(Object tag, String url) {
}
public void doDelete(Object tag, String url) {
}
// These methods below do request without params but with a listen.
public <T> void doGet(@NonNull String url, @Nullable Callback<T> responseCallback) {
doGet(url, null, responseCallback);
}
public <T> void doPost(@NonNull String url, @Nullable Callback<T> responseCallback) {
}
public void cancelRequestWithTag(Object tag) {
}
}
四、创建具体的xxHttpManager类,来封装第三方名为xx的网络框架。(说了那么多,该怎么做?)
这里以OkHttpManager为例。创建一个OkHttpManager的同时,需要再创建一个IOkHttpManager接口(这个又继承自IHttpManager)。之所以,要创建这个IOkHttpManager接口,是为了将OkHttpManager的行为定义在接口中,这样以后维护的时候,就不是直接看该类的代码,而是优先看他的协议(面向协议)。同时,以后如果只是要重构这个OkHttpManager,也只需要照着IOkHttpManager的协议去重构即可。虽然,方法定义在接口里,会导致一些不必要对外暴露的方法的访问权限变成public,破坏了封装性。但是,外面实际是不能拿到IOkHttpManager或OkHttpManager的实例,而只能拿到IHttpManager这个最基础的代理。其封装性的破坏,相较于上面所述的好处,也就不重要了。
下面是OkHttpManager的定义
/**
* @Author Lyf
* @CreateTime 2018/2/8
* @Description
**/
public final class OkHttpManager implements IOkHttpManager {
private final static String TAG = OkHttpManager.class.getSimpleName();
// Cache the Headers to avoid creating Headers for each request.
private static Headers mOkHttpHeaders;
// It's better to use a single instance of OkHttpClient in a certain project.
private final OkHttpClient mOkHttpClient = new OkHttpClient();
// Manager Headers.
private IHeaderManager mHeaderManager = HeaderManager.getHeaderManager();
// Sets some settings of http to OkHttpClient with a HttpBuilder.
public OkHttpManager(HttpBuilder httpBuilder) {
OkHttpClient.Builder okHttpBuilder = mOkHttpClient.newBuilder();
okHttpBuilder.readTimeout(httpBuilder.getReadTimeOut(), TimeUnit.MILLISECONDS)
.writeTimeout(httpBuilder.getWriteTimeOut(), TimeUnit.MILLISECONDS)
.connectTimeout(httpBuilder.getConnectTimeOut(), TimeUnit.MILLISECONDS);
okHttpBuilder.build();
}
@Override
public <T> void doGet(@NonNull String url, @Nullable ArrayMap<String, Object> params, final @Nullable Callback<T> responseCallback) {
// Creates a basic request
final Request request = new Request
.Builder()
.tag(url)
.headers(getOkHttpHeaders(mHeaderManager.getHeaders()))
.url(Util.composeParams(url, params))
.build();
handleNetTask(request, responseCallback);
}
@Override
public <T> void doPost(@NonNull String url, @Nullable ArrayMap<String, Object> params, @Nullable Callback<T> responseCallback) {
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),
ParseUtil.toJson(params));
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
handleNetTask(request, responseCallback);
}
@Override
public void cancelRequestWithTag(Object tag) {
// cancle a request via a tag.
}
@Override
public Headers getOkHttpHeaders(ArrayMap<String, String> originalHeaders) {
return mOkHttpHeaders;
}
五、Json解析库。(要用哪个解析库,怎么更换)
不管是用什么网络库,都要对接口返回的json进行解析。安卓这边有多种第三方库,用于解析json。为了解藕,解析库与xxHttpManager的关系。这里,也采用了类似代理的方式,定义IParseUtil协议。不同的工具库,都实现该协议,来达到解藕的目的。方便更换第三方库(虽然可能性不高,就我个人的经验来看,还没遇到过要换该库的需求,但理论上还是得过去,兼容以后的未知数)。
下面是ParseUtil的定义
public class ParseUtil {
private static IParseUtil INSTANCE;
private static IParseUtil getParseUtil() {
if (INSTANCE == null) {
synchronized (ParseUtil.class) {
if (INSTANCE == null) {
INSTANCE = new GsonUtil();
}
}
}
return INSTANCE;
}
public static <T> T parseJson(String json, Class tClass) {
return getParseUtil().parseJson(json, tClass);
}
public static String toJson(Object object) {
return getParseUtil().toJson(object);
}
}
六、线程切换。(线程切换方式这么多,如何实现无伤切换?)
线程切换。安卓中有UI线程和非UI线程之分。前者,用于处理UI绘制和响应。后者,用于处理一些费时操作,比如网络请求。同样为了解藕,线程切换与xxHttpManager的关系,也做了一些处理。现在用的是RxJava2(第三方库)去实现线程切换。
下面是负责线程切换的ThreadManager的定义
/**
* @Author Lyf
* @CreateTime 2018/2/6
* @Description
**/
public class ThreadManager {
/**
* This method is used to do something on two separately different threads(threads are called one after the other) in an async way.
* For instance, you can do some heavy cpu operations on subThread and then updates your views on UI thread.
*
* @param subscribeListener do something in subThread(non-ui thread)
* @param observerListener do something in mainThread(ui thread)
* @param <T> after do something in subThread,
* you may want to pass a T(bean) object as a result to ui thread to update views.
*/
public static <T> void execute(SubscribeListener<T> subscribeListener,
ObserverListener<T> observerListener) {
Observable.create((ObservableOnSubscribe<T>) emitter -> {
emitter.onNext(subscribeListener.runOnSubThread());
emitter.onComplete();
}).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observerListener::runOnUiThread);
}
/**
* Run on Ui Thread.
*/
public static void runOnUiThread(Runnable runnable) {
Observable.just(runnable)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Runnable::run);
}
}
七、其它配置。
- HeaderManager用于管理Header的配置和获取。
- HttpCode用于定义最基本的业务逻辑的成功与失败的错误码。
- Response和CallBack,是回调用的。具体怎么封,就看自己的定义。
八、最后总结:
1、面向协议。
2、第三方库,看需要,通过协议来解藕。
九、框架整体结构图。
十、Github的demo地址。
路径在framework的net包下,请求示例在app这个module的login包下。
封装示例入口