寫在開頭
PS:最近也在一直在學習kotlin,所以在寫完java後也手擼了一遍kotlin,其中包含自己在kotlin學習中的一些筆記什麼的。後續有新想法也會一直更新什麼的。下一步打算加入本地Mock json數據的功能。
不多說,本文圍繞一張圖展開,請查閱,歡迎共同討論,叨擾了。如果你還有什麼需求或者什麼想法,可一起完善此demo一起進步哦!!!
github:https://github.com/loveAndroidAndroid/MvpProject ,歡迎共同學習!可下載java和kotlin版本。
CSDN:https://download.csdn.net/download/say_from_wen/10671387 (java)
https://download.csdn.net/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:
主要是對封裝的庫的一些使用,沒啥好看的界面,爭取後面能夠補充到。
#寫在最後
適合自己的架構纔是最好的架構,希望和大家一起學習進步,也希望大家不令賜教。