打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约1

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约

抓住人生中的一分一秒,胜过虚度中的一月一年!

小做个动图开篇引题


懒洋洋.gif


前言

目前较火的网络框架有MVP+Retrofit2+okhttp3+Rxjava2,于是也加入了使用行列,本框架为Retrofit基本写法及特殊情况处理衍生,为大家学习使用提供帮助,本次优化对使用过程中所遇到问题进行总结,基本满足实际开发需求,有不足地方我将继续完善



相关业务需求及解决方案
一、 MVP+Retrofit2+okhttp3+Rxjava2框架基本搭建及使用
二、BaseActivityBaseFragment封装协调框架更好使用
三、Android部分手机4G网第一次请求很慢(wifi正常)解决方案
四、Retrofit运行时动态改变BaseUrl解决方案
五、Retrofit文件上传(本片文章介绍中包含进度条)
六、 Retrofit文件下载(含进度条)
七、Retrofit,Gson解析,请求返回的类型不统一,假如double返回的是null
八、Retrofit实现cookie自动化管理
九、路由判断第二种解决方案(文章为旧版,提供思路)
十、Retrofit配置及各情况处理(缓存拦截、日志打印、替换接口内容、参数添加等)
十一、 后记
十二、 本文譩在一篇文章搞定所有,上述描述文章都有讲解


一、MVP+Retrofit2+okhttp3+Rxjava2框架基本搭建

1、 相关依赖引用
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    //ConverterFactory的Gson依赖包
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
    //CallAdapterFactory的Rx依赖包
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
    //cookie管理
    implementation 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
    //日志
    implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
2、 创建接口类ApiServer,定义接口方法
public interface ApiServer {
    @FormUrlEncoded
    @POST("/api/table_list/")
    Observable<BaseModel<Object>> getCeShi(@FieldMap HashMap<String, String> params);
}
3、 创建Retrofit
public class ApiRetrofit {
    private static ApiRetrofit mApiRetrofit;
    private Retrofit retrofit;
    private ApiServer apiServer;
    private static final int DEFAULT_TIMEOUT = 15;
    public static String mBaseUrl = BaseContent.baseUrl;
    public ApiRetrofit() {
        OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        httpClientBuilder
                .cookieJar(new CookieManger(App.getContext()))
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
        retrofit = new Retrofit.Builder()
                .baseUrl(mBaseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                //支持RxJava2
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(httpClientBuilder.build())
                .build();
        apiServer = retrofit.create(ApiServer.class);
    }
    public static ApiRetrofit getInstance() {
        if (mApiRetrofit == null) {
            synchronized (Object.class) {
                if (mApiRetrofit == null) {
                    mApiRetrofit = new ApiRetrofit();
                }
            }
        }
        return mApiRetrofit;
    }
    public ApiServer getApiService() {
        return apiServer;
    }
}
4、 定义常用的接口,如网络请求开始,结束,进度条加载,错误码等
public interface BaseView {
    //显示dialog
    void showLoading();
    //隐藏 dialog
    void hideLoading();
    //显示错误信息
    void showError(String msg);
    //错误码
    void onErrorCode(BaseModel model);
    //进度条显示
    void showProgress();
    //进度条隐藏
    void hideProgress();
    //文件下载进度监听
    void onProgress(int progress);
}
5、 BaseModel封装

封装理由:一个项目一般情况下json返回格式外层都是统一的

public class BaseModel<T> implements Serializable {
    private String reason;
    private int error_code;
    private T result;
    public BaseModel(String reason, int error_code) {
        this.reason = reason;
        this.error_code = error_code;
    }
    public String getReason() {
        return reason;
    }
    public void setReason(String reason) {
        this.reason = reason;
    }
    public int getError_code() {
        return error_code;
    }
    public void setError_code(int error_code) {
        this.error_code = error_code;
    }
    public T getResult() {
        return result;
    }
    public void setResult(T result) {
        this.result = result;
    }
}
6、 BasePresenter封装,协调m层v层的中间信使通用代码封装
public class BasePresenter<V extends BaseView> {
    private CompositeDisposable compositeDisposable;
    public V baseView;
    protected ApiServer apiServer = ApiRetrofit.getInstance().getApiService();
    public BasePresenter(V baseView) {
        this.baseView = baseView;
    }
    /**
     * 解除绑定
     */
    public void detachView() {
        baseView = null;
        removeDisposable();
    }
    /**
     * 返回 view
     *
     * @return
     */
    public V getBaseView() {
        return baseView;
    }
    public void addDisposable(Observable<?> observable, BaseObserver observer) {
        if (compositeDisposable == null) {
            compositeDisposable = new CompositeDisposable();
        }
        compositeDisposable.add(observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(observer));
    }
    public void addFileDisposable(Observable<?> observable, FileObserver observer) {
        if (compositeDisposable == null) {
            compositeDisposable = new CompositeDisposable();
        }
        compositeDisposable.add(observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(observer));
    }
    public void removeDisposable() {
        if (compositeDisposable != null) {
            compositeDisposable.dispose();
        }
    }
}
6、 BaseObserver封装,数据等异常处理路由

封装理由:处理业务逻辑路由,与错误信息处理

注:在正常开发中,前后台会约定相关字段如code的值代表各情况,在此路由通道,另一种路由方案是重写Gson解析类,文章不做体现,demo中有相关代码

public abstract class BaseObserver<T> extends DisposableObserver<BaseModel<T>> {
    protected BaseView view;
    /**
     * 网络连接失败  无网
     */
    public static final int NETWORK_ERROR = 100000;
    /**
     * 解析数据失败
     */
    public static final int PARSE_ERROR = 1008;
    /**
     * 网络问题
     */
    public static final int BAD_NETWORK = 1007;
    /**
     * 连接错误
     */
    public static final int CONNECT_ERROR = 1006;
    /**
     * 连接超时
     */
    public static final int CONNECT_TIMEOUT = 1005;
    /**
     * 其他所有情况
     */
    public static final int NOT_TRUE_OVER = 1004;
    public BaseObserver(BaseView view) {
        this.view = view;
    }
    public BaseObserver() {
    }
    @Override
    protected void onStart() {
        if (view != null) {
            view.showLoading();
        }
    }
    @Override
    public void onNext(BaseModel<T> o) {
//        T t = o.getData();
        try {
            if (view != null) {
                view.hideLoading();
            }
            if (o.getError_code() == BaseContent.basecode) {
                onSuccess(o);
            } else {
                if (view != null) {
                    view.onErrorCode(o);
                }
                //非  true的所有情况
                onException(PARSE_ERROR, o.getReason());
            }
        } catch (Exception e) {
            e.printStackTrace();
            onError(e.toString());
        }
    }
    @Override
    public void onError(Throwable e) {
        if (view != null) {
            view.hideLoading();
        }
        if (e instanceof HttpException) {
            //   HTTP错误
            onException(BAD_NETWORK, "");
        } else if (e instanceof ConnectException
                || e instanceof UnknownHostException) {
            //   连接错误
            onException(CONNECT_ERROR, "");
        } else if (e instanceof InterruptedIOException) {
            //  连接超时
            onException(CONNECT_TIMEOUT, "");
        } else if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException) {
            //  解析错误
            onException(PARSE_ERROR, "");
            e.printStackTrace();
        } else {
            if (e != null) {
                onError(e.toString());
            } else {
                onError("未知错误");
            }
        }
    }
    private void onException(int unknownError, String message) {
        switch (unknownError) {
            case CONNECT_ERROR:
                onError("连接错误");
                break;
            case CONNECT_TIMEOUT:
                onError("连接超时");
                break;
            case BAD_NETWORK:
                onError("网络超时");
                break;
            case PARSE_ERROR:
                onError("数据解析失败");
                break;
            //非true的所有情况
            case NOT_TRUE_OVER:
                onError(message);
                break;
            default:
                break;
        }
    }
    //消失写到这 有一定的延迟  对dialog显示有影响
    @Override
    public void onComplete() {
       /* if (view != null) {
            view.hideLoading();
        }*/
    }
    public abstract void onSuccess(BaseModel<T> o);
    public abstract void onError(String msg);
}

如上,相关框架已封装完毕,下面看下如何使用

8、 定义MainView,并继承BaseView
public interface MainView extends BaseView {
    void onTextSuccess(BaseModel<TextBean> o);
}
9、 定义MainPresenter,并继承BasePresenter
public class MainPresenter extends BasePresenter<MainView> {
    public MainPresenter(MainView baseView) {
        super(baseView);
    }
    /**
     * 写法好多种  怎么顺手怎么来
     */
    public void getTextApi() {
        HashMap<String, String> params = new HashMap<>();
        params.put("type", "junshi");
        params.put("key", "2c1cb93f8c7430a754bc3ad62e0fac06");
        addDisposable(apiServer.getText(params), new BaseObserver(baseView) {
            @Override
            public void onSuccess(BaseModel o) {
                baseView.onTextSuccess((BaseModel<TextBean>) o);
            }
            @Override
            public void onError(String msg) {
                if (baseView != null) {
                    baseView.showError(msg);
                }
            }
        });
    }
}
10、 在Activity中进行网络请求,如下
public class MainActivity extends AppCompatActivity implements MainView {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MainPresenter presenter = new MainPresenter(this);
        //网络请求
        presenter.getTextApi();
    }
    @Override
    public void onTextSuccess(BaseModel<TextBean> o) {
    //我是网络请求成功后的结果
    }
    @Override
    public void showLoading() {
    //网络开始请求时我会执行
    }
    @Override
    public void hideLoading() {
    //网络请求完毕时我会执行
    }
    @Override
    public void showError(String msg) {
    //异常情况下我会提示内容
    }
    @Override
    public void onErrorCode(BaseModel model) {
    //在异常时候我会回调
    }
    @Override
    public void showProgress() {
    //需要显示进度条时候我是开始标识
    }
    @Override
    public void hideProgress() {
    //需要隐藏进度条时候我是结束标识
    }
    @Override
    public void onProgress(int progress) {
    //进度条最主要的是我
    }
}

第一章结束(MVP+Retrofit2+okhttp3+Rxjava2框架基本搭建及使用)

activity内容太多了,阅读性差,引发了强烈的需求对Activity封装与Fragment封装,请往下看

二、对BaseActivity、BaseFragment封装协调框架更好使用

浅谈BaseActivity写法,促使我们更高效开发

Fragment懒加载实现,BaseFragment封装

有兴趣的撸友们可以转战我其他俩篇文章,本文意在一篇掌握网络请求,如下继续介绍如何封装

1、 BaseActivity相关内容进行封装,BaseFragment可到demo中查看
public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements BaseView {
    protected final String TAG = this.getClass().getSimpleName();
    public Context mContext;
    protected P mPresenter;
    protected abstract P createPresenter();
    private LoadingDialog loadingDialog;
    private ProgressDialog progressDialog;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = this;
        setContentView(getLayoutId());
        mPresenter = createPresenter();
        setStatusBar();
        this.initData();
    }
    /**
     * 获取布局ID
     *
     * @return
     */
    protected abstract int getLayoutId();
    /**
     * 数据初始化操作
     */
    protected abstract void initData();
    /**
     * 此处设置沉浸式地方
     */
    protected void setStatusBar() {
        StatusBarUtil.setTranslucentForImageViewInFragment(this, 0, null);
    }
    /**
     * 封装toast方法(自行定制实现)
     *
     * @param str
     */
    public void showToast(String str) {
        ToastUtils.show(str);
    }
    public void showLongToast(String str) {
        ToastUtils.show(str);
    }
    @Override
    public void showError(String msg) {
        showToast(msg);
    }
    /**
     * 返回所有状态  除去指定的值  可设置所有(根据需求)
     *
     * @param model
     */
    @Override
    public void onErrorCode(BaseModel model) {
        if (model.getError_code() == 10000000) {
            //处理些后续逻辑   如果某个页面不想实现  子类重写这个方法  将super去掉  自定义方法
//            App.put();
//            startActivity(LoginActivity.class);
        }
    }
    @Override
    public void showLoading() {
        showLoadingDialog();
    }
    @Override
    public void hideLoading() {
        dissMissDialog();
    }
    public void showLoadingDialog() {
        showLoadingDialog("加载中...");
    }
    /**
     * 加载  黑框...
     */
    public void showLoadingDialog(String msg) {
        if (loadingDialog == null) {
            loadingDialog = new LoadingDialog(this);
        }
        loadingDialog.setMessage(msg);
        if (!loadingDialog.isShowing()) {
            loadingDialog.show();
        }
    }
    /**
     * 消失  黑框...
     */
    public void dissMissDialog() {
        if (loadingDialog != null) {
            loadingDialog.dismiss();
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.detachView();
        }
        if (loadingDialog != null) {
            loadingDialog.dismiss();
        }
        if (progressDialog != null) {
            progressDialog.dismiss();
        }
        if (mPresenter != null) {
            mPresenter.detachView();
        }
    }
    /**
     * 进度条显示
     */
    @Override
    public void showProgress() {
        if (progressDialog == null) {
            progressDialog = new ProgressDialog(this);
        }
        progressDialog.getProgressBar().performAnimation();
        if (!progressDialog.isShowing()) {
            progressDialog.show();
        }
    }
    /**
     * 进度条隐藏
     */
    @Override
    public void hideProgress() {
        if (progressDialog != null) {
            progressDialog.getProgressBar().releaseAnimation();
            progressDialog.dismiss();
        }
    }
    /**
     * 进度条 回调
     * @param progress
     */
    @Override
    public void onProgress(int progress) {
        if (progressDialog != null) {
            progressDialog.updateProgress(progress);
        }
    }
}
2、 定义MainView,并继承BaseView(同上)
3、 定义MainPresenter,并继承BasePresenter(同上)
4、 在Activity中进行网络请求,如下
public class MainActivity extends BaseActivity<MainPresenter> implements MainView {
    @Override
    protected MainPresenter createPresenter() {
        return new MainPresenter(this);
    }
    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }
    @Override
    protected void initData() {
        //网络请求
        mPresenter.getTextApi();
    }
    @Override
    public void onTextSuccess(BaseModel<TextBean> o) {
        //我是网络请求成功后的结果
    }
}

三、Android部分手机4G网第一次请求很慢(wifi正常)解决方案

Android部分手机4G网第一次请求很慢(wifi正常)解决方案

1、出现此类问题场景

经测试,一般手机都没有发现网络请求慢现象,只有部分手机会出现,如(小米手机)

2、出现此类问题现象

手机4G网网络请求特别慢,第一次进入app加载网络会出现30s+延迟现象,只有第一次慢,第二次网络访问回归正常,但重新进入又会出现网络延迟30s+

3、出现此类问题排查

通过网上查阅资料,都趋向于ipv4、ipv6地址问题,经对应手机测试发现,DNS 解析的 IP 地址①.连接到wifi,只解析到 ipv4 地址,②.连接到4G网,解析到了ipv4、ipv6俩个地址,但是ipv6默认为集合中的第一个,是否我们可以尝试修改集合第一个为ipv4呢?

4、出现此类问题解决方案
解决方案:集合中ipv4,ipv6调换位置,将ipv4当到集合首位
调换集合中ipv4 ipv6位置,将ipv4当到集合首位
import okhttp3.Dns;
public class ApiDns implements Dns {
    @Override
    public List<InetAddress> lookup(String hostname) throws UnknownHostException {
        if (hostname == null) {
            throw new UnknownHostException("hostname == null");
        } else {
            try {
                List<InetAddress> mInetAddressesList = new ArrayList<>();
                InetAddress[] mInetAddresses = InetAddress.getAllByName(hostname);
                for (InetAddress address : mInetAddresses) {
                    if (address instanceof Inet4Address) {
                        mInetAddressesList.add(0, address);
                    } else {
                        mInetAddressesList.add(address);
                    }
                }
                return mInetAddressesList;
            } catch (NullPointerException var4) {
                UnknownHostException unknownHostException = new UnknownHostException("Broken system behaviour");
                unknownHostException.initCause(var4);
                throw unknownHostException;
            }
        }
    }
}
第二步,将自定义方法插入到okhttp中
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        ClearableCookieJar cookieJar =
                new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(AppUMS.mContent));
        httpClientBuilder
                .cookieJar(cookieJar)
                .addInterceptor(interceptor)
                .addInterceptor(new HeadUrlInterceptor())
                //设置请求超时时长
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .dns(new ApiDns());//添加如下方法

四、Retrofit运行时动态改变BaseUrl解决方案

Retrofit运行时动态改变BaseUrl解决方案

1、出现此类问题场景

Android正式项目中可能会涉及到多个BaseUrl,使用Retrofit开发者可能会遇到多BaseUrl不是很好处理情况

2、第一种解决方案

简单粗暴解决方案,利用Retrofit请求优先级,因为Retrofit支持全路径,比如

@GET("http://www.baidu.com")
 Observable<Object> getApi(@Path("param") String param);
3、第二种解决方案

Retrofit默认只能设置一个BaseUrl,没有提供其Api去修改,所以我们只能通过其他方案去实现,网上也有很多介绍的,但尝试用了下感觉很不理想,于是自己稍加封装了下,思路其实简单。

思路:一个Retrofit只能设置一个BaseUrl,这样我们可以创建多个Retrofit不就可以了吗?但如果一个请求创建一个Retrofit必然是不理想的,所以我们可以有几个BaseUrl创建几个,有人会说这样不会造成内存的开销吗?答案是不会的,一个项目中也不会出现N多个BaseUrl,所以这点开销不用过于纠结

代码实现:在代码设计时可以尽可能去优化,所以当我们用到此BaseUrl时,再去创建,用不到不创建,这样便会出现个问题,怎样知道我应该使用哪个RetrofitRetrofit怎么去保存等问题,本人思路是创建成功便添加到集合缓存下载,使用的时候去比对集合中BaseUrl和当前是否匹配,如果一致从集合中获取,如果不一致去创建新的,如果使用没有传入BaseUrl便用默认的,最基本的判断,实现代码如下

4、正常创建Retrofit
public class ApiRetrofit {
    private static ApiRetrofit mApiRetrofit;
    private Retrofit retrofit;
    private ApiServer apiServer;
    public static String mBaseUrl = BaseContent.baseUrl;
    public ApiRetrofit() {
        OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        httpClientBuilder
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true);//错误重联
        retrofit = new Retrofit.Builder()
                .baseUrl(mBaseUrl )
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(httpClientBuilder.build())
                .build();
        apiServer = retrofit.create(ApiServer.class);
    }
    public static ApiRetrofit getInstance() {
        if (mApiRetrofit == null) {
            synchronized (Object.class) {
                if (mApiRetrofit == null) {
                    mApiRetrofit = new ApiRetrofit();
                }
            }
        }
        return mApiRetrofit;
    }
}
5、对创建Retrofit稍加封装,已适应我们的需求

新建保存对象的集合

private static List<Retrofit> mRetrofitList = new ArrayList<>();
private static List<ApiRetrofit> mApiRetrofitList = new ArrayList<>();

修改创建时候的逻辑,如果请求接口时传入BaseUrl,检测BaseUrl是否为空,如果为空使用默认接口,如果不为空,再从缓存的Retrofit中查找是否已经才创建过了,如果创建了用缓存的,如果没有创建则创建

注:这块可以用正则检查下传入的url是否为正规的域名,再做下判断

//创建Retrofit代码中加入
 apiServer = retrofit.create(ApiServer.class);
 mRetrofitList.add(retrofit);
public static ApiRetrofit getInstance() {
        mBaseUrl = BaseContent.baseUrl;
        int mIndex = -1;
        for (int i = 0; i < mRetrofitList.size(); i++) {
            if (BaseContent.baseUrl.equals(mRetrofitList.get(i).baseUrl().toString())) {
                mIndex = i;
                break;
            }
        }
        //新的baseUrl
        if (mIndex == -1) {
            synchronized (Object.class) {
                mApiRetrofit = new ApiRetrofit();
                mApiRetrofitList.add(mApiRetrofit);
                return mApiRetrofit;
            }
        } else {
            //以前已经创建过的baseUrl
            return mApiRetrofitList.get(mIndex);
        }
    }
    public static ApiRetrofit getInstance(String baseUrl) {
        if (!TextUtils.isEmpty(baseUrl)) {
            mBaseUrl = baseUrl;
        } else {
            mBaseUrl = BaseContent.baseUrl;
        }
        int mIndex = -1;
        for (int i = 0; i < mRetrofitList.size(); i++) {
            if (baseUrl.equals(mRetrofitList.get(i).baseUrl().toString())) {
                mIndex = i;
                break;
            }
        }
        //新的baseUrl
        if (mIndex == -1) {
            synchronized (Object.class) {
                mApiRetrofit = new ApiRetrofit();
                mApiRetrofitList.add(mApiRetrofit);
                return mApiRetrofit;
            }
        } else {
            //以前已经创建过的baseUrl
            return mApiRetrofitList.get(mIndex);
        }
    }
6、使用时写法

地址可以写成常量,不要我这样写,写成常量判断准确

ApiRetrofit.getInstance("http://www.baidu.com/").getApiService().getCeShi(params)

五、Retrofit文件上传(含进度条)

Retrofit文件上传

文件上传已封装到框架中,目的是让写法更简便

1、 FileObserver封装,文件上传下载时所用

上述文章有BaseObserver,现封装FileObserver单独用来文件上传下载时候所用,内容大同小异

public abstract class FileObserver<T> extends DisposableObserver<T> {
    protected BaseView view;
    /**
     * 网络连接失败  无网
     */
    public static final int NETWORK_ERROR = 100000;
    /**
     * 解析数据失败
     */
    public static final int PARSE_ERROR = 1008;
    /**
     * 网络问题
     */
    public static final int BAD_NETWORK = 1007;
    /**
     * 连接错误
     */
    public static final int CONNECT_ERROR = 1006;
    /**
     * 连接超时
     */
    public static final int CONNECT_TIMEOUT = 1005;
    /**
     * 其他所有情况
     */
    public static final int NOT_TRUE_OVER = 1004;
    public FileObserver(BaseView view) {
        this.view = view;
    }
    @Override
    protected void onStart() {
        if (view != null) {
            view.showProgress();
        }
    }
    @Override
    public void onNext(T t) {
        onSuccess(t);
    }
    @Override
    public void onError(Throwable e) {
        if (view != null) {
            view.hideProgress();
        }
        if (view != null) {
            view.hideLoading();
        }
        if (e instanceof HttpException) {
            //   HTTP错误
            onException(BAD_NETWORK, "");
        } else if (e instanceof ConnectException
                || e instanceof UnknownHostException) {
            //   连接错误
            onException(CONNECT_ERROR, "");
        } else if (e instanceof InterruptedIOException) {
            //  连接超时
            onException(CONNECT_TIMEOUT, "");
        } else if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException) {
            //  解析错误
            onException(PARSE_ERROR, "");
            e.printStackTrace();
        } else {
            if (e != null) {
                onError(e.toString());
            } else {
                onError("未知错误");
            }
        }
    }
    private void onException(int unknownError, String message) {
        switch (unknownError) {
            case CONNECT_ERROR:
                onError("连接错误");
                break;
            case CONNECT_TIMEOUT:
                onError("连接超时");
                break;
            case BAD_NETWORK:
                onError("网络超时");
                break;
            case PARSE_ERROR:
                onError("数据解析失败");
                break;
            //非true的所有情况
            case NOT_TRUE_OVER:
                onError(message);
                break;
            default:
                break;
        }
    }
    @Override
    public void onComplete() {
        if (view != null) {
            view.hideProgress();
        }
    }
    public abstract void onSuccess(T o);
    public abstract void onError(String msg);
}
2、定义接口
public interface ApiServer {
    @Multipart
    @POST("/wxapp/public/upload")
    Observable<BaseModel<Object>> getUpload(@PartMap Map<String, RequestBody> map,
                                            @Part MultipartBody.Part parts
    );
}
3、 定义MainView,并继承BaseView
public interface MainView extends BaseView {
    void onUpLoadImgSuccess(BaseModel<Object> o);
}
4、 定义ProgressRequestBody,监听上传进度
public class ProgressRequestBody extends RequestBody {
    private File mFile;
    private String mPath;
    private String mMediaType;
    private BaseView mListener;
    private int mEachBufferSize = 1024;
    public ProgressRequestBody(final File file, String mediaType, BaseView baseView) {
        mFile = file;
        mMediaType = mediaType;
        mListener = baseView;
    }
    public ProgressRequestBody(final File file, String mediaType, int eachBufferSize, BaseView baseView) {
        mFile = file;
        mMediaType = mediaType;
        mEachBufferSize = eachBufferSize;
        mListener = baseView;
    }
    @Override
    public MediaType contentType() {
        // i want to upload only images
        return MediaType.parse(mMediaType);
    }
    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        long fileLength = mFile.length();
        byte[] buffer = new byte[mEachBufferSize];
        FileInputStream in = new FileInputStream(mFile);
        long uploaded = 0;
        try {
            int read;
            Handler handler = new Handler(Looper.getMainLooper());
            while ((read = in.read(buffer)) != -1) {
                // update progress on UI thread
                handler.post(new ProgressUpdater(uploaded, fileLength));
                uploaded += read;
                sink.write(buffer, 0, read);
            }
        } finally {
            in.close();
        }
    }
    private class ProgressUpdater implements Runnable {
        private long mUploaded;
        private long mTotal;
        public ProgressUpdater(long uploaded, long total) {
            mUploaded = uploaded;
            mTotal = total;
        }
        @Override
        public void run() {
            mListener.onProgress((int) (100 * mUploaded / mTotal));
        }
    }
}

相关文章
|
25天前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
100 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
3天前
|
API
鸿蒙开发:切换至基于rcp的网络请求
本文的内容主要是把之前基于http封装的库,修改为当前的Remote Communication Kit(远场通信服务),无非就是通信的方式变了,其他都大差不差。
鸿蒙开发:切换至基于rcp的网络请求
|
1月前
|
XML 开发工具 Android开发
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
ExoPlayer最初是为了解决Android早期MediaPlayer控件对网络视频兼容性差的问题而推出的。现在,Android官方已将其升级并纳入Jetpack的Media3库,使其成为音视频操作的统一引擎。新版ExoPlayer支持多种协议,解决了设备和系统碎片化问题,可在整个Android生态中一致运行。通过修改`build.gradle`文件、布局文件及Activity代码,并添加必要的权限,即可集成并使用ExoPlayer进行网络视频播放。具体步骤包括引入依赖库、配置播放界面、编写播放逻辑以及添加互联网访问权限。
129 1
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
|
3月前
|
安全 网络安全 Android开发
安卓与iOS开发:选择的艺术网络安全与信息安全:漏洞、加密与意识的交织
【8月更文挑战第20天】在数字时代,安卓和iOS两大平台如同两座巍峨的山峰,分别占据着移动互联网的半壁江山。它们各自拥有独特的魅力和优势,吸引着无数开发者投身其中。本文将探讨这两个平台的特点、优势以及它们在移动应用开发中的地位,帮助读者更好地理解这两个平台的差异,并为那些正在面临选择的开发者提供一些启示。
127 56
|
3月前
|
C++
C++ Qt开发:QUdpSocket网络通信组件
QUdpSocket是Qt网络编程中一个非常有用的组件,它提供了在UDP协议下进行数据发送和接收的能力。通过简单的方法和信号,可以轻松实现基于UDP的网络通信。不过,需要注意的是,UDP协议本身不保证数据的可靠传输,因此在使用QUdpSocket时,可能需要在应用层实现一些机制来保证数据的完整性和顺序,或者选择在适用的场景下使用UDP协议。
142 2
|
3月前
|
小程序 数据安全/隐私保护
Taro@3.x+Vue@3.x+TS开发微信小程序,网络请求封装
在 `src/http` 目录下创建 `request.ts` 文件,并配置 Taro 的网络请求方法 `Taro.request`,支持多种 HTTP 方法并处理数据加密。
120 0
Taro@3.x+Vue@3.x+TS开发微信小程序,网络请求封装
|
3月前
|
数据采集 存储 前端开发
豆瓣评分9.0!Python3网络爬虫开发实战,堪称教学典范!
今天我们所处的时代是信息化时代,是数据驱动的人工智能时代。在人工智能、物联网时代,万物互联和物理世界的全面数字化使得人工智能可以基于这些数据产生优质的决策,从而对人类的生产生活产生巨大价值。 在这个以数据驱动为特征的时代,数据是最基础的。数据既可以通过研发产品获得,也可以通过爬虫采集公开数据获得,因此爬虫技术在这个快速发展的时代就显得尤为重要,高端爬虫人才的收人也在逐年提高。
|
3月前
|
网络协议 容器
Qt开发网络嗅探器03
Qt开发网络嗅探器03
|
3月前
|
安全 网络安全 数据安全/隐私保护
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享安卓与iOS开发中的线程管理比较
【8月更文挑战第30天】本文将探讨网络安全与信息安全的重要性,并分享关于网络安全漏洞、加密技术和安全意识的知识。我们将了解常见的网络攻击类型和防御策略,以及如何通过加密技术和提高安全意识来保护个人和组织的信息安全。
|
3月前
|
安全 网络安全 Android开发
探索安卓开发之旅:从新手到专家网络安全与信息安全:防范网络威胁,保护数据安全
【8月更文挑战第29天】在这篇技术性文章中,我们将踏上一段激动人心的旅程,探索安卓开发的世界。无论你是刚开始接触编程的新手,还是希望提升技能的资深开发者,这篇文章都将为你提供宝贵的知识和指导。我们将从基础概念入手,逐步深入到安卓开发的高级主题,包括UI设计、数据存储、网络通信等方面。通过阅读本文,你将获得一个全面的安卓开发知识体系,并学会如何将这些知识应用到实际项目中。让我们一起开启这段探索之旅吧!

热门文章

最新文章