Android APP架構淺談(含程式碼)
寫在開頭
不多說,本文圍繞一張圖展開,請查閱,歡迎共同討論,叨擾了。如果你還有什麼需求或者什麼想法,可一起完善此demo一起進步哦!!!
讓我們從library到mouble,讓我們談談我理解的專案的Project架構。
lib-network封裝淺談
如果是你封裝這個的話,你想到的會是什麼?一個好的網路底層封裝庫應該具備哪些功能?
首先: 1. 這個網路庫不應該和業務有任何的聯絡,只提供對外設定引數的方法。 2. 無論後端返回資料格式是否變化(比如成功的時候是物件,失敗會返回陣列格式),不會報錯解析錯誤。 3. 自定義各種異常的友好提示 4. 支援https設定 5. 支援本地域名驗證
下面我們看一下我的想法:
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封裝淺談
圖片封裝的話,我感覺我們還是考慮封裝一些通過的效果,比如圓形圓角等,其他的可以通過提供方法設定進去比較好。當然,不要和業務有關聯。
圖片庫功能: 1. 自定義ResponseBody實現對圖片載入進度的監聽。 2. 自定義BitmapTransformation實現各種通用效果 3. 提供初始化工具類 4. 提供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:
主要是對封裝的庫的一些使用,沒啥好看的介面,爭取後面能夠補充到。
寫在最後
適合自己的架構才是最好的架構,希望和大家一起學習進步,也希望大家不令賜教。