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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 打造终极MVP+Retrofit2+okhttp3+Rxjava2网络请求,开发实用,简约
5、 定义MainPresenter,并继承BasePresenter
public class MainPresenter extends BasePresenter<MainView> {
    public MainPresenter(MainView baseView) {
        super(baseView);
    }
    /**
     * 演示 文件上传进度监听
     *
     * @param url
     */
    public void upLoadVideoApi(String url) {
        HashMap<String, RequestBody> params = new HashMap<>();
        params.put("fileType", RetrofitUtil.convertToRequestBody("video"));
        MultipartBody.Part parts = MultipartBody.Part.createFormData("file", new File(url).getName(), new ProgressRequestBody(new File(url),"video/mpeg", baseView));
        ApiServer apiServer = ApiRetrofit.getBaseUrlInstance("https://bjlzbt.com/").getApiService();
        addFileDisposable(apiServer.getUpload(params, parts), new FileObserver(baseView) {
            @Override
            public void onSuccess(Object o) {
                baseView.onUpLoadImgSuccess((BaseModel<Object>) o);
            }
            @Override
            public void onError(String msg) {
                if (baseView != null) {
                    baseView.showError(msg);
                }
            }
        });
    }
}
6、 在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.upLoadVideoApi(BaseContent.baseFileName+"ceshi.mp4");
    }
   @Override
    public void onUpLoadImgSuccess(BaseModel<Object> o) {
        L.e("文件视频路径==" + o.getResult());
    }
}
7、 有人会问,说好的进度条去哪了?

进度条已封装到BaseActivity中了,相关代码如下

@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();
        }
    }
    @Override
    public void onProgress(int progress) {
        if (progressDialog != null) {
            progressDialog.updateProgress(progress);
        }
    }

六、Retrofit文件下载(含进度条)

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

如上

2、定义接口
public interface ApiServer {
     /**
     * 大文件官方建议用 @Streaming 来进行注解,不然会出现IO异常,小文件可以忽略不注入
     */
    @Streaming
    @GET
    Observable<ResponseBody> downloadFile(@Url String fileUrl);
}
3、 定义MainView,并继承BaseView
public interface MainView extends BaseView {
     void onFileSuccess(File file);
}
4、 定义MainPresenter,并继承BasePresenter
public class MainPresenter extends BasePresenter<MainView> {
    public MainPresenter(MainView baseView) {
        super(baseView);
   public void downFile(String url, final String destFileDir, final String destFileName) {
        ApiServer apiServer = ApiRetrofit.getFileInstance(baseView).getApiService();
        Observable<String> observable = apiServer.downloadFile(url).map(new Function<ResponseBody, String>() {
            @Override
            public String apply(ResponseBody body) throws Exception {
                File file = FileUtil.saveFile(destFileDir+destFileName, body);
                return file.getPath();
            }
        });
        addFileDisposable(observable, new FileObserver(baseView) {
            @Override
            public void onSuccess(Object o) {
                baseView.onFileSuccess(new File(o.toString()));
            }
            @Override
            public void onError(String msg) {
                if (baseView != null) {
                    baseView.showError(msg);
                }
            }
        });
    }
    }
}
5、创建Retrofit来实现进度条

说明:本人思路是通过okhttp拦截器拦截来检测文件下载进度,相关代码已放入到创建Retrofit中,详情请看Demo,demo封装为只有文件下载okhttp才会添加ProgressInterceptor下载进度监听,如下所示(okhttp添加)

/**
     * 文件处理
     *
     * @param httpClientBuilder
     */
    public void initFileClient(OkHttpClient.Builder httpClientBuilder) {
        /**
         * 处理文件下载进度展示所需
         */
        httpClientBuilder.addNetworkInterceptor(new ProgressInterceptor());
    }
/**
     * 文件下载进度拦截
     */
    public class ProgressInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            if (mBaseView != null) {
                Response response = chain.proceed(request);
                return response.newBuilder().body(new ProgressResponseBody(response.body(),
                        new ProgressResponseBody.ProgressListener() {
                            @Override
                            public void onProgress(long totalSize, long downSize) {
                                int progress = (int) (downSize * 100 / totalSize);
                                if (mBaseView != null) {
                                    mBaseView.onProgress(progress);
                                    L.e("文件下载速度 === " + progress);
                                }
                            }
                        })).build();
            } else {
                return chain.proceed(request);
            }
        }
    }
6、 在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() {
        //文件上传
         String url = "https://bjlzbt.com/upload/default//20190725//c13948258c6ef6a36cbe2d3322b98f5c.mp4";
         if (FileUtils.createOrExistsDir(BaseContent.baseFileName)) {//删除此行代码也可以
             mPresenter.downFile(url, BaseContent.baseFileName, "ceshi.mp4");
         }
    }
   @Override
    public void onUpLoadImgSuccess(BaseModel<Object> o) {
        L.e("文件视频路径==" + o.getResult());
    }
}

七、Retrofit,Gson解析,请求返回的类型不统一,假如double返回的是null

对应文章解析

现实开发中,往往会遇到后台返回数据格式不规范情况,比如前端字段原本定义为int类型,而数据返回为空,如果用Gson解析会导致解析失败,比如字段定义为double类型,而返回的格式为字符串null,导致解析失败等等(只在后台返回数据格式不规范情况下出现,如果后台返回格式规范并不用考虑此问题)

1、 实现目标

1、格式化数据不规范【格式化int类型数据】

2、格式化数据不规范【格式化Long类型数据】

3、格式化数据不规范【格式化Double类型数据】

4、格式化数据不规范【格式化String类型数据】

5、格式化数据不规范【格式化Null类型数据】

2、 添加格式化工具方法到Gson解析中
if (gson == null) {
            gson = new GsonBuilder()
                    .registerTypeAdapter(Integer.class, new IntegerDefaultAdapter())
                    .registerTypeAdapter(int.class, new IntegerDefaultAdapter())
                    .registerTypeAdapter(Double.class, new DoubleDefaultAdapter())
                    .registerTypeAdapter(double.class, new DoubleDefaultAdapter())
                    .registerTypeAdapter(Long.class, new LongDefaultAdapter())
                    .registerTypeAdapter(long.class, new LongDefaultAdapter())
                    .registerTypeAdapter(String.class, new StringNullAdapter())
                    .create();
        }
        return gson;
    }
 public ApiRetrofit() {
        OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        httpClientBuilder
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true);//错误重联
        retrofit = new Retrofit.Builder()
                .baseUrl(BASE_SERVER_URL)
                .addConverterFactory(GsonConverterFactory.create(buildGson()))//添加json转换框架buildGson()根据需求添加
                //支持RxJava2
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(httpClientBuilder.build())
                .build();
        apiServer = retrofit.create(ApiServer.class);
    }
3、 对double类型处理,返回“”,或“null”,动态更改为默认值0.00,新建DoubleDefaultAdapter类
public class DoubleDefault0Adapter implements JsonSerializer<Double>, JsonDeserializer<Double> {
    @Override
    public Double deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        try {
            if (json.getAsString().equals("") || json.getAsString().equals("null")) {//定义为double类型,如果后台返回""或者null,则返回0.00
                return 0.00;
            }
        } catch (Exception ignore) {
        }
        try {
            return json.getAsDouble();
        } catch (NumberFormatException e) {
            throw new JsonSyntaxException(e);
        }
    }
    @Override
    public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(src);
    }
}
4、 对int类型处理,返回“”,或“null”,动态更改为默认值0,新建DoubleDefaultAdapter类
public class IntegerDefaultAdapter implements JsonSerializer<Integer>, JsonDeserializer<Integer> {
    @Override
    public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        try {
            if (json.getAsString().equals("") || json.getAsString().equals("null")) {//定义为int类型,如果后台返回""或者null,则返回0
                return 0;
            }
        } catch (Exception ignore) {
        }
        try {
            return json.getAsInt();
        } catch (NumberFormatException e) {
            throw new JsonSyntaxException(e);
        }
    }
    @Override
    public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(src);
    }
}
5、 对Long类型处理,返回“”,或“null”,动态更改为默认值0,新建DoubleDefaultAdapter类
public class LongDefault0Adapter implements JsonSerializer<Long>, JsonDeserializer<Long> {
    @Override
    public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        try {
            if (json.getAsString().equals("") || json.getAsString().equals("null")) {//定义为long类型,如果后台返回""或者null,则返回0
                return 0l;
            }
        } catch (Exception ignore) {
        }
        try {
            return json.getAsLong();
        } catch (NumberFormatException e) {
            throw new JsonSyntaxException(e);
        }
    }
    @Override
    public JsonElement serialize(Long src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(src);
    }
}
5、 重点说一下String类型

根据上边其他类型处理代码可以看出,String也就是把上述类中代码改成String就可以了,答案是可以的,如下,处理的内容为如果服务器返回字符串类型“null”,我们将其格式化成“”,空类型,但是我们为什么不直接写,请往下看

public class StringDefaultConverter implements JsonSerializer<String>, JsonDeserializer<String> {
    @Override
    public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        try {
            if (json.getAsString().equals("null")) {
                return "";
            }
        } catch (Exception ignore) {
        }
        try {
            return json.getAsJsonPrimitive().getAsString();
        } catch (NumberFormatException e) {
            throw new JsonSyntaxException(e);
        }
    }
    @Override
    public JsonElement serialize(String src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(src);
    }
}

但是有种比较常见的不规范数据返回,为null,不是字符串的"null",是这个null,如果返回null,会进入到上边这个类吗,经过测试,返回null的直接跳过,所以出现了个问题,null到底是什么类型?

通过读源码可知,我们可以自定义TypeAdapter,将其放入facotries中,并且gson在解析json时使用对应的TypeAdapter来的,而我们手动添加的TypeAdapter会优先于预设的TypeAdapter被使用。

于是乎找到了一种其他方法来解决这个问题

新建个类来集成TypeAdapter,这样就便优先于预设的TypeAdapter

public class StringNullAdapter extends TypeAdapter<String> {
    @Override
    public String read(JsonReader reader) throws IOException {
        if (reader.peek() == JsonToken.NULL) {
            reader.nextNull();
            return "";//原先是返回Null,这里改为返回空字符串
        }
        String jsonStr = reader.nextString();
        if(jsonStr.equals("null")) {
            return "";
        }else {
            return jsonStr;
        }
    }
    @Override
    public void write(JsonWriter writer, String value) throws IOException {
        if (value == null) {
            writer.nullValue();
            return;
        }
        writer.value(value);
    }
}

定义的类型为String,这样为null的情况会都归这个类来处理,但是String的所有情况也会走里边的方法,所以为了同样的类型不执行俩遍,String和null都在此类处理,就没必要写上边那个方法了, 处理所有情况为返回null,或字符串"null",格式化为"" 空

八、Retrofit实现cookie自动化管理

对应文章解析

在现实开发中,我们可能会遇到这样的需求,需要保持长登陆状态,登陆失效为服务器判断,在我们不想往接口添加任何参数处理时,我们便想到cookie

最终实现效果为:登录成功后将将服务器返回的cookie保存到本地(每次接口请求成功,更新本地保存Cookie值,目的让本地的cookie值一直为最新的),下次请求接口时将本地最新cookie带上,用来告诉哪个用户与服务器之间的交互

1、 第一种实现方方法(第三方库实现Cookie自动化管理)

(1)依赖第三方库

implementation 'com.github.franmontiel:PersistentCookieJar:v1.0.1'

(2)创建OkHttpClient时添加cookieJar

PersistentCookieJar cookieJar = new PersistentCookieJar(new  SetCookieCache(), new SharedPrefsCookiePersistor(context));
  OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .addInterceptor(new LoginInterceptor())
                .cookieJar(cookieJar)// 设置封装好的cookieJar
                .build();
2、 第二种实现方方法(手写cookie管理类),自我操控性强

(1)创建CookieManger类

public class CookieManger implements CookieJar {
    private static Context mContext;
    private static PersistentCookieStore cookieStore;
    public CookieManger(Context context) {
        mContext = context;
        if (cookieStore == null) {
            cookieStore = new PersistentCookieStore(mContext);
        }
    }
    @Override
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
        if (cookies != null && cookies.size() > 0) {
            for (Cookie item : cookies) {
                cookieStore.add(url, item);
                if (item.name() != null && !TextUtils.isEmpty(item.name()) &&
                        item.value() != null && !TextUtils.isEmpty(item.value())) {
                    /*保存cookie到sp地方  可能会用到 */
//                    PrefUtils.setString(mContext, "cookie_name", item.name());
//                    PrefUtils.setString(mContext, "cookie_value", item.value());
                }
            }
        }
    }
    @Override
    public List<Cookie> loadForRequest(HttpUrl url) {
        List<Cookie> cookies = cookieStore.get(url);
        for (int i = 0; i < cookies.size(); i++) {
            Log.e("", "拿出来的cookies name()==" + cookies.get(i).name());
            Log.e("", "拿出来的cookies value()==" + cookies.get(i).value());
        }
        return cookies;
    }
}

(2)创建OkHttpCookies类

public class OkHttpCookies  implements Serializable {
    private transient final Cookie cookies;
    private transient Cookie clientCookies;
    public OkHttpCookies(Cookie cookies) {
        this.cookies = cookies;
    }
    public Cookie getCookies() {
        Cookie bestCookies = cookies;
        if (clientCookies != null) {
            bestCookies = clientCookies;
        }
        return bestCookies;
    }
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeObject(cookies.name());
        out.writeObject(cookies.value());
        out.writeLong(cookies.expiresAt());
        out.writeObject(cookies.domain());
        out.writeObject(cookies.path());
        out.writeBoolean(cookies.secure());
        out.writeBoolean(cookies.httpOnly());
        out.writeBoolean(cookies.hostOnly());
        out.writeBoolean(cookies.persistent());
    }
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        String name = (String) in.readObject();
        String value = (String) in.readObject();
        long expiresAt = in.readLong();
        String domain = (String) in.readObject();
        String path = (String) in.readObject();
        boolean secure = in.readBoolean();
        boolean httpOnly = in.readBoolean();
        boolean hostOnly = in.readBoolean();
        boolean persistent = in.readBoolean();
        Cookie.Builder builder = new Cookie.Builder();
        builder = builder.name(name);
        builder = builder.value(value);
        builder = builder.expiresAt(expiresAt);
        builder = hostOnly ? builder.hostOnlyDomain(domain) : builder.domain(domain);
        builder = builder.path(path);
        builder = secure ? builder.secure() : builder;
        builder = httpOnly ? builder.httpOnly() : builder;
        clientCookies =builder.build();
    }
}

(3)创建PersistentCookieStore类

public class PersistentCookieStore {
    private static final String LOG_TAG = "PersistentCookieStore";
    private static final String COOKIE_PREFS = "Cookies_Prefs";
    private final Map<String, ConcurrentHashMap<String, Cookie>> cookies;
    private final SharedPreferences cookiePrefs;
    public PersistentCookieStore(Context context) {
        cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
        cookies = new HashMap<>();
        //将持久化的cookies缓存到内存中 即map cookies
        Map<String, ?> prefsMap = cookiePrefs.getAll();
        for (Map.Entry<String, ?> entry : prefsMap.entrySet()) {
            String[] cookieNames = TextUtils.split((String) entry.getValue(), ",");
            for (String name : cookieNames) {
                String encodedCookie = cookiePrefs.getString(name, null);
                if (encodedCookie != null) {
                    Cookie decodedCookie = decodeCookie(encodedCookie);
                    if (decodedCookie != null) {
                        if (!cookies.containsKey(entry.getKey())) {
                            cookies.put(entry.getKey(), new ConcurrentHashMap<String, Cookie>());
                        }
                        cookies.get(entry.getKey()).put(name, decodedCookie);
                    }
                }
            }
        }
    }
    protected String getCookieToken(Cookie cookie) {
        return cookie.name() + "@" + cookie.domain();
    }
    public void add(HttpUrl url, Cookie cookie) {
        String name = getCookieToken(cookie);
        //将cookies缓存到内存中 如果缓存过期 就重置此cookie
        if (!cookie.persistent()) {
            if (!cookies.containsKey(url.host())) {
                cookies.put(url.host(), new ConcurrentHashMap<String, Cookie>());
            }
            cookies.get(url.host()).put(name, cookie);
        } else {
            if (cookies.containsKey(url.host())) {
                cookies.get(url.host()).remove(name);
            }
        }
        //讲cookies持久化到本地
        SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
        prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
        prefsWriter.putString(name, encodeCookie(new OkHttpCookies(cookie)));
        prefsWriter.apply();
    }
    public List<Cookie> get(HttpUrl url) {
        ArrayList<Cookie> ret = new ArrayList<>();
        if (cookies.containsKey(url.host())) {
            ret.addAll(cookies.get(url.host()).values());
        }
        return ret;
    }
    public boolean removeAll() {
        SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
        prefsWriter.clear();
        prefsWriter.apply();
        cookies.clear();
        return true;
    }
    public boolean remove(HttpUrl url, Cookie cookie) {
        String name = getCookieToken(cookie);
        if (cookies.containsKey(url.host()) && cookies.get(url.host()).containsKey(name)) {
            cookies.get(url.host()).remove(name);
            SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
            if (cookiePrefs.contains(name)) {
                prefsWriter.remove(name);
            }
            prefsWriter.putString(url.host(), TextUtils.join(",", cookies.get(url.host()).keySet()));
            prefsWriter.apply();
            return true;
        } else {
            return false;
        }
    }
    public List<Cookie> getCookies() {
        ArrayList<Cookie> ret = new ArrayList<>();
        for (String key : cookies.keySet()) {
            ret.addAll(cookies.get(key).values());
        }
        return ret;
    }
    /**
     * cookies 序列化成 string
     *
     * @param cookie 要序列化的cookie
     * @return 序列化之后的string
     */
    protected String encodeCookie(OkHttpCookies cookie) {
        if (cookie == null) {
            return null;
        }
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(os);
            outputStream.writeObject(cookie);
        } catch (IOException e) {
            Log.d(LOG_TAG, "IOException in encodeCookie", e);
            return null;
        }
        return byteArrayToHexString(os.toByteArray());
    }
    /**
     * 将字符串反序列化成cookies
     *
     * @param cookieString cookies string
     * @return cookie object
     */
    protected Cookie decodeCookie(String cookieString) {
        byte[] bytes = hexStringToByteArray(cookieString);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        Cookie cookie = null;
        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            cookie = ((OkHttpCookies) objectInputStream.readObject()).getCookies();
        } catch (IOException e) {
            Log.d(LOG_TAG, "IOException in decodeCookie", e);
        } catch (ClassNotFoundException e) {
            Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e);
        }
        return cookie;
    }
    /**
     * 二进制数组转十六进制字符串
     *
     * @param bytes byte array to be converted
     * @return string containing hex values
     */
    protected String byteArrayToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (byte element : bytes) {
            int v = element & 0xff;
            if (v < 16) {
                sb.append('0');
            }
            sb.append(Integer.toHexString(v));
        }
        return sb.toString().toUpperCase(Locale.US);
    }
    /**
     * 十六进制字符串转二进制数组
     *
     * @param hexString string of hex-encoded values
     * @return decoded byte array
     */
    protected byte[] hexStringToByteArray(String hexString) {
        int len = hexString.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
        }
        return data;
    }
}

(4)创建OkHttpClient时添加cookieJar

OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .addInterceptor(new LoginInterceptor())
                .cookieJar(new CookieManger (context))// 设置封装好的cookieJar
                .build();

九、路由判断第二种解决方案(文章为旧版,只提供思路)

参考连接如下

上述文章提到了路由这个概念,其实自己命名的,实际意义和这个此比较契合,本文章目的只服务器下发数据,当我们得到的值代表数据正常,(比如code=0数据正常),我们会正常执行我们的解析数据并处理显示数据内容等操作,如果服务器下发的数据为异常信息,前端只需要个提示操作,这样我们就没必要执行解析+显示等操作,所以我们会想到怎样可以一次性判断,终身不用管走向呢?

1、 第一种判断方法,在Rxjava的OnNext中判断
@Override
    public void onNext(BaseModel<T> o) {
        T t = o.getData();
        try {
           /* if (t!=null){
                L.e("返回数据="+o.toString());
            }else {
                L.e("返回数据=null");
            }*/
            if (view != null) {
                view.hideLoading();
            }
            if (o.getErrcode() == mSuccessCode) {
                onSuccess(t, o.getMsg(), o.getErrcode());
            } else {
                view.onErrorCode(o);
            }
        } catch (Exception e) {
            e.printStackTrace();
            onError(e.toString());
        }
    }

当我们执行到OnNext方法中,此时已经执行了Gson解析代码,所以我们是否可以将判断提前到Gson解析时候判断呢? 请看第二种方法

2、 第二种判断方法,Gson解析期间判断

如果想通过Gson解析期间判断,这样必然会设计到Gson源码如果走向,我们通过更改源码来自定义操作,通过阅读源码我们会发现解析数据会涉及到三个类,GsonConverterFactory,GsonRequestBodyConverter,GsonResponseBodyConverter这三个类,我们需要重写这个三个类,阅读代码会返现主要执行解析代码在GsonResponseBodyConverter中,所以我们的目标便是这里。

思路:Gosn解析数据时,如果出现服务器下发非正常标识,此刻我们已判断服务器返回数据不是我们需要展示的,那我们解析到这一步已不用再向下解析,可以通过抛异常来释放当前任务代码如下

@Override
    public T convert(ResponseBody value) throws IOException {
        String response = value.string();
        BaseResult re = gson.fromJson(response, BaseResult.class);
        //关注的重点,自定义响应码中非0的情况,一律抛出ApiException异常。
        //这样,我们就成功的将该异常交给onError()去处理了。
        if (re.getCode() != BaseContent.basecode) {
            value.close();
            throw new ApiException(re.getCode(), re.getMessage());
        }
        MediaType mediaType = value.contentType();
        Charset charset = mediaType != null ? mediaType.charset(UTF_8) : UTF_8;
        ByteArrayInputStream bis = new ByteArrayInputStream(response.getBytes());
        InputStreamReader reader = new InputStreamReader(bis, charset);
        JsonReader jsonReader = gson.newJsonReader(reader);
        try {
            return adapter.read(jsonReader);
        } finally {
            value.close();
        }
    }

异常已成功抛出,那异常信息到哪里了呢?答案是到Rxjava的OnError中,异常我们抛的是自定义实体类ApiException,内含code,message,那我们到Rxjava中OnError获取到异常信息 e,e instanceof ApiException通过分析异常是否为我们自定义实体类来判断下一步如何操作,此方法为路由的第二种判断,示例如下

@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 instanceof ApiException) {
            ApiException exception = (ApiException) e;
            int code = exception.getErrorCode();
            switch (code) {
                //未登录(此处只是案例 供理解)
                case CONNECT_NOT_LOGIN:
                    view.onErrorCode(new BaseModel(exception.getMessage(), code));
                    onException(CONNECT_NOT_LOGIN, "");
                    break;
                //其他不等于0 的所有状态
                default:
                    onException(OTHER_MESSAGE, exception.getMessage());
                    view.onErrorCode(new BaseModel(exception.getMessage(), code));
                    break;
            }
        } else {
            if (e != null) {
                onError(e.toString());
            } else {
                onError("未知错误");
            }
        }
    }

十、Retrofit配置及各情况处理(缓存拦截、日志打印、替换接口内容、参数添加等

相关参考跳转此链接

十一、后记

一、问:这样封装每个Activity对应一个Presenter,有些接口会多次用不想多次写

答:onCreate随便写

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MainPresenter1 presenter1 = new MainPresenter1(this);
        presenter.getTextApi();
        MainPresenter2 presenter2 = new MainPresenter2(this);
        presenter2.getTextApi();
        MainPresenter3 presenter3 = new MainPresenter3(this);
        presenter3getTextApi();
    }


二、问:有人问dialog加载圈封装的不够好,这样每个接口都得显示加载圈,不想实现都不行

答:BaseActivity和BaseFragment中都有这俩个方法

//显示加载进度框回调
    @Override
    public void showLoading() {
        showLoadingDialog();
    }
    //隐藏进度框回调
    @Override
    public void hideLoading() {
        closeLoadingDialog();
    }

如果说我本页面都不想显示Loading动画,那就在对应的Activity重写下父类的方法,比如

@Override
    public void showLoading() {
    //    super.showLoading();  //将super去掉  就不会显示Loading动画了
    }

如果我们需要显示就在对应的Fragment调用请求方法之后手动掉一下父类的显示Loading方法,如下:

mPresenter.collectApi("id");
 showLoadingDialog();


三、问:假如接口返回1001,代表重写登录或者token失效,我想在对应activity拿到状态或者做统一操作

答:可以在BaseActivity判断跳页面

//BaseActivity代码
 @Override
    public void onErrorCode(BaseModel model) {
       if (model.getErrcode() == 1001) {
            startLogin();
        }
    }
    private void startLogin() {
        startActivity(LoginActivity.class);
    }

如果想在对应Activity操作,那就在对应Activity重写此方法

//对应Activity代码
 @Override
    public void onErrorCode(BaseModel model) {
        //super.onErrorCode(model);
     if (model.getErrcode()==1001){
            //............................................
        }else if (model.getErrcode()==1002){
                  //............................................
        }
    }

github地址:https://github.com/LPTim/MVP-Retrofit2-okhttp3-Rxjava

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