EasyHttp框架的使用

项目的集成

// JitPack 远程仓库:https://jitpack.io
 maven { url 'https://jitpack.io' }
// 网络请求框架:https://github.com/getActivity/EasyHttp
implementation 'com.github.getActivity:EasyHttp:12.5'
// OkHttp 框架:https://github.com/square/okhttp
// noinspection GradleDependency
implementation 'com.squareup.okhttp3:okhttp:3.12.13'

源码及介绍

网络请求,如斯优雅 - 简书

GitHub - getActivity/EasyHttp: Android 网络请求框架,简单易用,so easy

解决明文流量的网络请求报错

CLEARTEXT communication to 3g.163.com not permitted by network security policy
    java.net.UnknownServiceException: CLEARTEXT communication to 3g.163.com not permitted by network security policy

添加res/xml/network_security_config.xml文件。文件名可以自拟,和后面的配置对应上即可。如果项目中,res文件夹中没有xml文件夹,我们需要在里面创建一个xml文件夹,然后在里面创建一个network_security_config.xml文件,network_security_config.xml文件中的内容如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- 解决 Android P 禁用 http 请求的问题 -->
<network-security-config>
    <!-- 允许明文通信 -->
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>
android:networkSecurityConfig="@xml/network_security_config"

初始化EasyHttp

由于直接复用轮子哥的配置代码,需要引入他调用的包,如果不引用的话修改他的配置代码

// Json 解析框架:https://github.com/google/gson
implementation 'com.google.code.gson:gson:2.10.1'
// Gson 解析容错:https://github.com/getActivity/GsonFactory
implementation 'com.github.getActivity:GsonFactory:8.0'
// 腾讯 MMKV:https://github.com/Tencent/MMKV MMKV初始化,MMKV是基于mmap内存映射的key-value组件
implementation 'com.tencent:mmkv-static:1.2.14'
    /**
     * 初始哈EasyHttp
     */
    private void initEasyHttp() {
        MMKV.initialize(this);//MMKV初始化,MMKV是基于mmap内存映射的key-value组件
        OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
        // 网络请求框架初始化
        IRequestServer server = new ReleaseServer();
        EasyConfig.with(okHttpClient)
                //.setLogEnabled(BuildConfig.DEBUG)// 是否打印日志
                .setServer(server)// 设置服务器配置(必须设置)
                .setHandler(new RequestHandler(this))// 设置请求处理策略(必须设置)
                // 设置请求参数拦截器
                .setInterceptor(new IRequestInterceptor() {
                    @Override
                    public void interceptArguments(@NonNull HttpRequest<?> httpRequest,
                                                   @NonNull HttpParams params,
                                                   @NonNull HttpHeaders headers) {
                        headers.put("timestamp", String.valueOf(System.currentTimeMillis()));
                    }
                })
                .setRetryCount(1) // 设置请求重试次数
                .setRetryTime(2000)// 设置请求重试时间
//                .addParam("token", "6666666")// 添加全局请求参数
//                .addHeader("date", "20191030")// 添加全局请求头
                .into();
    }

RequestHandler 请求处理类 

final public class RequestHandler implements IRequestHandler {
    private final Application mApplication;

    public RequestHandler(Application application) {
        mApplication = application;
    }

    @NonNull
    @Override
    public Object requestSuccess(@NonNull HttpRequest<?> httpRequest, @NonNull Response response,
                                 @NonNull Type type) throws Exception {
        if (Response.class.equals(type)) {
            return response;
        }

        if (!response.isSuccessful()) {
            throw new ResponseException(String.format(mApplication.getString(R.string.http_response_error),
                    response.code(), response.message()), response);
        }

        if (Headers.class.equals(type)) {
            return response.headers();
        }

        ResponseBody body = response.body();
        if (body == null) {
            throw new NullBodyException(mApplication.getString(R.string.http_response_null_body));
        }

        if (ResponseBody.class.equals(type)) {
            return body;
        }

        // 如果是用数组接收,判断一下是不是用 byte[] 类型进行接收的
        if(type instanceof GenericArrayType) {
            Type genericComponentType = ((GenericArrayType) type).getGenericComponentType();
            if (byte.class.equals(genericComponentType)) {
                return body.bytes();
            }
        }

        if (InputStream.class.equals(type)) {
            return body.byteStream();
        }

        if (Bitmap.class.equals(type)) {
            return BitmapFactory.decodeStream(body.byteStream());
        }

        String text;
        try {
            text = body.string();
        } catch (IOException e) {
            // 返回结果读取异常
            throw new DataException(mApplication.getString(R.string.http_data_explain_error), e);
        }

        // 打印这个 Json 或者文本
        EasyLog.printJson(httpRequest, text);

        if (String.class.equals(type)) {
            return text;
        }

        final Object result;

        try {
            result = GsonFactory.getSingletonGson().fromJson(text, type);
        } catch (JsonSyntaxException e) {
            // 返回结果读取异常
            throw new DataException(mApplication.getString(R.string.http_data_explain_error), e);
        }

        if (result instanceof HttpData) {
            HttpData<?> model = (HttpData<?>) result;
            model.setResponseHeaders(response.headers());

            if (model.isRequestSuccess()) {
                // 代表执行成功
                return result;
            }

            if (model.isTokenInvalidation()) {
                // 代表登录失效,需要重新登录
                throw new TokenException(mApplication.getString(R.string.http_token_error));
            }

            // 代表执行失败
            throw new ResultException(model.getMessage(), model);
        }
        return result;
    }

    @NonNull
    @Override
    public Exception requestFail(@NonNull HttpRequest<?> httpRequest, @NonNull Exception e) {
        if (e instanceof HttpException) {
            if (e instanceof TokenException) {
                // 登录信息失效,跳转到登录页

            }
            return e;
        }

        if (e instanceof SocketTimeoutException) {
            return new TimeoutException(mApplication.getString(R.string.http_server_out_time), e);
        }

        if (e instanceof UnknownHostException) {
            NetworkInfo info = ((ConnectivityManager) mApplication.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
            // 判断网络是否连接
            if (info != null && info.isConnected()) {
                // 有连接就是服务器的问题
                return new ServerException(mApplication.getString(R.string.http_server_error), e);
            }
            // 没有连接就是网络异常
            return new NetworkException(mApplication.getString(R.string.http_network_error), e);
        }

        if (e instanceof IOException) {
            // 出现该异常的两种情况
            // 1. 调用 EasyHttp.cancel
            // 2. 网络请求被中断
            return new CancelException(mApplication.getString(R.string.http_request_cancel), e);
        }

        return new HttpException(e.getMessage(), e);
    }

    @NonNull
    @Override
    public Exception downloadFail(@NonNull HttpRequest<?> httpRequest, @NonNull Exception e) {
        if (e instanceof ResponseException) {
            ResponseException responseException = ((ResponseException) e);
            Response response = responseException.getResponse();
            responseException.setMessage(String.format(mApplication.getString(R.string.http_response_error),
                    response.code(), response.message()));
            return responseException;
        } else if (e instanceof NullBodyException) {
            NullBodyException nullBodyException = ((NullBodyException) e);
            nullBodyException.setMessage(mApplication.getString(R.string.http_response_null_body));
            return nullBodyException;
        } else if (e instanceof FileMd5Exception) {
            FileMd5Exception fileMd5Exception = ((FileMd5Exception) e);
            fileMd5Exception.setMessage(mApplication.getString(R.string.http_response_md5_error));
            return fileMd5Exception;
        }
        return requestFail(httpRequest, e);
    }

    @Nullable
    @Override
    public Object readCache(@NonNull HttpRequest<?> httpRequest, @NonNull Type type, long cacheTime) {
        String cacheKey = HttpCacheManager.generateCacheKey(httpRequest);
        String cacheValue = HttpCacheManager.readHttpCache(cacheKey);
        if (cacheValue == null || "".equals(cacheValue) || "{}".equals(cacheValue)) {
            return null;
        }
        EasyLog.printLog(httpRequest, "----- read cache key -----");
        EasyLog.printJson(httpRequest, cacheKey);
        EasyLog.printLog(httpRequest, "----- read cache value -----");
        EasyLog.printJson(httpRequest, cacheValue);
        EasyLog.printLog(httpRequest, "cacheTime = " + cacheTime);
        boolean cacheInvalidate = HttpCacheManager.isCacheInvalidate(cacheKey, cacheTime);
        EasyLog.printLog(httpRequest, "cacheInvalidate = " + cacheInvalidate);
        if (cacheInvalidate) {
            // 表示缓存已经过期了,直接返回 null 给外层,表示缓存不可用
            return null;
        }
        return GsonFactory.getSingletonGson().fromJson(cacheValue, type);
    }

    @Override
    public boolean writeCache(@NonNull HttpRequest<?> httpRequest, @NonNull Response response, @NonNull Object result) {
        String cacheKey = HttpCacheManager.generateCacheKey(httpRequest);
        String cacheValue = GsonFactory.getSingletonGson().toJson(result);
        if (cacheValue == null || "".equals(cacheValue) || "{}".equals(cacheValue)) {
            return false;
        }
        EasyLog.printLog(httpRequest, "----- write cache key -----");
        EasyLog.printJson(httpRequest, cacheKey);
        EasyLog.printLog(httpRequest, "----- write cache value -----");
        EasyLog.printJson(httpRequest, cacheValue);
        boolean writeHttpCacheResult = HttpCacheManager.writeHttpCache(cacheKey, cacheValue);
        EasyLog.printLog(httpRequest, "writeHttpCacheResult = " + writeHttpCacheResult);
        boolean refreshHttpCacheTimeResult = HttpCacheManager.setHttpCacheTime(cacheKey, System.currentTimeMillis());
        EasyLog.printLog(httpRequest, "refreshHttpCacheTimeResult = " + refreshHttpCacheTimeResult);
        return writeHttpCacheResult && refreshHttpCacheTimeResult;
    }

    @Override
    public void clearCache() {
        HttpCacheManager.clearCache();
    }
}

字符串

<!--EasyHttp-->
<string name="http_response_error">服务器响应异常,请稍后再试,响应码:%d,响应信息:%s</string>
<string name="http_response_null_body">服务器数据返回异常,请稍后再试</string>
<string name="http_data_explain_error">数据解析异常,请稍后</string>
<string name="http_token_error">登录失效,请重新登录</string>
<string name="http_server_out_time">服务器请求超时,请稍后再试</string>
<string name="http_server_error">服务器连接异常,请稍后再试</string>
<string name="http_network_error">请求失败,请检查网络设置</string>
<string name="http_request_cancel">请求被中断,请重试</string>
<string name="http_response_md5_error">下载失败,文件 md5 校验失败</string>

HttpCacheManager Http缓存管理器

public final class HttpCacheManager {

    private static final MMKV HTTP_CACHE_CONTENT = MMKV.mmkvWithID("http_cache_content");

    private static final MMKV HTTP_CACHE_TIME = MMKV.mmkvWithID("http_cache_time");

    /**
     * 生成缓存的 key
     */
    public static String generateCacheKey(@NonNull HttpRequest<?> httpRequest) {
        IRequestApi requestApi = httpRequest.getRequestApi();
        return "请替换成当前的用户 id" + "\n" + requestApi.getApi() + "\n" + GsonFactory.getSingletonGson().toJson(requestApi);
    }

    /**
     * 读取缓存
     */
    public static String readHttpCache(String cacheKey) {
        String cacheValue = HTTP_CACHE_CONTENT.getString(cacheKey, null);
        if ("".equals(cacheValue) || "{}".equals(cacheValue)) {
            return null;
        }
        return cacheValue;
    }

    /**
     * 写入缓存
     */
    public static boolean writeHttpCache(String cacheKey, String cacheValue) {
        return HTTP_CACHE_CONTENT.putString(cacheKey, cacheValue).commit();
    }

    /**
     * 清理缓存
     */
    public static void clearCache() {
        HTTP_CACHE_CONTENT.clearMemoryCache();
        HTTP_CACHE_CONTENT.clearAll();

        HTTP_CACHE_TIME.clearMemoryCache();
        HTTP_CACHE_TIME.clearAll();
    }

    /**
     * 获取 Http 写入缓存的时间
     */
    public static long getHttpCacheTime(String cacheKey) {
        return HTTP_CACHE_TIME.getLong(cacheKey, 0);
    }

    /**
     * 设置 Http 写入缓存的时间
     */
    public static boolean setHttpCacheTime(String cacheKey, long cacheTime) {
        return HTTP_CACHE_TIME.putLong(cacheKey, cacheTime).commit();
    }

    /**
     * 判断缓存是否过期
     */
    public static boolean isCacheInvalidate(String cacheKey, long maxCacheTime) {
        if (maxCacheTime == Long.MAX_VALUE) {
            // 表示缓存长期有效,永远不会过期
            return false;
        }
        long httpCacheTime = getHttpCacheTime(cacheKey);
        if (httpCacheTime == 0) {
            // 表示不知道缓存的时间,这里默认当做已经过期了
            return true;
        }
        return httpCacheTime + maxCacheTime < System.currentTimeMillis();
    }
}

HttpData 统一接口数据结构

public class HttpData<T>  {
    /** 响应头 */
    @Nullable
    private Headers responseHeaders;

    /** 返回码 */
    private int errorCode;
    /** 提示语 */
    private String errorMsg;
    /** 数据 */
    @Nullable
    private T data;

    public void setResponseHeaders(@Nullable Headers responseHeaders) {
        this.responseHeaders = responseHeaders;
    }

    @Nullable
    public Headers getResponseHeaders() {
        return responseHeaders;
    }

    public int getCode() {
        return errorCode;
    }

    public String getMessage() {
        return errorMsg;
    }

    @Nullable
    public T getData() {
        return data;
    }

    /**
     * 是否请求成功
     */
    public boolean isRequestSuccess() {
        // 这里为了兼容 WanAndroid 接口才这样写,但是一般情况下不建议这么设计
        // 因为 int 的默认值就是 0,这样就会导致,后台返回结果码为 0 和没有返回的效果是一样的
        // 本质上其实不一样,没有返回结果码本身就是一种错误数据结构,理论上应该走失败的回调
        // 因为这里会判断是否等于 0,所以就会导致原本走失败的回调,结果走了成功的回调
        // 所以在定义错误码协议的时候,请不要将后台返回的某个成功码或者失败码的值设计成 0
        // 如果你的项目已经出现了这种情况,可以尝试将结果码的数据类型从 int 修改成 Integer
        // 这样就可以通过结果码是否等于 null 来判断后台是否返回了,当然这样也有一些弊端
        // 后面外层在使用这个结果码的时候,要先对 Integer 对象进行一次判空,否则会出现空指针异常
        return errorCode == 0;
    }

    /**
     * 是否 Token 失效
     */
    public boolean isTokenInvalidation() {
        return errorCode == 1001;
    }
}

ReleaseServer 请求服务配置

public class ReleaseServer implements IRequestServer {
    @NonNull
    @Override
    public String getHost() {
        return "https://www.wanandroid.com/";
    }
}

ResultException 返回结果异常

public final class ResultException extends HttpException {

    private final HttpData<?> mData;

    public ResultException(String message, HttpData<?> data) {
        super(message);
        mData = data;
    }

    public ResultException(String message, Throwable cause, HttpData<?> data) {
        super(message, cause);
        mData = data;
    }

    @NonNull
    public HttpData<?> getHttpData() {
        return mData;
    }
}

TokenException Token失效异常

public final class TokenException extends HttpException {

    public TokenException(String message) {
        super(message);
    }

    public TokenException(String message, Throwable cause) {
        super(message, cause);
    }
}

模拟下载文件

// 如果是放到外部存储的应用专属目录则不需要适配分区存储特性
        File file = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "微信 8.0.15.apk");
        String url = "http://3g.163.com/links/4636";
        EasyHttp.download(this)
                .method(HttpMethod.GET)
                .file(file)
                //.url("https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk")
//                .url("https://dldir1.qq.com/weixin/android/weixin8015android2020_arm64.apk")
//                .md5("b05b25d4738ea31091dd9f80f4416469")
                .url(url)
                .listener(new OnDownloadListener() {

                    @Override
                    public void onDownloadStart(File file) {
                        dialoading.setTv("开始下载");
                    }

                    @Override
                    public void onDownloadProgressChange(File file, int progress) {
                        dialoading.setTv("下载进度:" + progress);
                    }

                    @Override
                    public void onDownloadSuccess(File file) {
                        ToastUtils.success("下载成功:" + file.getPath());
                        AppUtils.installApp(file);
                    }

                    @Override
                    public void onDownloadFail(File file, Exception e) {
                        ToastUtils.error("下载失败:" + e.getMessage());
                        new WarningDialog.Builder(DownLoadActivity.this).
setTitle("下载失败").setText(e.toString()).show();
                        if (e instanceof FileMd5Exception) {
                            // 如果是文件 md5 校验失败,则删除文件
                            if (file.exists()) {
                                file.delete();
                            }
                        }
                    }

                    @Override
                    public void onDownloadEnd(File file) {
                        dialoading.close();
                    }
                })
                .start();

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值