android 通用Http网络请求管理工具开发思维

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_38184748/article/details/89468865

前言

嘿嘿
目前我接触的大部分项目都是走Http的协议,行业大概也是如此。所以在规划整个项目的时候,网络请求的数据流通应该如何管理才能提高拓展性比较关键。目前圈子里也有很多开源框架可以直接使用,也比较好用,出名的有OkHttp这种,也有xUtils这种包罗万象的,这些都可以使用毕竟已经经过好多前辈的验证,非常成熟。

但是,最好不要在我们项目的主包里直接使用,后期如果遇到什么问题需要更换网络架构或升级网络架构,会改动太大了。我们需要做一个中间件,随时更换网络依赖都不会对业务出现影响。
在这里插入图片描述
图中是我自己现在在用的架构,所有业务活动都不能直接用到第三方的依赖方法和类,需要中间件进行沟通维护,这样子,下层依赖的替换就不会影响到上层的事务逻辑,维护起来会更加的方便。

1、业务活动只能使用一种方法,那就是去数据管理缓存区伸手拿数据。
2、数据管理缓存区决定是否需要去进行网络请求,如果需要就给业务活动返回一个状态值,告诉业务活动我现在去帮你拿数据,你等一下。否则,直接返回数据即可
3、网络请求中间件进行网络请求管理,利用线程池合理安排线程,返回网络请求结果。

而这一次我主要讲解的就是如何写一个 “网络请求中间件”

上代码

首先在工程中new一个Module,选择Android library,名称就填network。

HttpManager

新建一个文件HttpManager,搞上单例模式

public class HttpManager {

    private static HttpManager instance;

    /**
     * 单例模式
     *
     * @return 单例
     */
    public static HttpManager getInstance(){
        if (null == instance){
            synchronized (HttpManager.class){
                if (null == instance){
                    instance = new HttpManager();
                }
            }
        }
        return instance;
    }
}

HttpException

再新建一个文件夹exception,在其中新建一个HttpException;自定义异常信息:

/**
 * 自定义异常消息
 * @author  dlong
 * created at 2019/4/12 10:28 AM
 */
public class HttpException extends RuntimeException {

    public HttpException(String detailMessage) {
        super(detailMessage);
    }

    public HttpException(Throwable throwable) {
        super(throwable);
    }

    public HttpException(String detailMessage, Throwable throwable) {
        super(detailMessage, throwable);
    }
}

RequestMode

我们的http请求可以分为4种类型
1、get
2、post
3、upload
4、download
所以新建一个文件夹bean,新建一个RequestMode

/**
 * 请求模式
 *
 * @author D10NG
 * @date on 2019-04-22 10:39
 */
public class RequestMode {

    /** get */
    public static final int MODE_GET = 1;

    /** post */
    public static final int MODE_POST = 2;

    /** 上传 */
    public static final int MODE_UPLOAD = 3;

    /** 下载 */
    public static final int MODE_DOWNLOAD = 4;
}

OnResultListener

新建文件夹method,新建一个OnResultListener接口

/**
 * 结果回调接口
 *
 * @author D10NG
 * @date on 2019-04-22 10:25
 */
public interface OnResultListener {

    /**
     * 请求过程中的失败
     * @param what tag
     * @param e 失败信息
     */
    void onRequestFail(int what, String e);

    /**
     * 请求结果失败
     * @param what tag
     * @param code 失败码
     */
    void onResultFail(int what, int code);

    /**
     * 请求成功
     * @param what tag
     * @param mode 模式
     * @param result 成功字符串
     */
    void onSuccess(int what, int mode, byte[] result);
}

HttpGet

在method文件夹里新建HttpGet

/**
 * get请求
 *
 * @author D10NG
 * @date on 2019-04-22 10:49
 */
public class HttpGet {

    private static OnResultListener onResultListener;

    /**
     * 请求
     * @param what tag
     * @param requestUrl 链接地址
     * @param resultListener 结果监听
     */
    public static void call(int what, String requestUrl,
                            OnResultListener resultListener){
        onResultListener = resultListener;
        if (null == onResultListener) {
            throw new HttpException("OnResultListener can not be null");
        }
        if (null == requestUrl || requestUrl.equals("")){
            onResultListener.onRequestFail(what, "requestUrl is null or empty");
        }
        try {
            // 新建一个URL对象
            URL url = new URL(requestUrl);
            // 打开一个HttpURLConnection连接
            HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
            // 设置连接主机超时时间
            urlConn.setConnectTimeout(Config.CONNECT_TIME_OUT);
            //设置从主机读取数据超时
            urlConn.setReadTimeout(Config.READ_TIME_OUT);
            // 设置是否使用缓存  默认是true
            urlConn.setUseCaches(true);
            // 设置为Post请求
            urlConn.setRequestMethod("GET");
            //urlConn设置请求头信息
            //设置请求中的媒体类型信息。
            urlConn.setRequestProperty("Content-Type", Config.CONTENT_TYPE);
            //设置客户端与服务连接类型
            urlConn.addRequestProperty("Connection", "Keep-Alive");
            // 开始连接
            urlConn.connect();
            // 判断请求是否成功
            if (urlConn.getResponseCode() == 200) {
                // 获取返回的数据
                byte[] result = ConversionTool.streamTobytes(urlConn.getInputStream());
                onResultListener.onSuccess(what, RequestMode.MODE_GET, result);
            } else {
                onResultListener.onResultFail(what, urlConn.getResponseCode());
            }
            // 关闭连接
            urlConn.disconnect();
        } catch (Exception e) {
            onResultListener.onRequestFail(what, e.getMessage());
        }
    }
}

HttpPost

在method文件夹里新建HttpPost

/**
 * post请求
 *
 * @author D10NG
 * @date on 2019-04-22 13:51
 */
public class HttpPost {

    private static OnResultListener onResultListener;

    /**
     * 请求
     * @param what tag
     * @param requestUrl 链接地址
     * @param paramsMap 参数携带
     * @param resultListener 结果监听
     */
    public static void call(int what, String requestUrl, HashMap<String, String> paramsMap,
                            OnResultListener resultListener) {
        onResultListener = resultListener;
        if (null == onResultListener) {
            throw new HttpException("OnResultListener can not be null");
        }
        if (null == requestUrl || requestUrl.equals("")){
            onResultListener.onRequestFail(what, "requestUrl is null or empty");
        }
        try {
            //合成参数
            StringBuilder tempParams = new StringBuilder();
            int pos = 0;
            for (String key : paramsMap.keySet()) {
                if (pos > 0) {
                    tempParams.append("&");
                }
                tempParams.append(String.format("%s=%s",
                        key,  URLEncoder.encode(paramsMap.get(key),"utf-8")));
                pos++;
            }
            String params = tempParams.toString();
            // 请求的参数转换为byte数组
            byte[] postData = params.getBytes();
            // 新建一个URL对象
            URL url = new URL(requestUrl);
            // 打开一个HttpURLConnection连接
            HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
            // 设置连接超时时间
            urlConn.setConnectTimeout(Config.CONNECT_TIME_OUT);
            //设置从主机读取数据超时
            urlConn.setReadTimeout(Config.READ_TIME_OUT);
            // Post请求必须设置允许输出 默认false
            urlConn.setDoOutput(true);
            //设置请求允许输入 默认是true
            urlConn.setDoInput(true);
            // Post请求不能使用缓存
            urlConn.setUseCaches(false);
            // 设置为Post请求
            urlConn.setRequestMethod("POST");
            //设置本次连接是否自动处理重定向
            urlConn.setInstanceFollowRedirects(true);
            // 配置请求Content-Type
            urlConn.setRequestProperty("Content-Type", Config.CONTENT_TYPE);
            // 开始连接
            urlConn.connect();
            // 发送请求参数
            DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream());
            dos.write(postData);
            dos.flush();
            dos.close();
            // 判断请求是否成功
            if (urlConn.getResponseCode() == 200) {
                // 获取返回的数据
                byte[] result = ConversionTool.streamTobytes(urlConn.getInputStream());
                onResultListener.onSuccess(what, RequestMode.MODE_POST, result);
            } else {
                onResultListener.onResultFail(what, urlConn.getResponseCode());
            }
            // 关闭连接
            urlConn.disconnect();
        } catch (Exception e) {
            onResultListener.onRequestFail(what, e.getMessage());
        }
    }
}

HttpUpload

在method文件夹里新建HttpUpload

/**
 * 上传
 *
 * @author D10NG
 * @date on 2019-04-22 14:00
 */
public class HttpUpload {

    private static OnResultListener onResultListener;

    /**
     * 请求
     * @param what tag
     * @param requestUrl 链接地址
     * @param filePath 文件路径
     * @param resultListener 结果监听
     */
    public static void call(int what, String requestUrl, String filePath,
                            OnResultListener resultListener) {
        onResultListener = resultListener;
        if (null == onResultListener) {
            throw new HttpException("OnResultListener can not be null");
        }
        if (null == requestUrl || requestUrl.equals("")){
            onResultListener.onRequestFail(what, "requestUrl is null or empty");
        }
        if (null == filePath || filePath.equals("")){
            onResultListener.onRequestFail(what, "filePath is null or empty");
        }
        try {
            File file = new File(filePath);
            //新建url对象
            URL url = new URL(requestUrl);
            //通过HttpURLConnection对象,向网络地址发送请求
            HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
            //设置该连接允许读取
            urlConn.setDoOutput(true);
            //设置该连接允许写入
            urlConn.setDoInput(true);
            //设置不能适用缓存
            urlConn.setUseCaches(false);
            //设置连接超时时间
            urlConn.setConnectTimeout(Config.CONNECT_TIME_OUT);
            //设置读取超时时间
            urlConn.setReadTimeout(Config.READ_TIME_OUT);
            //设置连接方法post
            urlConn.setRequestMethod("POST");
            //设置维持长连接
            urlConn.setRequestProperty("connection", "Keep-Alive");
            //设置文件字符集
            urlConn.setRequestProperty("Accept-Charset", "UTF-8");
            //设置文件类型
            urlConn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + "*****");
            String name = file.getName();
            DataOutputStream requestStream = new DataOutputStream(urlConn.getOutputStream());
            requestStream.writeBytes("--" + "*****" + "\r\n");
            //发送文件参数信息
            StringBuilder tempParams = new StringBuilder();
            tempParams.append("Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + name + "\"; ");
            tempParams.append("\r\n");
            tempParams.append("Content-Type: application/octet-stream\r\n");
            tempParams.append("\r\n");
            String params = tempParams.toString();
            requestStream.writeBytes(params);
            //发送文件数据
            FileInputStream fileInput = new FileInputStream(file);
            int bytesRead;
            byte[] buffer = new byte[1024];
            DataInputStream in = new DataInputStream(new FileInputStream(file));
            while ((bytesRead = in.read(buffer)) != -1) {
                requestStream.write(buffer, 0, bytesRead);
            }
            requestStream.writeBytes("\r\n");
            requestStream.flush();
            requestStream.writeBytes("--" + "*****" + "--" + "\r\n");
            requestStream.flush();
            fileInput.close();
            int statusCode = urlConn.getResponseCode();
            if (statusCode == 200) {
                // 获取返回的数据
                byte[] result = ConversionTool.streamTobytes(urlConn.getInputStream());
                onResultListener.onSuccess(what, RequestMode.MODE_UPLOAD, result);
            } else {
                onResultListener.onResultFail(what, urlConn.getResponseCode());
            }
        } catch (IOException e) {
            onResultListener.onRequestFail(what, e.getMessage());
        }
    }
}

HttpDownload

在method文件夹里新建HttpDownload

/**
 * 下载
 *
 * @author D10NG
 * @date on 2019-04-22 14:15
 */
public class HttpDownload {

    private static OnResultListener onResultListener;

    /**
     * 请求
     * @param what tag
     * @param requestUrl 连接地址
     * @param filePath 存储文件地址
     * @param resultListener 结果监听
     */
    public static void call(int what, String requestUrl, String filePath,
                            OnResultListener resultListener){
        onResultListener = resultListener;
        if (null == onResultListener) {
            throw new HttpException("OnResultListener can not be null");
        }
        if (null == requestUrl || requestUrl.equals("")){
            onResultListener.onRequestFail(what, "requestUrl is null or empty");
        }
        if (null == filePath || filePath.equals("")){
            onResultListener.onRequestFail(what, "filePath is null or empty");
        }
        try {
            // 新建一个URL对象
            URL url = new URL(requestUrl);
            // 打开一个HttpURLConnection连接
            HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
            // 设置连接主机超时时间
            urlConn.setConnectTimeout(Config.CONNECT_TIME_OUT);
            //设置从主机读取数据超时
            urlConn.setReadTimeout(Config.READ_TIME_OUT);
            // 设置是否使用缓存  默认是true
            urlConn.setUseCaches(true);
            // 设置为get请求
            urlConn.setRequestMethod("GET");
            //urlConn设置请求头信息
            //设置请求中的媒体类型信息。
            urlConn.setRequestProperty("Content-Type", Config.CONTENT_TYPE);
            //设置客户端与服务连接类型
            urlConn.addRequestProperty("Connection", "Keep-Alive");
            // 开始连接
            urlConn.connect();
            // 判断请求是否成功
            if (urlConn.getResponseCode() == 200) {
                File descFile = new File(filePath);
                FileOutputStream fos = new FileOutputStream(descFile);
                byte[] buffer = new byte[1024];
                int len;
                InputStream inputStream = urlConn.getInputStream();
                while ((len = inputStream.read(buffer)) != -1) {
                    // 写到本地
                    fos.write(buffer, 0, len);
                }
                onResultListener.onSuccess(what, RequestMode.MODE_DOWNLOAD, null);
            } else {
                onResultListener.onResultFail(what, urlConn.getResponseCode());
            }
            // 关闭连接
            urlConn.disconnect();
        } catch (Exception e) {
            onResultListener.onRequestFail(what, e.getMessage());
        }
    }
}

ConversionTool

在method里再写一个数据转换工具

/**
 * 数据转换工具
 *
 * @author D10NG
 * @date on 2019-04-22 11:15
 */
class ConversionTool {

    /**
     * 将输入流转换成byte数组
     *
     * @param is 从网络获取的输入流
     * @return byte[]
     */
    public static byte[] streamTobytes(InputStream is) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            baos.close();
            is.close();
            return baos.toByteArray();
        } catch (Exception e) {
            return null;
        }
    }
}

Config

写一个配置类,所有的配置信息存放在这里,我们可以根据需要进行修改

/**
 * 配置文件
 *
 * @author D10NG
 * @date on 2019-04-22 11:24
 */
public class Config {

    /** ------------------------------------- 工作线程池 ---------------------------------------- */
    /** 能同时运行的请求数 */
    public static final int CORE_POOL_SIZE = 3;
    /** 超出队列后的非核心线程数 */
    public static final int MAXINUM_POOL_SIZE = 5;
    /** 保持空闲不被回收的时间 */
    public static final int KEEP_ALIVE_TIME = 1;
    /** 保持空闲不被回收的时间单位 */
    public static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
    /** 排队队列数量 */
    public static final int QUEUE_SIZE = 15;
    /** ------------------------------------- 工作线程池 ---------------------------------------- */

    /** ------------------------------------- Http请求 ----------------------------------------- */
    /** 连接超时设定 */
    public static final int CONNECT_TIME_OUT = 5000;
    /** 读取超时设定 */
    public static final int READ_TIME_OUT = 5000;
    /** 读取超时设定 */
    public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
}

OnCallBack

数据回调接口,返回给中间件使用者,也就是数据缓存区

/**
 * 整个Http框架回调
 * @author D10NG
 * @date on 2019-04-22 15:28
 */
public interface OnCallBack extends OnResultListener {

    /**
     * 已经有了相同的参数
     * @param what tag
     */
    void onHaveSameWhat(int what);
}

完善HttpManager逻辑

写完其他的类,这里就是我们的主要逻辑的编写地方。
1、利用线程池进行业务排序;
2、将操作后的结果通过接口回调;

/**
 * 总管理
 *
 * @author D10NG
 * @date on 2019-04-22 14:30
 */
public class HttpManager {

    private static HttpManager instance;

    private OnCallBack onCallBack;

    /** 线程池 */
    private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(Config.CORE_POOL_SIZE,
            Config.MAXINUM_POOL_SIZE, Config.KEEP_ALIVE_TIME, Config.KEEP_ALIVE_TIME_UNIT,
            new LinkedBlockingQueue<Runnable>(Config.QUEUE_SIZE));

    /** 存储what与Runnable */
    private final SparseArray<Runnable> taskByWhat = new SparseArray<>();

    /**
     * 单例模式
     *
     * @return 单例
     */
    public static HttpManager getInstance(){
        if (null == instance){
            synchronized (HttpManager.class){
                if (null == instance){
                    instance = new HttpManager();
                }
            }
        }
        return instance;
    }

    /**
     * 初始化结果监听
     * @param callBack 结果监听
     */
    public void init(OnCallBack callBack){
        onCallBack = callBack;
    }

    /**
     * 检查是否设置了监听
     */
    private void checkCallBack(){
        if (null == onCallBack){
            throw new HttpException("OnCallBack is null");
        }
    }

    /**
     * 检查what是否存在
     * @param what
     * @return boolean
     */
    private boolean checkWhat(int what, boolean sendBack){
        if (null != taskByWhat.get(what)){
            if (sendBack){
                onCallBack.onHaveSameWhat(what);
            }
            return true;
        }
        return false;
    }

    /**
     * 请求监听
     */
    private OnResultListener onResultListener = new OnResultListener() {
        @Override
        public void onRequestFail(int what, String e) {
            checkCallBack();
            onCallBack.onRequestFail(what, e);
            if (checkWhat(what, false)){
                taskByWhat.remove(what);
            }
        }

        @Override
        public void onResultFail(int what, int code) {
            checkCallBack();
            onCallBack.onResultFail(what, code);
            if (checkWhat(what, false)){
                taskByWhat.remove(what);
            }
        }

        @Override
        public void onSuccess(int what, int mode, byte[] result) {
            checkCallBack();
            onCallBack.onSuccess(what, mode, result);
            if (checkWhat(what, false)){
                taskByWhat.remove(what);
            }
        }
    };

    /**
     * get
     * @param what tag
     * @param requestUrl 连接地址
     */
    public void executeGet (final int what, final String requestUrl){
        checkCallBack();
        if (checkWhat(what, true)) return;
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                HttpGet.call(what, requestUrl, onResultListener);
            }
        };
        taskByWhat.put(what, runnable);
        threadPoolExecutor.execute(runnable);
    }

    /**
     * post
     * @param what tag
     * @param requestUrl 连接地址
     * @param paramsMap 参数集合
     */
    public void executePost (final int what, final String requestUrl,
                            final HashMap<String, String> paramsMap){
        checkCallBack();
        if (checkWhat(what, true)) return;
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                HttpPost.call(what, requestUrl, paramsMap, onResultListener);
            }
        };
        taskByWhat.put(what, runnable);
        threadPoolExecutor.execute(runnable);
    }

    /**
     * 上传
     * @param what tag
     * @param requestUrl 连接地址
     * @param filePath 文件路径
     */
    public void executeUpload (final int what, final String requestUrl,
                               final String filePath){
        checkCallBack();
        if (checkWhat(what, true)) return;
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                HttpUpload.call(what, requestUrl, filePath, onResultListener);
            }
        };
        taskByWhat.put(what, runnable);
        threadPoolExecutor.execute(runnable);
    }

    /**
     * 下载
     * @param what tag
     * @param requestUrl 连接地址
     * @param filePath 下载保存的文件路径
     */
    public void executeDownload (final int what, final String requestUrl,
                              final String filePath){
        checkCallBack();
        if (checkWhat(what, true)) return;
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                HttpDownload.call(what, requestUrl, filePath, onResultListener);
            }
        };
        taskByWhat.put(what, runnable);
        threadPoolExecutor.execute(runnable);
    }

    /**
     * 取消任务
     * @param what tag
     * @return boolean
     */
    public boolean cancleTask(int what){
        Runnable runnable = taskByWhat.get(what);
        if (null == runnable){
            return true;
        }
        return threadPoolExecutor.remove(runnable);
    }
}

使用方法

添加依赖

implementation project(path: ':network')

数据缓存区

新建文件夹http,新建文件HttpHandler

/**
 * Http回复分发
 * @author D10NG
 * @date on 2019-04-22 16:43
 */
public class HttpHandler {

    private static final String TAG = "HttpHandler";

    /**
     * 初始化
     * 这里的监听结果可以使用EventBus进行数据的传递
     * 分发数据
     */
    public static void init(){
        HttpManager.getInstance().init(new OnCallBack() {
            @Override
            public void onHaveSameWhat(int what) {
                Log.e(TAG, "已经存在该请求" + what);
            }

            @Override
            public void onRequestFail(int what, String e) {
                Log.e(TAG, what + "请求失败!" + e);
            }

            @Override
            public void onResultFail(int what, int code) {
                Log.e(TAG, what + "请求结果不正确!" + code);
            }

            @Override
            public void onSuccess(int what, int mode, byte[] result) {
                Log.e(TAG, what + "请求成功!" + mode);
                Log.e(TAG, "result => " + new String(result));
            }
        });
    }

    /**
     * 获得管理者
     * @return HttpManager
     */
    public static HttpManager getManager(){
        return HttpManager.getInstance();
    }
}

新建文件HttpWhat

/**
 * Http请求的tag标记列表
 *
 * 全部的http请求都应该把tag放在这里统一管理;
 *
 * @author D10NG
 * @date on 2019-04-22 16:40
 */
public class HttpWhat {

    public static final int APP_UPDATA_0 = 10;

    public static final int APP_UPDATA_1 = 11;

    public static final int APP_UPDATA_2 = 12;
}

初始化

在Application中初始化

/**
 * @author D10NG
 * @date on 2019-04-22 16:37
 */
public class app extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // 初始化
        HttpHandler.init();
    }
}

点击事件

再在点击事件中触发请求。

    public void Click(View view){
        HashMap<String, String> params = new HashMap<>();
        params.put("appid", "1234567890");
        HttpHandler.getManager().executePost(HttpWhat.APP_UPDATA_0, url, params);
    }

完事

demo : D10NGYANG/DL10HttpManager

业务逻辑这里因为我懒,所以没有写出来缓存区的缓存的工作方法,也挺简单的,用一个Map存起来,key就是what,而value就是一个object。
业务活动过来拿数据就是根据what来取。
那缓存区怎么通知业务活动有新的消息来了呢?
就是用EventBus,但是只传递what,这样能减少EventBus携带的信息量,也更好管理信息去向与来源,然后监听同样what的业务活动来缓存区拿数据即可。

这样子应该比较清晰了,如果还有问题可以评论联系我。

猜你喜欢

转载自blog.csdn.net/sinat_38184748/article/details/89468865