Android 寫一個簡單的網絡請求框架

 

    雖然市面上有很多優秀的開源網絡框架,例如volley,Okhttp。爲啥還要自己來手寫一個呢?主要是爲了加深一下自己的記憶,還有把自己的技術點串聯起來。代碼並不難,都挺簡單的,有詳細的註釋。可以優化的地方很多,但是寫文的時候狀態不是特別好,所以就懶得去改了。再者,大部分情況下我也是會去用OKHttp的!寫下來是對自己花費時間的認可。

    我相信大部分朋友手寫一個簡單的網絡請求框架都沒什麼問題。不過溫故而知新嘛,我再寫一遍的時候,倒是發現了一些以前未曾注意的地方

    這個框架稍微注重了一下多併發的問題。但是沒有集合命令模式,所以沒有撤銷請求這個功能。

    第一個類是展示給應用層調用的靜態方法。寫成靜態方法是因爲我懶,喜歡的可以自己改成鏈式調用。這個方法需要傳入的參數也寫的很清楚啦!

public class SimpleHttp {

    /**
     * 訪問地址URL
     * 請求參數DATA
     * 回調函數 Listener
     * 需要的類型Cls
     */

    public static<T,K> void sendRequest(String url, T data, Class<K> cls, IHttpRequestListener listener){
        //構建一個請求對象
        IHttpRequest iHttpRequest=new HttpRequest();
        //構建一個返回回調
        IHttpResponseListener iHttpResponseListener=new HttpResponseListener<>(cls,listener);
        //構建一個請求線程
        HttpTask httpTask=new HttpTask(iHttpRequest,url,data,iHttpResponseListener);
        //構建一個線程管理
        ThreadManager.getInstance().addTask(httpTask);

    }

}

 這個是請求的對象接口,至少得滿足可擴展性嘛!

public interface IHttpRequest {

    //訪問的鏈接URL
    void setUrl(String Url);

    //請求的參數
    void setBytes(byte[] data);

    //請求返回的回調
    void setIDnHttpResponseListener(IHttpResponseListener iDnHttpResponseListener);

    //請求方法
    void execute();
}

 這個是請求的實現類啦!選的是HttpURLConnection 因爲足夠輕量,基本能滿足需求。請求方法我沒有封裝到外面,想要使用的可以自行封裝一下。

public class HttpRequest implements IHttpRequest {

    private String Url;
    private byte[] data;
    private IHttpResponseListener iDnHttpResponseListener;

    private HttpURLConnection httpURLConnection;

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

    }

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

    @Override
    public void setIDnHttpResponseListener(IHttpResponseListener iDnHttpResponseListener) {

        this.iDnHttpResponseListener=iDnHttpResponseListener;
    }

    @Override
    public void execute() {

        URL url = null;
        try {
            url = new URL(this.Url);
            httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setConnectTimeout(6000);  //連接超時時間
            httpURLConnection.setUseCaches(false);  //不使用緩存
            httpURLConnection.setInstanceFollowRedirects(true); //是成員變量 僅作用域當前函數,設置當前這個對象
            httpURLConnection.setReadTimeout(3000); //響應超時的時間
            httpURLConnection.setDoInput(true);  //設置這個連接對否可以寫入數據;
            httpURLConnection.setDoOutput(true);  //設置這個連接對否可以輸出數據;
            httpURLConnection.setRequestMethod("POST");    //設置這個請求的方法
            httpURLConnection.setRequestProperty("Content-Type","application/json;charset=UTF-8");
            httpURLConnection.connect();    //建立連接
            //---------------使用字節流發送數據---------------------------
            OutputStream out = httpURLConnection.getOutputStream();
            //緩衝字節流  包裝字節流
            BufferedOutputStream bos = new BufferedOutputStream(out);
            //把字節流數組寫入緩衝區中
            bos.write(data);
            //刷新緩衝區 發送數據
            bos.flush();
            out.close();
            bos.close();
            //如果響應碼爲200代表請求訪問成功
            if(httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK){
                InputStream in = httpURLConnection.getInputStream();
                //回調內部的回調接口
                iDnHttpResponseListener.onSuccess(in);
            }else{
                throw new RuntimeException("請求失敗");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("請求失敗");
        }finally {
            //關閉httpURLConnection對象
            httpURLConnection.disconnect();
        }


    }
}

這是返回時用的回調接口,實現2個方法即可。 


public interface IHttpResponseListener {

    //返回成功
    void onSuccess(InputStream inputStream);

    //反回失敗
    void onFailed();

}

 返回回調接口的實現類,把返回回來的參數轉換成自己想要的數據類型。

public class HttpResponseListener<T> implements IHttpResponseListener {

    private IHttpRequestListener listener;
    private Class<T> cls ;
    //切換線程
    Handler handler=new Handler(Looper.getMainLooper());

    public HttpResponseListener(Class<T>cls , IHttpRequestListener listener){
        this.cls=cls;
        this.listener=listener;
    }


    @Override
    public void onSuccess(InputStream inputStream) {
        String responseStr=getContent(inputStream);
        final T t= JSON.parseObject(responseStr,cls);
        handler.post(new Runnable() {
            @Override
            public void run() {
                listener.onSuccess(t);
            }
        });

    }

    @Override
    public void onFailed() {
        handler.post(new Runnable() {
            @Override
            public void run() {
                listener.onFailed();
            }
        });
    }


    /**
     * inputStream轉爲String類型
     * @param inputStream
     * @return
     */
    private String getContent(InputStream inputStream) {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuilder sb = new StringBuilder();
        String line = null;
        try {
            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line + "/n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sb.toString().replace("/n","");
    }

}

 線程池的管理類,這裏爲什麼要用LinkedBlockingQueue不用ArrayBlockingQueue的原因在於

鏈表持有2把鎖,可以同時進出,但是Array只有一把鎖。

在只進或者只出的時候,用數組會有明顯優勢,但是要滿足同時進出,鏈表會更快。

這裏還用了DelayQueue,這是一個延時列表。如果不設置延時時間,會出問題

雖然拒絕策略大部分時候都沒啥用,但是你不寫肯定是不行的

public class ThreadManager {
    private static ThreadManager threadManager=new ThreadManager();

    public static ThreadManager getInstance(){
        return threadManager;
    }

    //請求隊列
    private LinkedBlockingQueue<Runnable> requestQueue=new LinkedBlockingQueue<>();
    //失敗隊列
    private DelayQueue<Delayed> failedQueue=new DelayQueue<>();
    //線程池
    private ThreadPoolExecutor threadPoolExecutor;


    private ThreadManager(){

        threadPoolExecutor = new ThreadPoolExecutor(3, 10, 15,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                addTask(r);
            }
        });
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(failRunnable);
    }

    //添加線程
    public void addTask(Runnable runnable){
        if(runnable==null){
            return;
        }
        try {
            requestQueue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //添加失敗線程
    public void addFailedTask(HttpTask httpTask){
        if(httpTask==null){
            return;
        }

        httpTask.setDelayTIme(3000);
        failedQueue.offer(httpTask);
    }

    //核心線程
    private Runnable runnable=new Runnable() {
        @Override
        public void run() {
            while (true){
                try {
                    Runnable runnable=requestQueue.take();
                    threadPoolExecutor.execute(runnable);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

    //失敗核心線程
    private Runnable failRunnable=new Runnable() {
        @Override
        public void run() {
            while (true){
                try {
                    HttpTask httpTask= (HttpTask) failedQueue.take();

                    if(httpTask.getRetryTimes()<3){

                        httpTask.setRetryTimes(httpTask.getRetryTimes()+1);
                        threadPoolExecutor.execute(httpTask);
                    }else {
                        IHttpResponseListener httpResponseListener=httpTask.getListener();
                        httpResponseListener.onFailed();
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

}

 這個就是請求的線程啦!之後需要把請求線程交給線程池去管理。

public class HttpTask<T> implements Runnable, Delayed {
    private IHttpRequest iHttpRequest;
    private IHttpResponseListener listener;
    //延遲時間
    private long delayTIme;
    //重試次數
    private int retryTimes;


    public HttpTask(IHttpRequest iHttpRequest,String url,T Data,IHttpResponseListener listener){
        this.iHttpRequest=iHttpRequest;
        this.listener=listener;
        this.iHttpRequest.setUrl(url);
        this.iHttpRequest.setIDnHttpResponseListener(listener);

        if(Data!=null){
            String jsonStr= JSON.toJSONString(Data);
            try {
                this.iHttpRequest.setBytes(jsonStr.getBytes("UtF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

        }

    }


    @Override
    public void run() {
        try {
            this.iHttpRequest.execute();

        }catch (Exception e){

            ThreadManager.getInstance().addFailedTask(this);
        }
    }

    public IHttpResponseListener getListener() {
        return listener;
    }

    public long getDelayTIme() {
        return delayTIme;
    }

    public void setDelayTIme(long delayTIme) {
        this.delayTIme = delayTIme+System.currentTimeMillis();
    }

    public int getRetryTimes() {
        return retryTimes;
    }

    public void setRetryTimes(int retryTimes) {
        this.retryTimes = retryTimes;
    }

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

    @Override
    public int compareTo(Delayed o) {
        return 0;
    }
}

 

    大部分地方都加了註釋,加上代碼本身難度也不高。如果還有什麼問題也可以直接問我。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章