写在开头
PS:最近也在一直在学习kotlin,所以在写完java后也手撸了一遍kotlin,其中包含自己在kotlin学习中的一些笔记什么的。后续有新想法也会一直更新什么的。下一步打算加入本地Mock json数据的功能。
不多说,本文围绕一张图展开,请查阅,欢迎共同讨论,叨扰了。如果你还有什么需求或者什么想法,可一起完善此demo一起进步哦!!!
github:https://github.com/loveAndroidAndroid/MvpProject ,欢迎共同学习!可下载java和kotlin版本。
优快云:https://download.youkuaiyun.com/download/say_from_wen/10671387 (java)
https://download.youkuaiyun.com/download/say_from_wen/10691277 (kotlin版本)
让我们从library到mouble,让我们谈谈我理解的项目的Project架构。本文章全部就java版本来谈,如果对于kotlin版本的探讨,可留言或者QQ认基友807315559 哈哈
##lib-network封装浅谈
如果是你封装这个的话,你想到的会是什么?一个好的网络底层封装库应该具备哪些功能?
首先:
- 这个网络库不应该和业务有任何的联系,只提供对外设置参数的方法。
- 无论后端返回数据格式是否变化(比如成功的时候是对象,失败会返回数组格式),不会报错解析错误。
- 自定义各种异常的友好提示
- 支持https设置
- 支持本地域名验证
下面我们看一下我的想法:
1.关于第一个问题:主要代码在RetrofitUtil工具类中,所有设置所需的参数都由外部设置进来,提供必要的可扩展性:
public void init(String baseURL, long readTime, long writeTime, long connectTime, SocketFactory socketFactory, HostnameVerifier hostnameVerifier, Interceptor... interceptor) {
if (gson == null) {
gson = new GsonBuilder()
.enableComplexMapKeySerialization() //支持Map的key为复杂对象的形式
.create();
}
retrofit = new Retrofit.Builder()
.baseUrl(baseURL)
.client(genericClient(readTime, writeTime, connectTime, socketFactory, hostnameVerifier, interceptor))
//2.自定义ConverterFactory处理异常情况
.addConverterFactory(JsonArrayConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
public OkHttpClient genericClient(long readTime, long writeTime, long connectTime, SocketFactory socketFactory, HostnameVerifier hostnameVerifier, Interceptor... interceptors) {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.readTimeout(readTime, TimeUnit.SECONDS)
.writeTimeout(writeTime, TimeUnit.SECONDS)
.connectTimeout(connectTime, TimeUnit.SECONDS);
// 可以添加进来https认证
// .socketFactory(socketFactory)
// .hostnameVerifier(hostnameVerifier);
if (interceptors != null && interceptors.length > 0) {
for (int i = 0; i < interceptors.length; i++) {
builder.addInterceptor(interceptors[i]);
}
}
return builder.build();
}
2.不知道你的后台是不是PHP,不知道你有没有遇到过后台数据格式返回在正确的时候是个对象object,出错的时候是个数组array。然后给你报错解析异常,后来经过了解,PHP都是以Array取值,给我们返回的object也是他们强转的,我总不能要求人家每次或者每个错误都给我处理吧。无奈之只能自己搞了。
自定义Converter能解决我们的烦恼:主要为以下代码,核心思想其实利用Gosn的分段解析的一个思想。
@Override
public T convert(ResponseBody value) throws IOException {
String response = value.string();
try {
//ResultResponse 只解析status字段
ResultResponse resultResponse = gson.fromJson(response, ResultResponse.class);
if (Integer.parseInt(resultResponse.getStatus()) == 200) {
//result==200表示成功返回,继续用本来的Model类解析
return gson.fromJson(response, type);
} else {
//ErrResponse 将msg解析为异常消息文本
ErrResponse errResponse = gson.fromJson(response, ErrResponse.class);
throw new ResultException(resultResponse.getStatus(), errResponse.getMsg());
}
} finally {
}
}
3.关于网络请求,出错的情况千遍万变,我们总要自定义一些自己的友好提示,我们如何拦截并设置呢?因为我用的事RxJava2.x,所以此时通过自定义DisposableSubscriber实现,关于友好提示,当然是由你的产品来定,对外提供成功和失败的方法就行了。建议代码如下:
/**
* Created by wen on 2018/5/14.
* 适配器模式 去除不必要的接口方法
*/
public abstract class ApiSubscriberCallBack<T> extends DisposableSubscriber<T> {
@Override
public void onNext(T t) {
onSuccess(t);
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
//在这里做全局的错误处理
if (e instanceof HttpException||
e instanceof ConnectException ||
e instanceof SocketTimeoutException ||
e instanceof TimeoutException ||
e instanceof UnknownHostException) {
//网络错误
onFailure(new Throwable("网络不好哦亲,请确认网络重新连接"));
} else if (e instanceof ResultException) {
//todo 自定义的ResultException 此处结合业务进行处理
onFailure(e);
} else {
//其他错误
onFailure(new Throwable("未知错误,地球即将爆炸,请赶紧跑路"));
}
}
@Override
public void onComplete() {
}
public abstract void onSuccess(T t);
public abstract void onFailure(Throwable t);
}
case 4:
case 5:关于Https设置和域名设置,暂不提供,可google一下就好,有问题可留言讨论。
##lib-image封装浅谈
图片封装的话,我感觉我们还是考虑封装一些通过的效果,比如圆形圆角等,其他的可以通过提供方法设置进去比较好。当然,不要和业务有关联。
图片库功能:
- 自定义ResponseBody实现对图片加载进度的监听。
- 自定义BitmapTransformation实现各种通用效果
- 提供初始化工具类
- 提供Glide缓存清理工具类
1.自定义ResponseBody(Glide4.x)
如果我们不使用Glide的话,我们首先得自定义RegistersComponents并重写RegistersComponents方法来实现对网络请求的监听,替换Glide的默认加载、解码和*编码逻辑。。Glide的话,在4.x以后提供了AppGlideModule的封装类,我们继承它实现registerComponents就好了,代码如下:
@GlideModule
public class ProgressAppGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
super.registerComponents(context, glide, registry);
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(ProgressManager.getOkHttpClient()));
}
}
然后,我们需要将我们自定义的ResponseBody设置给OkHttpClient,并在ResponseBody的source方法中的ForwardingSource内部类中的read方法返回我们需要的进度,主要代码如下:
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead;
long lastTotalBytesRead;
@Override
public long read(@NonNull Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
totalBytesRead += (bytesRead == -1) ? 0 : bytesRead;
if (internalProgressListener != null && lastTotalBytesRead != totalBytesRead) {
lastTotalBytesRead = totalBytesRead;
mainThreadHandler.post(new Runnable() {
@Override
public void run() {
internalProgressListener.onProgress(url, totalBytesRead, contentLength());
}
});
}
return bytesRead;
}
};
}
2.通过BitmapTransformation实现各种各样的图片效果
我们还是主要通过Transformation的transform方法中来对bitmap进行处理来返回我们最终想要的效果,例如我们实现一个圆形效果:(代码会有详细的注释)
@Override
protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
//得到图片最小边
int size = Math.min(toTransform.getWidth(), toTransform.getHeight());
//计算图片起点
int x = (toTransform.getWidth() - size) / 2;
int y = (toTransform.getHeight() - size) / 2;
//创建新的bitmaop
Bitmap square = Bitmap.createBitmap(toTransform, x, y, size, size);
//得到glide中BitmapPool的bitmap位图对象
Bitmap circle = pool.get(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(circle);
Paint paint = new Paint();
//设置TileMode的样式 CLAMP 拉伸 REPEAT 重复 MIRROR 镜像
paint.setShader(new BitmapShader(square, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
float r = size / 2f;
//画圆
canvas.drawCircle(r, r, r, paint);
return circle;
}
3.初始化工具类
4.缓存处理工具类
其实没啥好写的,就是封装一些方法共外界调用,具体的话欢迎下载代码查看。
##lib-core封装浅谈
说到这个模块,就涉及到组件化的一些概念了。lib-core的概念主要就是为上层mouble提供公共服务的一个隔离层,起到承上启下的作用。比如说网络与图片,他肯定是所有mouble一定要使用的两个功能。所以我把它封装进了lib-core中,在其中采用代理模式隔离底层库和上层mouble,更大程度的实现了解耦。下图是他的构造。
base:主要是MVP的一层封装,采用泛型和继承的思想进行了封装,包括loading的显示隐藏逻辑,网络请求的控制,ButterKnife的的注册与反注册等等
bean:就会一个总的data类。
config:采用建造者模式给mouble提供网络库和图片库的全局初始化。
imagehelper:GlideManger提供的图片加载的方法,mouble传入固定的参数来加载想要的图片。
nethelper:这个文件夹下写了三个interceptor,可以打印okhttp的日志和在header里添加和获取参数。(如果您项目没在项目中在header中添加固定参数,也可以自定义interceptor实现参数的添加,项目在使用,后续会在demo更新中添加)
view:自定义的loading显示,不在多说。
PS:关于此部分代码,在这里写不列出,如果您正好有需要,欢迎下载共同学习。
##APPMouble:
主要是对封装的库的一些使用,没啥好看的界面,争取后面能够补充到。
#写在最后
适合自己的架构才是最好的架构,希望和大家一起学习进步,也希望大家不令赐教。