仿照Android开源框架OkHttp而写的一个基于HttpURLConnection网络请求的demo包含Basic认证和重试机制

废话前言

我们在平时使用第三方框架的时候总觉得就那样使用就行了,然而如果第三方框架不存在了,就会感觉不知所措了。其实我们应该了解第三方访问框架的设计思路,进而学习其中的思想,不管到哪里,用什么语言都能应对。

市面上常用的网络访问框架是okhttp。他的优点是接收多个请求,重试机制。他在请求的时候将请求放入请求队列。使用线程池来响应每一个请求。线程池如果有空闲的线程时去相应请求。否则就等待。线程池有默认的几个任务线程。任务线程是负责处理请求的,线程池中有一个核心线程专门负责不停的去队列中获取请求,把请求交给任务线程去处理。这样任务线程跟请求关联起来。

网络访问框架的请求设计包含url,params,listener又包括post和get请求。还包括Basic认证等。这些都是对HttpUrlConnection的封装。

对高并发的理解

最原始的处理方式

1、一个activity有多个请求,假设10个,先存起来这些请求。先后顺序往server发送。服务器对客户端的相应来一个请求就会创建一个线程,然而server假设最多能承受10万个请求。如果数量多了话服务器受不了。

2、后来解决方法是服务器建立线程池,将线程交给线程池处理,去优化thread。如果请求越来越多还是承受不了,

3、分发,中央处理器,请求分发到不同的服务器。一台服务器承受不了,扩展服务器,中央处理器将请求分发到机房的服务器。用来相应请求。

对服务器的优化,Nio。

高并发最终解决唯一的就是加服务器。代码对高并发的处理是节省服务器的硬件成本。

正文

废话少说,上代码

定义请求接口封装数据

package com.tydfd.tydfdokhttp.util;


import com.tydfd.tydfdokhttp.http.UserBean;

/**
 * @author liudo
 */
public interface IHttpRequest {
    /**
     * 封装请求接口
     * @param url
     */
    void setUrl(String url);

    /**
     * 设置数据
     * @param data
     */
    void setData(byte[] data);

    /**
     * 设置回调
     * @param callbackListener
     */
    void setListener(CallbackListener callbackListener);

    /**
     * 执行线程
     */
    void execute(UserBean userBean);



}

设置回调接口

package com.tydfd.tydfdokhttp.util;

import java.io.InputStream;

public interface CallbackListener {

    void onSuccess(InputStream inputStream);

    void onFailure();

}

线程池设计

package com.tydfd.tydfdokhttp.util;

import android.util.Log;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author liudo
 */
public class ThreadPoolManager {

    /**
     * 创建队列,用来保存异步请求任务
     */
    private LinkedBlockingDeque<Runnable> mQueue = new LinkedBlockingDeque<>();
    /**
     * 添加异步任务到队列中
     * @param runnable
     */
    public void addTask(Runnable runnable){
        if(runnable != null){
            try {
                mQueue.put(runnable);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 创建延迟队列
     */
    private DelayQueue<HttpTask> mDelayQueue = new DelayQueue<>();

    public void addDelayTask(HttpTask ht){
        if(ht != null){
            ht.setDelayTime(3000);
            mDelayQueue.offer(ht);
        }
    }

    public Runnable delayThread = new Runnable() {
        @Override
        public void run() {
            HttpTask ht = null;
            while (true){
                try {
                    ht = mDelayQueue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(ht.getRetryCount() < 3){
                    mThreadPoolExecutor.execute(ht);
                    ht.setRetryCount(ht.getRetryCount()+1);
                    Log.e("=== 重试机制 ===",ht.getRetryCount() + "");
                }else{
                    Log.e("=== 重试机制 ===","执行次数超限,放弃");
                }
            }
        }
    };

    /**
     *  3 创建线程池
     */
    private ThreadPoolExecutor mThreadPoolExecutor;

    private ThreadPoolManager(){
        mThreadPoolExecutor = new ThreadPoolExecutor(3, 10, 15, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                /**
                 * 处理抛出来的任务
                 */
                addTask(r);
            }
        });
        mThreadPoolExecutor.execute(communicateThread);
        mThreadPoolExecutor.execute(delayThread);
    }

    /**
     * 创建 队列与线程池的"交互"线程
     */
    public Runnable communicateThread = new Runnable() {
        @Override
        public void run() {
            Runnable ruun = null;
            while (true){
                try {
                    ruun = mQueue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                    mThreadPoolExecutor.execute(ruun);
            }
        }
    };

    private static ThreadPoolManager threadPoolManager = new ThreadPoolManager();

    public static ThreadPoolManager getInstance(){
        return threadPoolManager;
    }

}

请求实现类

package com.tydfd.tydfdokhttp.util;


import android.util.Base64;


import com.tydfd.tydfdokhttp.http.UserBean;

import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * @author liudo
 */
public class JsonHttpRequest implements IHttpRequest{

    private String url;
    private byte[] data;
    private CallbackListener mCallbackListener;
    private HttpURLConnection urlConnection;

    @Override
    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public void setData(byte[] data) {
        this.data = data;
    }

    @Override
    public void setListener(CallbackListener callbackListener) {
        this.mCallbackListener = callbackListener;
    }

    @Override
    public void execute(UserBean userBean) {
        URL url = null;
        try {
            url = new URL(this.url);
            /**
             * 打开http连接
             */
            urlConnection = (HttpURLConnection) url.openConnection();
            /**
             * 连接的超时时间
             */
            urlConnection.setConnectTimeout(6000);
            /**
             * 不使用缓存
             */
            urlConnection.setUseCaches(false);
            /**
             * 是成员函数,仅作用于当前函数,设置这个连接是否可以被重定向
             */
            urlConnection.setInstanceFollowRedirects(true);
            /**
             * 响应的超时时间
             */
            urlConnection.setReadTimeout(3000);
            /**
             * 设置这个连接是否可以写入数据
             */
            urlConnection.setDoInput(true);
            /**
             * 设置这个连接是否可以输出数据
             */
            urlConnection.setDoOutput(true);
            /**
             * 设置请求的方式
             */
            if(data==null){
                urlConnection.setRequestMethod("GET");
            }else {
                urlConnection.setRequestMethod("POST");
            }
            /**
             * 设置Basic认证
             */
            if(userBean!=null||userBean.getUsername()!= ""||userBean.getPassword()!=""){
            String userMsg = userBean.getUsername() + ":" + userBean.getPassword();
            String base64UserMsg = Base64.encodeToString(userMsg.getBytes(),Base64.DEFAULT);
            final String tokenStr = "Basic " + base64UserMsg;
            urlConnection.addRequestProperty("Authorization", tokenStr);

            }


            /**
             * 设置消息的类型
             */
            urlConnection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
            /**
             * 连接,从上述至此的配置必须要在connect之前完成,实际上它只是建立了一个与服务器的TCP连接
             */
            urlConnection.connect();
            /**
             * -------------使用字节流发送数据--------------
             */
            OutputStream out = urlConnection.getOutputStream();
            /**
             * 缓冲字节流包装字节流
             */
            BufferedOutputStream bos = new BufferedOutputStream(out);
            /**
             * 把这个字节数组的数据写入缓冲区中
             */
            bos.write(data);
            /**
             * 刷新缓冲区,发送数据
             */
            bos.flush();
            out.close();
            bos.close();
            /**
             * ------------字符流写入数据------------
             */

            /**
             * 得到服务端的返回码是否连接成功
             */
            if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                InputStream in = urlConnection.getInputStream();
                mCallbackListener.onSuccess(in);
            }else{
                // 访问失败,重试
                throw new RuntimeException("请求失败");
            }
    }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("请求失败");
        }finally{
            urlConnection.disconnect();
        }
    }



}

响应请求泛型封装

package com.tydfd.tydfdokhttp.util;

import android.os.Handler;
import android.os.Looper;

import com.alibaba.fastjson.JSON;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * @author liudo
 */
public class JsonCallbackListener<T> implements CallbackListener{

    private Class<T> responseClass;
    Handler handler = new Handler(Looper.getMainLooper());
    private IJsonDataListener mIJsonDataListener;

    public JsonCallbackListener(Class<T> responseClass,IJsonDataListener listener){
        this.responseClass = responseClass;
        mIJsonDataListener = listener;
    }

    @Override
    public void onSuccess(InputStream inputStream) {
        String response = getContent(inputStream);
        final T clazz = JSON.parseObject(response,responseClass);
        handler.post(new Runnable() {
            @Override
            public void run() {
                mIJsonDataListener.onSuccess(clazz);
            }
        });

    }

    private String getContent(InputStream inputStream){
        String content=null;
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

            StringBuilder sb = new StringBuilder();

            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }

            } catch (IOException e) {

                System.out.println("Error=" + e.toString());

            } finally {

                try {

                    inputStream.close();

                } catch (IOException e) {

                    System.out.println("Error=" + e.toString());

                }

            }
            return sb.toString();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return content;
    }

    @Override
    public void onFailure() {

    }

}

请求任务

package com.tydfd.tydfdokhttp.util;



import com.alibaba.fastjson.JSON;
import com.tydfd.tydfdokhttp.http.UserBean;

import java.io.UnsupportedEncodingException;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * @author liudo
 */
public class HttpTask<T> implements Runnable, Delayed {

    private IHttpRequest mIHttpRequest;
    private UserBean mUserBean;

    public HttpTask(T requestData, String url, UserBean userBean,IHttpRequest httpRequest, CallbackListener callbackListener){
        this.mIHttpRequest = httpRequest;
        this.mUserBean = userBean;
        httpRequest.setUrl(url);
        httpRequest.setListener(callbackListener);
        String content = JSON.toJSONString(requestData);
        try {
            httpRequest.setData(content.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try{
            mIHttpRequest.execute(mUserBean);
        }catch (Exception e){
            /**
             * 将失败的任务添加到重试队列中
             */
            ThreadPoolManager.getInstance().addDelayTask(this);
        }

    }

    private long delayTime;
    private int retryCount;

    public int getRetryCount(){
        return retryCount;
    }

    public void setRetryCount(int retryCount) {
        this.retryCount = retryCount;
    }

    public long getDelayTime() {
        return delayTime;
    }

    public void setDelayTime(long delayTime) {
        // 设置延迟时间  3000
        this.delayTime = System.currentTimeMillis()+delayTime;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.delayTime - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        return 0;
    }
}
IJsonDataListener
package com.tydfd.tydfdokhttp.util;

public interface IJsonDataListener<T> {

    /**
     * 请求成功
     * @param m
     */
    void onSuccess(T m);

    /**
     * 请求失败
     */
    void onFailure();

}

请求方法封装

package com.tydfd.tydfdokhttp.http;


import com.tydfd.tydfdokhttp.util.CallbackListener;
import com.tydfd.tydfdokhttp.util.HttpTask;
import com.tydfd.tydfdokhttp.util.IHttpRequest;
import com.tydfd.tydfdokhttp.util.IJsonDataListener;
import com.tydfd.tydfdokhttp.util.JsonCallbackListener;
import com.tydfd.tydfdokhttp.util.JsonHttpRequest;
import com.tydfd.tydfdokhttp.util.ThreadPoolManager;

/**
 * @author liudo
 */
public class NeOkHttp {
    /**
     *
     * @param requestData 请求数据
     * @param url 请求url
     * @param userBean Basic认证
     * @param response
     * @param listener
     * @param <T>
     * @param <M>
     */
    public static<T,M> void sendJsonRequest(T requestData, String url,UserBean userBean,
                                            Class<M> response, IJsonDataListener listener){
        IHttpRequest httpRequest = new JsonHttpRequest();
        CallbackListener callbackListener = new JsonCallbackListener<>(response,listener);
        HttpTask httpTask = new HttpTask(requestData,url,userBean,httpRequest,callbackListener);
        ThreadPoolManager.getInstance().addTask(httpTask);
    }


}

MainActivity

package com.tydfd.httputils;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.util.TimeUtils;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.tydfd.tydfdokhttp.http.NeOkHttp;
import com.tydfd.tydfdokhttp.http.ResponseBean;
import com.tydfd.tydfdokhttp.http.TimeDateUtils;
import com.tydfd.tydfdokhttp.http.UserBean;
import com.tydfd.tydfdokhttp.util.IJsonDataListener;


/**
 * @author liudo
 */
public class MainActivity extends AppCompatActivity {
    private Button mButton;
    private TextView mTextView;
    /**
     * http://www.mxnzp.com/api/holiday/single/20181208 获取今日运势http://www.mxnzp.com/api/holiday/single/
     */
    private String url = "http://www.mxnzp.com/api/holiday/single/";
//    private String url2 = "http://192.168.120.194:8080/Demo/ResponseData";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = findViewById(R.id.button);
        mTextView = findViewById(R.id.textView);
        String time = TimeDateUtils.getCurrentDateStr(TimeDateUtils.FORMAT_TYPE_1);
        url = url+ time;
        mButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this, "点击了", Toast.LENGTH_SHORT).show();
                UserBean userBean = new UserBean();
                userBean.setUsername("");
                userBean.setPassword("123456");
                NeOkHttp.sendJsonRequest(null, url,userBean, ResponseBean.class, new IJsonDataListener<ResponseBean>() {
                    @Override
                    public void onSuccess(final ResponseBean rb) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                mTextView.setText(rb.getData());
                            }
                        });
                        Log.i("===> ",rb.toString());
                    }

                    @Override
                    public void onFailure() {

                    }
                });
            }
        });


    }


    public void Test(){
        TimeUtils.getTimeZoneDatabaseVersion();
    }

}

该http请求框架使用线程池控制线程访问队列中的请求,如果请求失败使用延迟队列将失败的请求加入,进而重新发起请求。大致思路就是上面的代码。不明白的欢迎交流。

发布了22 篇原创文章 · 获赞 4 · 访问量 4322

猜你喜欢

转载自blog.csdn.net/qq_38366111/article/details/97150235
今日推荐