1. 程式人生 > >Android APP架構淺談(含程式碼)

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:

主要是對封裝的庫的一些使用,沒啥好看的介面,爭取後面能夠補充到。

寫在最後

適合自己的架構才是最好的架構,希望和大家一起學習進步,也希望大家不令賜教。