1. <div id="f8mbs"></div>
        您好,歡迎來到源碼搜藏網!分享精神,快樂你我!
        [加入VIP] 設為首頁 | 收藏本站 | 網站地圖 | Sitemap | TAG標簽
      2. 首 頁
      3. 在線工具
      4. jquery手冊
      5. 當前位置:首頁 > 安卓源碼 > 技術博客 >

        Android開發一步步封裝實現自己的網絡請求框架

        時間:2019-02-14 12:15 來源:互聯網 作者:源碼搜藏 瀏覽:收藏 挑錯 推薦 打印

        一、前言 現如今 Android 領域流行的網絡請求框架基本都是用 Retrofit 加 RxJava 來搭配構建的,而以 ViewModel + LiveData + Retrofit + RxJava 來構建請求框架的例子要相對少得多。而本文就是以這四者作為基礎組件,介紹如何一步步封裝實現自己的網絡請求

        一、前言

        現如今 Android 領域流行的網絡請求框架基本都是用 Retrofit 加 RxJava 來搭配構建的,而以 ViewModel + LiveData + Retrofit + RxJava 來構建請求框架的例子要相對少得多。而本文就是以這四者作為基礎組件,介紹如何一步步封裝實現自己的網絡請求框架(本文實現的例子不僅僅只是一個網絡請求框架,同時也是在介紹應用的架構模式),希望對你有所幫助

        目前已實現的功能或者說特色包含以下幾點:

        1、網絡請求結果基于觀察者模式進行傳遞,回調操作與 UI 層的生命周期相綁定,避免了內存泄漏

        2、數據加載時的 startLoading 與加載結束后的 dismissLoading 操作都是自動調用的,具體實現都封裝在基類中。當然,子類也可以實現自己的特定實現。例如,本文提供的例子中,BaseActivity 實現的加載對話框是 ProgressDialog ,子 Activity 可以自主實現其他彈窗形式

        3、當網絡請求結果為非成功狀態時(網絡請求失敗或者業務請求失敗),默認操作是用 Toast 提示失敗原因,支持自定義實現失敗時的操作

        4、邏輯操作與 UI 層相分離,基于觀察者模式來實現消息驅動 UI 變化。提供了在 ViewModel 中操作 UI 變化的能力,包括使 Activity / Fragment 彈出對話框、Toast 消息、finishActivity 等 UI 操作,但 ViewModel 不持有 Activity / Fragment 的引用,而是基于消息驅動實現,從而避免了內存泄漏

        源碼點擊這里查看:ViewModel_Retrofit_RxJava

        Apk 點擊這里下載:ViewModel_Retrofit_RxJava

        二、封裝 BaseViewModel 與 BaseActivity

        ViewModel 與 LiveData 都是 Android Jetpack 架構組件之一。ViewModel 被設計用來存儲和管理 UI 相關數據,以便數據能在界面銷毀時(比如屏幕旋轉)保存數據,而與 ViewModel 相掛鉤的 LiveData 是一個用于保存可以被觀察的值的數據持有類,且遵循應用組件的生命周期,只有在組件的生命周期處于活躍狀態時才會收到數據更新通知

        既然是消息驅動,那么自然需要一個用于抽象消息類型的 Event 類

        /**
         * 作者:leavesC
         * 時間:2018/9/30 22:17
         * 描述:
         * GitHub:https://github.com/leavesC
         * Blog:https://www.jianshu.com/u/9df45b87cfdf
         */
        public class BaseEvent {
        
            private int action;
        
            public BaseEvent(int action) {
                this.action = action;
            }
        
            public int getAction() {
                return action;
            }
        
        }
        
        public class BaseActionEvent extends BaseEvent {
        
            public static final int SHOW_LOADING_DIALOG = 1;
        
            public static final int DISMISS_LOADING_DIALOG = 2;
        
            public static final int SHOW_TOAST = 3;
        
            public static final int FINISH = 4;
        
            public static final int FINISH_WITH_RESULT_OK = 5;
        
            private String message;
        
            public BaseActionEvent(int action) {
                super(action);
            }
        
            public String getMessage() {
                return message;
            }
        
            public void setMessage(String message) {
                this.message = message;
            }
        
        }

        BaseActionEvent 即用于向 View 層傳遞 Action 的 Model,在 ViewModel 通過向 View 層傳遞不同的消息類型,從而觸發相對應的操作。因此,BaseViewModel 需要向子類提供默認的實現

        public interface IViewModelAction {
        
            void startLoading();
        
            void startLoading(String message);
        
            void dismissLoading();
        
            void showToast(String message);
        
            void finish();
        
            void finishWithResultOk();
        
            MutableLiveData<BaseActionEvent> getActionLiveData();
        
        }
        /**
         * 作者:leavesC
         * 時間:2018/9/30 22:24
         * 描述:
         * GitHub:https://github.com/leavesC
         * Blog:https://www.jianshu.com/u/9df45b87cfdf
         */
        public class BaseViewModel extends ViewModel implements IViewModelAction {
        
            private MutableLiveData<BaseActionEvent> actionLiveData;
        
            protected LifecycleOwner lifecycleOwner;
        
            public BaseViewModel() {
                actionLiveData = new MutableLiveData<>();
            }
        
            @Override
            public void startLoading() {
                startLoading(null);
            }
        
            @Override
            public void startLoading(String message) {
                BaseActionEvent baseActionEvent = new BaseActionEvent(BaseActionEvent.SHOW_LOADING_DIALOG);
                baseActionEvent.setMessage(message);
                actionLiveData.setValue(baseActionEvent);
            }
        
            @Override
            public void dismissLoading() {
                actionLiveData.setValue(new BaseActionEvent(BaseActionEvent.DISMISS_LOADING_DIALOG));
            }
        
            @Override
            public void showToast(String message) {
                BaseActionEvent baseActionEvent = new BaseActionEvent(BaseActionEvent.SHOW_TOAST);
                baseActionEvent.setMessage(message);
                actionLiveData.setValue(baseActionEvent);
            }
        
            @Override
            public void finish() {
                actionLiveData.setValue(new BaseActionEvent(BaseActionEvent.FINISH));
            }
        
            @Override
            public void finishWithResultOk() {
                actionLiveData.setValue(new BaseActionEvent(BaseActionEvent.FINISH_WITH_RESULT_OK));
            }
        
            @Override
            public MutableLiveData<BaseActionEvent> getActionLiveData() {
                return actionLiveData;
            }
        
            void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
                this.lifecycleOwner = lifecycleOwner;
            }
        
        }

        那作為消息發送方的 BaseViewModel 的具體實現就完成了,之后是消息的接收方 BaseActivity / BaseFragment

        BaseActivity 通過監聽 BaseViewModel 中 actionLiveData 的數據變化從而在網絡請求開始加載時 startLoading,在加載結束時 dismissLoading

        一般一個 Activity 對應一個 ViewModel,少部分情況是會對應多個 ViewModel,因此 initViewModel() 聲明為了抽象方法,而 initViewModelList() 默認返回了 null

        /**
         * 作者:leavesC
         * 時間:2017/11/29 21:04
         * 描述:
         * GitHub:https://github.com/leavesC
         * Blog:https://www.jianshu.com/u/9df45b87cfdf
         */
        @SuppressLint("Registered")
        public abstract class BaseActivity extends AppCompatActivity {
        
            private ProgressDialog loadingDialog;
        
            @Override
            protected void onCreate(@Nullable Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                initViewModelEvent();
            }
        
            protected abstract ViewModel initViewModel();
        
            protected List<ViewModel> initViewModelList() {
                return null;
            }
        
            private void initViewModelEvent() {
                List<ViewModel> viewModelList = initViewModelList();
                if (viewModelList != null && viewModelList.size() > 0) {
                    observeEvent(viewModelList);
                } else {
                    ViewModel viewModel = initViewModel();
                    if (viewModel != null) {
                        List<ViewModel> modelList = new ArrayList<>();
                        modelList.add(viewModel);
                        observeEvent(modelList);
                    }
                }
            }
        
            private void observeEvent(List<ViewModel> viewModelList) {
                for (ViewModel viewModel : viewModelList) {
                    if (viewModel instanceof IViewModelAction) {
                        IViewModelAction viewModelAction = (IViewModelAction) viewModel;
                        viewModelAction.getActionLiveData().observe(this, baseActionEvent -> {
                            if (baseActionEvent != null) {
                                switch (baseActionEvent.getAction()) {
                                    case BaseActionEvent.SHOW_LOADING_DIALOG: {
                                        startLoading(baseActionEvent.getMessage());
                                        break;
                                    }
                                    case BaseActionEvent.DISMISS_LOADING_DIALOG: {
                                        dismissLoading();
                                        break;
                                    }
                                    case BaseActionEvent.SHOW_TOAST: {
                                        showToast(baseActionEvent.getMessage());
                                        break;
                                    }
                                    case BaseActionEvent.FINISH: {
                                        finish();
                                        break;
                                    }
                                    case BaseActionEvent.FINISH_WITH_RESULT_OK: {
                                        setResult(RESULT_OK);
                                        finish();
                                        break;
                                    }
                                }
                            }
                        });
                    }
                }
            }
        
            @Override
            protected void onDestroy() {
                super.onDestroy();
                dismissLoading();
            }
        
            protected void startLoading() {
                startLoading(null);
            }
        
            protected void startLoading(String message) {
                if (loadingDialog == null) {
                    loadingDialog = new ProgressDialog(this);
                    loadingDialog.setCancelable(false);
                    loadingDialog.setCanceledOnTouchOutside(false);
                }
                loadingDialog.setTitle(message);
                loadingDialog.show();
            }
        
            protected void dismissLoading() {
                if (loadingDialog != null && loadingDialog.isShowing()) {
                    loadingDialog.dismiss();
                }
            }
        
            protected void showToast(String message) {
                Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
            }
        
            protected void finishWithResultOk() {
                setResult(RESULT_OK);
                finish();
            }
        
            protected BaseActivity getContext() {
                return BaseActivity.this;
            }
        
            protected void startActivity(Class cl) {
                startActivity(new Intent(this, cl));
            }
        
            public void startActivityForResult(Class cl, int requestCode) {
                startActivityForResult(new Intent(this, cl), requestCode);
            }
        
            @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
            protected boolean isFinishingOrDestroyed() {
                return isFinishing() || isDestroyed();
            }
        
        }

        三、封裝 Retrofit 與 RxJava

        在前言中說了,框架默認實現了請求失敗時的操作(Toast 提示失敗原因),也支持自定義回調接口。因此,需要兩個回調接口,一個只包含請求成功時的回調接口,另一個多包含了一個請求失敗時的回調接口

        /**
         * 作者:leavesC
         * 時間:2018/10/27 20:53
         * 描述:
         * GitHub:https://github.com/leavesC
         * Blog:https://www.jianshu.com/u/9df45b87cfdf
         */
        public interface RequestCallback<T> {
        
            void onSuccess(T t);
        
        }
        
        public interface RequestMultiplyCallback<T> extends RequestCallback<T> {
        
            void onFail(BaseException e);
        
        }

        此外,為了在網絡請求成功但業務邏輯請求失敗時(例如,請求參數缺失、Token失效等),可以拋出詳細的失敗信息,需要自定義 BaseException

        public class BaseException extends RuntimeException {
        
            private int errorCode = HttpCode.CODE_UNKNOWN;
        
            public BaseException() {
            }
        
            public BaseException(int errorCode, String errorMessage) {
                super(errorMessage);
                this.errorCode = errorCode;
            }
        
            public int getErrorCode() {
                return errorCode;
            }
        
        }

        實現具體的異常類

        public class ParamterInvalidException extends BaseException {
        
            public ParamterInvalidException() {
                super(HttpCode.CODE_PARAMETER_INVALID, "參數有誤");
            }
        
        }
        
        public class TokenInvalidException extends BaseException {
        
            public TokenInvalidException() {
                super(HttpCode.CODE_TOKEN_INVALID, "Token失效");
            }
        
        }
        
        ···

        為了提升性能,Retrofit 一般是設計成單例模式。為了應對應用中 BaseUrl 可能有多個的情況(本文提供的Demo就是如此),此處使用 Map 來存儲多個 Retrofit 實例

        /**
         * 作者:leavesC
         * 時間:2018/10/26 23:11
         * 描述:
         * GitHub:https://github.com/leavesC
         * Blog:https://www.jianshu.com/u/9df45b87cfdf
         */
        public class RetrofitManagement {
        
            private static final long READ_TIMEOUT = 6000;
        
            private static final long WRITE_TIMEOUT = 6000;
        
            private static final long CONNECT_TIMEOUT = 6000;
        
            private final Map<String, Object> serviceMap = new ConcurrentHashMap<>();
        
            private RetrofitManagement() {
        
            }
        
            public static RetrofitManagement getInstance() {
                return RetrofitHolder.retrofitManagement;
            }
        
            private static class RetrofitHolder {
                private static final RetrofitManagement retrofitManagement = new RetrofitManagement();
            }
        
            private Retrofit createRetrofit(String url) {
                OkHttpClient.Builder builder = new OkHttpClient.Builder()
                        .readTimeout(READ_TIMEOUT, TimeUnit.MILLISECONDS)
                        .writeTimeout(WRITE_TIMEOUT, TimeUnit.MILLISECONDS)
                        .connectTimeout(CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
                        .addInterceptor(new HttpInterceptor())
                        .addInterceptor(new HeaderInterceptor())
                        .addInterceptor(new FilterInterceptor())
                        .retryOnConnectionFailure(true);
                if (BuildConfig.DEBUG) {
                    HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
                    httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
                    builder.addInterceptor(httpLoggingInterceptor);
                    builder.addInterceptor(new ChuckInterceptor(ContextHolder.getContext()));
                }
                OkHttpClient client = builder.build();
                return new Retrofit.Builder()
                        .client(client)
                        .baseUrl(url)
                        .addConverterFactory(GsonConverterFactory.create())
                        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                        .build();
            }
        
            <T> ObservableTransformer<BaseResponseBody<T>, T> applySchedulers() {
                return observable -> observable.subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .flatMap(result -> {
                            switch (result.getCode()) {
                                case HttpCode.CODE_SUCCESS: {
                                    return createData(result.getData());
                                }
                                case HttpCode.CODE_TOKEN_INVALID: {
                                    throw new TokenInvalidException();
                                }
                                case HttpCode.CODE_ACCOUNT_INVALID: {
                                    throw new AccountInvalidException();
                                }
                                default: {
                                    throw new ServerResultException(result.getCode(), result.getMsg());
                                }
                            }
                        });
            }
        
        
            private <T> Observable<T> createData(T t) {
                return Observable.create(new ObservableOnSubscribe<T>() {
                    @Override
                    public void subscribe(ObservableEmitter<T> emitter) {
                        try {
                            emitter.onNext(t);
                            emitter.onComplete();
                        } catch (Exception e) {
                            emitter.onError(e);
                        }
                    }
                });
            }
        
            <T> T getService(Class<T> clz) {
                return getService(clz, HttpConfig.BASE_URL_WEATHER);
            }
        
            <T> T getService(Class<T> clz, String host) {
                T value;
                if (serviceMap.containsKey(host)) {
                    Object obj = serviceMap.get(host);
                    if (obj == null) {
                        value = createRetrofit(host).create(clz);
                        serviceMap.put(host, value);
                    } else {
                        value = (T) obj;
                    }
                } else {
                    value = createRetrofit(host).create(clz);
                    serviceMap.put(host, value);
                }
                return value;
            }
        
        }

        此外還需要一個自定義的 Observer 來對數據請求結果進行自定義回調

        /**
         * 作者:leavesC
         * 時間:2018/10/27 20:52
         * 描述:
         * GitHub:https://github.com/leavesC
         * Blog:https://www.jianshu.com/u/9df45b87cfdf
         */
        public class BaseSubscriber<T> extends DisposableObserver<T> {
        
            private BaseViewModel baseViewModel;
        
            private RequestCallback<T> requestCallback;
        
            public BaseSubscriber(BaseViewModel baseViewModel) {
                this.baseViewModel = baseViewModel;
            }
        
            BaseSubscriber(BaseViewModel baseViewModel, RequestCallback<T> requestCallback) {
                this.baseViewModel = baseViewModel;
                this.requestCallback = requestCallback;
            }
        
            @Override
            public void onNext(T t) {
                if (requestCallback != null) {
                    requestCallback.onSuccess(t);
                }
            }
        
            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
                if (requestCallback instanceof RequestMultiplyCallback) {
                    RequestMultiplyCallback callback = (RequestMultiplyCallback) requestCallback;
                    if (e instanceof BaseException) {
                        callback.onFail((BaseException) e);
                    } else {
                        callback.onFail(new BaseException(HttpCode.CODE_UNKNOWN, e.getMessage()));
                    }
                } else {
                    if (baseViewModel == null) {
                        Toast.makeText(ContextHolder.getContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
                    } else {
                        baseViewModel.showToast(e.getMessage());
                    }
                }
            }
        
            @Override
            public void onComplete() {
        
            }
        
        }

        四、BaseRemoteDataSource 與 BaseRepo

        上文所介紹的 RequestCallback、RetrofitManagement 與 BaseSubscriber 還是一個個單獨的個體,還需要一個鏈接器來將之串起來,這個鏈接器的實現類即 BaseRemoteDataSource

        在這里,對 BaseRemoteDataSource 的定位是將之當成一個接口實現者,即在 RemoteDataSource 中實際調用各個請求接口,并通過 RxJava 來控制 loading 彈出以及銷毀的時機

        一般而言,BaseRemoteDataSource 的實現類中聲明的是具有相關邏輯的接口。例如,對于登錄模塊,可聲明一個 LoginDataSource,對于設置模塊,可以聲明一個 SettingsDataSource

        /**
         * 作者:leavesC
         * 時間:2018/10/27 7:42
         * 描述:
         * GitHub:https://github.com/leavesC
         * Blog:https://www.jianshu.com/u/9df45b87cfdf
         */
        public abstract class BaseRemoteDataSource {
        
            private CompositeDisposable compositeDisposable;
        
            private BaseViewModel baseViewModel;
        
            public BaseRemoteDataSource(BaseViewModel baseViewModel) {
                this.compositeDisposable = new CompositeDisposable();
                this.baseViewModel = baseViewModel;
            }
        
            protected <T> T getService(Class<T> clz) {
                return RetrofitManagement.getInstance().getService(clz);
            }
        
            protected <T> T getService(Class<T> clz, String host) {
                return RetrofitManagement.getInstance().getService(clz, host);
            }
        
            private <T> ObservableTransformer<BaseResponseBody<T>, T> applySchedulers() {
                return RetrofitManagement.getInstance().applySchedulers();
            }
        
            protected <T> void execute(Observable observable, RequestCallback<T> callback) {
                execute(observable, new BaseSubscriber<>(baseViewModel, callback), true);
            }
        
            protected <T> void execute(Observable observable, RequestMultiplyCallback<T> callback) {
                execute(observable, new BaseSubscriber<>(baseViewModel, callback), true);
            }
        
            public void executeWithoutDismiss(Observable observable, Observer observer) {
                execute(observable, observer, false);
            }
        
            private void execute(Observable observable, Observer observer, boolean isDismiss) {
                Disposable disposable = (Disposable) observable
                        .throttleFirst(500, TimeUnit.MILLISECONDS)
                        .subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .compose(applySchedulers())
                        .compose(isDismiss ? loadingTransformer() : loadingTransformerWithoutDismiss())
                        .subscribeWith(observer);
                addDisposable(disposable);
            }
        
            private void addDisposable(Disposable disposable) {
                compositeDisposable.add(disposable);
            }
        
            public void dispose() {
                if (!compositeDisposable.isDisposed()) {
                    compositeDisposable.dispose();
                }
            }
        
            private void startLoading() {
                if (baseViewModel != null) {
                    baseViewModel.startLoading();
                }
            }
        
            private void dismissLoading() {
                if (baseViewModel != null) {
                    baseViewModel.dismissLoading();
                }
            }
        
            private <T> ObservableTransformer<T, T> loadingTransformer() {
                return observable -> observable
                        .subscribeOn(AndroidSchedulers.mainThread())
                        .unsubscribeOn(AndroidSchedulers.mainThread())
                        .observeOn(AndroidSchedulers.mainThread())
                        .doOnSubscribe(disposable -> startLoading())
                        .doFinally(this::dismissLoading);
            }
        
            private <T> ObservableTransformer<T, T> loadingTransformerWithoutDismiss() {
                return observable -> observable
                        .subscribeOn(AndroidSchedulers.mainThread())
                        .unsubscribeOn(AndroidSchedulers.mainThread())
                        .observeOn(AndroidSchedulers.mainThread())
                        .doOnSubscribe(disposable -> startLoading());
            }
        
        }

        除了 BaseRemoteDataSource 外,還需要一個 BaseRepo。對 BaseRepo 的定位是將其當做一個接口調度器,其持有 BaseRemoteDataSource 的實例并中轉 ViewModel 的接口調用請求,并可以在 BaseRepo 分擔一部分數據處理邏輯

        /**
         * 作者:leavesC
         * 時間:2018/10/27 21:10
         * 描述:
         * GitHub:https://github.com/leavesC
         * Blog:https://www.jianshu.com/u/9df45b87cfdf
         */
        public class BaseRepo<T> {
        
            protected T remoteDataSource;
        
            public BaseRepo(T remoteDataSource) {
                this.remoteDataSource = remoteDataSource;
            }
        
        }

        這樣,ViewModel 不關心接口的實際調用實現,方便以后更換 BaseRemoteDataSource 的實現方式,且將一部分的數據處理邏輯放到了 BaseRepo ,有利于邏輯的復用

        五、實踐操作(1)-請求天氣數據

        上文講了一些基礎組件的邏輯實現以及對其的定位,此小節就以一個請求天氣數據的接口為例,來介紹如何具體實現一個網絡請求的整體流程

        首先是聲明接口

        public interface ApiService {
        
            @Headers({HttpConfig.HTTP_REQUEST_TYPE_KEY + ":" + HttpConfig.HTTP_REQUEST_WEATHER})
            @GET("onebox/weather/query")
            Observable<BaseResponseBody<Weather>> queryWeather(@Query("cityname") String cityName);
        
        }

        增加的頭部信息是為了標明該接口的請求類型,因為本文作為 demo 的幾個接口所用到的 baseUrl以及 請求key 并不相同,因此通過聲明頭部來為接口動態指定請求參數,而這就需要用到 Retrofit 的攔截器了

        public class FilterInterceptor implements Interceptor {
        
            @NonNull
            @Override
            public Response intercept(@NonNull Chain chain) throws IOException {
                Request originalRequest = chain.request();
                HttpUrl.Builder httpBuilder = originalRequest.url().newBuilder();
                Headers headers = originalRequest.headers();
                if (headers != null && headers.size() > 0) {
                    String requestType = headers.get(HttpConfig.HTTP_REQUEST_TYPE_KEY);
                    if (!TextUtils.isEmpty(requestType)) {
                        switch (requestType) {
                            case HttpConfig.HTTP_REQUEST_WEATHER: {
                                httpBuilder.addQueryParameter(HttpConfig.KEY, HttpConfig.KEY_WEATHER);
                                break;
                            }
                            case HttpConfig.HTTP_REQUEST_QR_CODE: {
                                httpBuilder.addQueryParameter(HttpConfig.KEY, HttpConfig.KEY_QR_CODE);
                                break;
                            }
                            case HttpConfig.HTTP_REQUEST_NEWS: {
                                httpBuilder.addQueryParameter(HttpConfig.KEY, HttpConfig.KEY_NEWS);
                                break;
                            }
                        }
                    }
                }
                Request.Builder requestBuilder = originalRequest.newBuilder()
                        .removeHeader(HttpConfig.HTTP_REQUEST_TYPE_KEY)
                        .url(httpBuilder.build());
                return chain.proceed(requestBuilder.build());
            }
        
        }

        聲明 BaseRemoteDataSource 的實現類 WeatherDataSource

        public class WeatherDataSource extends BaseRemoteDataSource implements IWeatherDataSource {
        
            public WeatherDataSource(BaseViewModel baseViewModel) {
                super(baseViewModel);
            }
        
            @Override
            public void queryWeather(String cityName, RequestCallback<Weather> responseCallback) {
                execute(getService(ApiService.class).queryWeather(cityName), responseCallback);
            }
        
        }

        聲明 BaseRepo 的實現類 WeatherRepo

        public class WeatherRepo extends BaseRepo<IWeatherDataSource> {
        
            public WeatherRepo(IWeatherDataSource remoteDataSource) {
                super(remoteDataSource);
            }
        
            public MutableLiveData<Weather> queryWeather(String cityName) {
                MutableLiveData<Weather> weatherMutableLiveData = new MutableLiveData<>();
                remoteDataSource.queryWeather(cityName, new RequestCallback<Weather>() {
                    @Override
                    public void onSuccess(Weather weather) {
                        weatherMutableLiveData.setValue(weather);
                    }
                });
                return weatherMutableLiveData;
            }
        
        }

        還需要一個 WeatherViewModelView 層通過調用 queryWeather() 方法在請求成功時觸發 weatherLiveData 更新數據,View 層已事先監聽 weatherLiveData,并在數據更新時就可以立即收到最新數據

        public class WeatherViewModel extends BaseViewModel {
        
            private MutableLiveData<Weather> weatherLiveData;
        
            private WeatherRepo weatherRepo;
        
            public WeatherViewModel() {
                weatherLiveData = new MutableLiveData<>();
                weatherRepo = new WeatherRepo(new WeatherDataSource(this));
            }
        
            public void queryWeather(String cityName) {
                weatherRepo.queryWeather(cityName).observe(lifecycleOwner, new Observer<Weather>() {
                    @Override
                    public void onChanged(@Nullable Weather weather) {
                        weatherLiveData.setValue(weather);
                    }
                });
            }
        
            public MutableLiveData<Weather> getWeatherLiveData() {
                return weatherLiveData;
            }
        }

        在 QueryWeatherActivity 中打印出接口的請求結果

        public class QueryWeatherActivity extends BaseActivity {
        
            private static final String TAG = "QueryWeatherActivity";
        
            private WeatherViewModel weatherViewModel;
        
            private EditText et_cityName;
        
            private TextView tv_weather;
        
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_query_weather);
                et_cityName = findViewById(R.id.et_cityName);
                tv_weather = findViewById(R.id.tv_weather);
            }
        
            @Override
            protected ViewModel initViewModel() {
                weatherViewModel = LViewModelProviders.of(this, WeatherViewModel.class);
                weatherViewModel.getWeatherLiveData().observe(this, this::handlerWeather);
                return weatherViewModel;
            }
        
            private void handlerWeather(Weather weather) {
                StringBuilder result = new StringBuilder();
                for (Weather.InnerWeather.NearestWeather nearestWeather : weather.getData().getWeather()) {
                    result.append("\n\n").append(new Gson().toJson(nearestWeather));
                }
                tv_weather.setText(result.toString());
            }
        
            public void queryWeather(View view) {
                tv_weather.setText(null);
                weatherViewModel.queryWeather(et_cityName.getText().toString());
            }
        
        }

        Android開發一步步封裝實現自己的網絡請求框架

        也許有人會覺得為了請求一個接口需要建立三個實現類(WeatherDataSource、WeatherRepo、WeatherViewModel)以及一個接口(IQrCodeDataSource)有點繁瑣,但這是想要劃分職責并實現邏輯與UI相隔離的必然結果。WeatherDataSource 用來實現接口的實際調用,只負責請求數據并傳遞請求結果。WeatherRepo 用來屏蔽 WeatherViewModel 對 WeatherDataSource 的感知,并承擔起一部分數據處理邏輯。WeatherViewModel 用于實現邏輯與 UI 的隔離,并保障數據不因為頁面重建而丟失。這樣,Activity 就可以盡量只承擔數據呈現的職責,而不必摻雜數據處理邏輯

        六、實踐操作(2)-請求生成二維碼

        此處再來看一個例子,用于生成指定內容的二維碼

        public class QrCodeDataSource extends BaseRemoteDataSource implements IQrCodeDataSource {
        
            public QrCodeDataSource(BaseViewModel baseViewModel) {
                super(baseViewModel);
            }
        
            @Override
            public void createQrCode(String text, int width, RequestCallback<QrCode> callback) {
                execute(getService(ApiService.class, HttpConfig.BASE_URL_QR_CODE).createQrCode(text, width), callback);
            }
        
        }

        此處接口請求回來的只是一段 base64 編碼的字符串,而外部希望獲取到的自然是一個可以直接使用的 Bitmap ,因此可以在 Repo 中先對數據進行轉換后再傳遞到外部

        public class QrCodeRepo extends BaseRepo<IQrCodeDataSource> {
        
            public QrCodeRepo(IQrCodeDataSource remoteDataSource) {
                super(remoteDataSource);
            }
        
            public MutableLiveData<QrCode> createQrCode(String text, int width) {
                MutableLiveData<QrCode> liveData = new MutableLiveData<>();
                remoteDataSource.createQrCode(text, width, new RequestCallback<QrCode>() {
                    @SuppressLint("CheckResult")
                    @Override
                    public void onSuccess(QrCode qrCode) {
                        Observable.create(new ObservableOnSubscribe<Bitmap>() {
                            @Override
                            public void subscribe(@NonNull ObservableEmitter<Bitmap> emitter) throws Exception {
                                Bitmap bitmap = base64ToBitmap(qrCode.getBase64_image());
                                emitter.onNext(bitmap);
                                emitter.onComplete();
                            }
                        }).subscribeOn(Schedulers.io())
                                .observeOn(AndroidSchedulers.mainThread())
                                .subscribe(new Consumer<Bitmap>() {
                                    @Override
                                    public void accept(@NonNull Bitmap bitmap) throws Exception {
                                        qrCode.setBitmap(bitmap);
                                        liveData.setValue(qrCode);
                                    }
                                });
                    }
                });
                return liveData;
            }
        
            private static Bitmap base64ToBitmap(String base64String) {
                byte[] decode = Base64.decode(base64String, Base64.DEFAULT);
                return BitmapFactory.decodeByteArray(decode, 0, decode.length);
            }
        
        }
        public class QrCodeViewModel extends BaseViewModel {
        
            private MutableLiveData<QrCode> qrCodeLiveData;
        
            private QrCodeRepo qrCodeRepo;
        
            public QrCodeViewModel() {
                qrCodeLiveData = new MutableLiveData<>();
                qrCodeRepo = new QrCodeRepo(new QrCodeDataSource(this));
            }
        
            public void createQrCode(String text, int width) {
                qrCodeRepo.createQrCode(text, width).observe(lifecycleOwner, new Observer<QrCode>() {
                    @Override
                    public void onChanged(@Nullable QrCode qrCode) {
                        qrCodeLiveData.setValue(qrCode);
                    }
                });
            }
        
            public MutableLiveData<QrCode> getQrCodeLiveData() {
                return qrCodeLiveData;
            }
        
        }
        

        Android開發一步步封裝實現自己的網絡請求框架

        七、實踐操作(3)-請求失敗示例

        前言說了,本文封裝的網絡框架當網絡請求結果為非成功狀態時(網絡請求失敗或者業務請求失敗),默認操作是用 Toast 提示失敗原因,也支持自定義實現失敗時的操作。此處就來看當請求失敗時如何進行處理

        此處需要聲明兩個并不存在的接口

        public interface ApiService {
        
            @GET("leavesC/test1")
            Observable<BaseResponseBody<String>> test1();
        
            @GET("leavesC/test2")
            Observable<BaseResponseBody<String>> test2();
        
        }
        
        public class FailExampleDataSource extends BaseRemoteDataSource implements IFailExampleDataSource {
        
            public FailExampleDataSource(BaseViewModel baseViewModel) {
                super(baseViewModel);
            }
        
            @Override
            public void test1(RequestCallback<String> callback) {
                execute(getService(ApiService.class).test1(), callback);
            }
        
            @Override
            public void test2(RequestCallback<String> callback) {
                execute(getService(ApiService.class).test2(), callback);
            }
        
        }
        public class FailExampleRepo extends BaseRepo<IFailExampleDataSource> {
        
            public FailExampleRepo(IFailExampleDataSource remoteDataSource) {
                super(remoteDataSource);
            }
        
            public MutableLiveData<String> test1() {
                MutableLiveData<String> newsPackMutableLiveData = new MutableLiveData<>();
                remoteDataSource.test1(new RequestCallback<String>() {
                    @Override
                    public void onSuccess(String newsPack) {
                        newsPackMutableLiveData.setValue(newsPack);
                    }
                });
                return newsPackMutableLiveData;
            }
        
            public void test2(RequestMultiplyCallback<String> callback) {
                remoteDataSource.test2(callback);
            }
        
        }

        test1() 方法用的是基礎類的默認失敗回調,即直接 Toast 提示失敗信息。而 test2() 方法則是自定義了請求失敗時的回調操作

        public class FailExampleViewModel extends BaseViewModel {
        
            private MutableLiveData<String> test1LiveData = new MutableLiveData<>();
        
            private MutableLiveData<String> test2LiveData = new MutableLiveData<>();
        
            private FailExampleRepo failExampleRepo = new FailExampleRepo(new FailExampleDataSource(this));
        
            public void test1() {
                failExampleRepo.test1().observe(lifecycleOwner, new Observer<String>() {
                    @Override
                    public void onChanged(@Nullable String s) {
                        test1LiveData.setValue(s);
                    }
                });
            }
        
            public void test2() {
                failExampleRepo.test2(new RequestMultiplyCallback<String>() {
                    @Override
                    public void onFail(BaseException e) {
                        showToast("test2方法請求失敗:" + e.getMessage());
                        finish();
                    }
        
                    @Override
                    public void onSuccess(String s) {
                        test2LiveData.setValue(s);
                    }
                });
            }
        
        }

        Android開發一步步封裝實現自己的網絡請求框架

        八、結束語

        這就是整個請求框架的大體架構了,也經過了實際項目的考驗了,目前運行良好,但里面可能還會包含一些不合理的地方,歡迎大家指正反饋,如果覺得對你有所幫助,也歡迎 star

        Android開發一步步封裝實現自己的網絡請求框架轉載http://www.rhcg.tw/appboke/39530.html
        標簽:網站源碼
        辽宁十一选五单双