Http工具類

網絡請求在安卓開發中非常常見,寫一個工具類能有效的節省開發時間提高效率,這裏就分享一下,有不足的請斧正

引入

implementation("com.squareup.okhttp3:okhttp:4.2.1")

需要的權限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />

java版本

import android.app.Application;
import android.os.Environment;
import android.widget.ProgressBar;

import org.jetbrains.annotations.NotNull;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import okhttp3.Cache;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.BufferedSink;


public class HttpHelper {

    private static Application httpApplication;

    private static final String MEDIA_TYPE = "application/json; charset=utf-8";

    private static final Long MAX_CACHE_SIZE = 1024 * 1024 * 50L;// 50M 的緩存大小

    private static final Long DEFAULT_TIMEOUT = 15L;

    private static class HttpHelperHolder {
        private static final HttpHelper INSTANCE = new HttpHelper();
    }

    private HttpHelper() {
    }

    public static HttpHelper getInstance(Application application) {
        httpApplication = application;
        return HttpHelperHolder.INSTANCE;
    }

    private OkHttpClient getOkHttpClient() {
        OkHttpClient.Builder builder = new OkHttpClient().newBuilder();

        //設置 請求的緩存的大小跟位置
        File cacheFile = new File(httpApplication.getCacheDir(), "cache");
        Cache cache = new Cache(cacheFile, MAX_CACHE_SIZE);
        builder.cache(cache)
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true);

        return builder.build();
    }

    /**
     * post方式提交鍵值對
     * 參數1:[url] url ,參數2:[body] 鍵值對
     * 返回 可空字符串
     */
    public String post(String url, RequestBody body) {
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();

        try (Response response = getOkHttpClient().newCall(request).execute()) {
            ResponseBody responseBody = response.body();
            if (responseBody != null) {
                return responseBody.string();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * post方式提交json字符串
     * 參數1:[url] url,參數2:[json] json字符串
     * 返回可空字符串
     */
    public String post(String url, String json) {
        ///okHttp4 拓展功能(Kotlin)
        ///String.toMediaType()替換MediaType.get(String),需要導入import okhttp3.MediaType.Companion.toMediaType
        ///String.toRequestBody(MediaType)替換RequestBody.create(String,MediaType),import okhttp3.RequestBody.Companion.toRequestBody
        return post(url, RequestBody.create(json, MediaType.get(MEDIA_TYPE)));
    }

    /**
     * post方式提交流
     * 參數1:[url] url,參數2:[bis] 緩衝字節流
     * 返回可空字符串
     */
    public String post(String url, final BufferedInputStream bis) {
        RequestBody body = new RequestBody() {
            @Override
            public void writeTo(@NotNull BufferedSink bufferedSink) {
                byte[] bytes = new byte[1024 * 8];
                int length;
                try {
                    while ((length = bis.read(bytes)) != -1) {
                        bufferedSink.write(bytes, 0, length);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        bis.close();
                        bufferedSink.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            @NotNull
            @Override
            public MediaType contentType() {
                return MediaType.get(MEDIA_TYPE);
            }

        };
        return post(url, body);
    }

    /**
     * post方式提交文件
     * 參數1:[url] url,參數2:[file] 文件
     * 返回可空字符串
     */
    public String post(String url, File file) {
        //okHttp4 拓展功能
        //需要import okhttp3.RequestBody.Companion.asRequestBody
        //替換RequestBody.create(MediaType.parse("text/x-markdown; charset=utf-8"), file)
        RequestBody body = RequestBody.create(MediaType.parse("text/x-markdown; charset=utf-8"), file);
        return post(url, body);
    }

    /**
     * Post方式提交分塊請求,可以上傳多個文件
     * 參數1:[imageUrl] url,參數2:[file] 多個文件
     * 返回可空字符串
     */
    public String post(String url, File... files) {
        MultipartBody.Builder requestBody = new MultipartBody.Builder()
                //設置分塊提交模式
                .setType(MultipartBody.FORM)
                //分塊提交,標題
                .addFormDataPart("title", "block");

        for (File file : files) {
            requestBody.addFormDataPart(file.getName(), file.getName(), RequestBody.create(MediaType.parse("image/png"), file));
        }

        return post(url, requestBody.build());
    }

    /**
     * 下載文件
     * 參數1:[url] 文件url,參數2:[file] 保存到指定目錄
     * 必須在線程中調用,不然主線程會卡住,返回是否下載成功布爾值
     */
    public boolean downloadFile(String url, File file) {
        Request request = new Request.Builder()
                .url(url)
                .build();

        try (Response response = getOkHttpClient().newCall(request).execute()) {
            ResponseBody responseBody = response.body();
            if (responseBody != null) {
                try (BufferedInputStream bis = new BufferedInputStream(responseBody.byteStream()); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {
                    byte[] bytes = new byte[1024 * 8];
                    int length;
                    while ((length = bis.read(bytes)) != -1) {
                        bos.write(bytes, 0, length);
                    }
                    return true;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 順序下載
     * 需要參數:url
     * 必須在線程中調用,不然主線程會卡住,返回是否下載成功布爾值
     */
    public boolean sequentialDownload(String... urls){
        //失敗次數
        int failure = 0;

        for (String url:urls){
            if (failure == 3){
                return false;
            }
            String[] fileNames = url.split("/");
            if (fileNames.length > 0){
                File file = createFilePath(fileNames[fileNames.length - 1]);
                if (downloadFile(url,file)){
                    failure = 0;
                }else {
                    failure++;
                }
            }
        }
        return true;
    }

    /**
     * 順序下載
     * 需要參數:[progressBar]進度條 [url]url
     * 必須在線程中調用,不然主線程會卡住,返回是否下載成功布爾值
     */
    public boolean sequentialDownload(ProgressBar progressBar,String... urls){
        //失敗次數
        int failure = 0;
        int i = 0;
        progressBar.setMax(100);

        for (String url:urls){
            if (failure == 3){
                return false;
            }
            String[] fileNames = url.split("/");
            if (fileNames.length > 0){
                File file = createFilePath(fileNames[fileNames.length - 1]);
                if (downloadFile(url,file)){
                    failure = 0;
                    i++;
                    int progress = (int) (i * 1.0f / urls.length * 100);
                    progressBar.setProgress(progress);
                }else {
                    failure++;
                }
            }
        }
        return true;
    }

    /**
     * 創建文件路徑
     */
    public File createFilePath(String fileName){
        String path = Objects.requireNonNull(httpApplication.getExternalFilesDir(Environment.DIRECTORY_PICTURES)).getAbsolutePath();
        File file = new File(path,fileName);

        try {
            //如果有同名文件則刪除
            if (file.exists()){
                file.delete();
            }
            if (file.createNewFile()){
                return file;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null ;
    }
}

Kotlin版本

import android.app.Application
import android.widget.ProgressBar

import java.io.BufferedInputStream
import java.io.File
import java.io.IOException
import java.util.Objects
import java.util.concurrent.TimeUnit

import okhttp3.Cache
import okhttp3.MediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okio.BufferedSink
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.asRequestBody

import android.os.Environment.DIRECTORY_PICTURES

class HttpHelper private constructor() {

    private val okHttpClient: OkHttpClient
        get() {
            val builder = OkHttpClient().newBuilder()
            val cacheFile = File(httpApplication.cacheDir, "cache")
            val cache = Cache(cacheFile, MAX_CACHE_SIZE)
            builder.cache(cache)//設置 請求的緩存的大小跟位置
                    .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                    .readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                    .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                    .retryOnConnectionFailure(true)

            return builder.build()
        }

    private object HttpHelperHolder {
        val INSTANCE = HttpHelper()
    }

    /**
     * post方式提交鍵值對
     * 參數1:[url] url ,參數2:[body] 鍵值對
     * 返回 可空字符串
     */
    fun post(url: String, body: RequestBody): String {
        val request = Request.Builder()
                .url(url)
                .post(body)
                .build()

        try {
            okHttpClient.newCall(request).execute().use { response ->
                val responseBody = response.body
                if (responseBody != null) {
                    return responseBody.string()
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        return ""
    }

    /**
     * post方式提交json字符串
     * 參數1:[url] url,參數2:[json] json字符串
     * 返回可空字符串
     */
    fun post(url: String, json: String): String {
        ///okHttp4 拓展功能(Kotlin)
        ///String.toMediaType()替換MediaType.get(String),需要導入import okhttp3.MediaType.Companion.toMediaType
        ///String.toRequestBody(MediaType)替換RequestBody.create(String,MediaType),import okhttp3.RequestBody.Companion.toRequestBody
        return post(url, json.toRequestBody(MEDIA_TYPE.toMediaType()))
    }

    /**
     * post方式提交流
     * 參數1:[url] url,參數2:[bis] 緩衝字節流
     * 返回可空字符串
     */
    fun post(url: String, bufferedInputStream: BufferedInputStream): String {
        val body = object : RequestBody() {
            override fun writeTo(sink: BufferedSink) {
                try {
                    bufferedInputStream.use { bis ->
                        sink.outputStream().buffered().use { bos ->
                            bis.copyTo(bos)
                        }
                    }
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }

            override fun contentType(): MediaType {
                return MEDIA_TYPE.toMediaType()
            }

        }
        return post(url, body)
    }

    /**
     * post方式提交文件
     * 參數1:[url] url,參數2:[file] 文件
     * 返回可空字符串
     */
    fun post(url: String, file: File): String {
        //okHttp4 拓展功能
        //file.asRequestBody需要import okhttp3.RequestBody.Companion.asRequestBody
        //替換RequestBody.create(MediaType.parse("text/x-markdown; charset=utf-8"), file)
        return post(url, file.asRequestBody("text/x-markdown; charset=utf-8".toMediaType()))
    }

    /**
     * Post方式提交分塊請求,可以上傳多個文件
     * 參數1:[imageUrl] url,參數2:[file] 多個文件
     * 返回可空字符串
     */
    fun post(url: String, vararg files: File): String {
        val requestBody = MultipartBody.Builder()
                //設置分塊提交模式
                .setType(MultipartBody.FORM)
                //分塊提交,標題
                .addFormDataPart("title", "block")

        for (file in files) {
            requestBody.addFormDataPart(file.name, file.name, file.asRequestBody("image/png".toMediaType()))
        }

        return post(url, requestBody.build())
    }

    /**
     * 下載文件
     * 參數1:[url] 文件url,參數2:[file] 保存到指定目錄
     * 必須在線程中調用,不然主線程會卡住,返回是否下載成功布爾值
     */
    fun downloadFile(url: String, file: File): Boolean {
        val request = Request.Builder()
                .url(url)
                .build()

        try {
            okHttpClient.newCall(request).execute().use { response ->
                val responseBody = response.body
                if (responseBody != null) {
                    try {
                        responseBody.byteStream().buffered().use { bis ->
                            file.outputStream().buffered().use { bos ->
                                bis.copyTo(bos)
                                return true
                            }
                        }
                    } catch (e: IOException) {
                        e.printStackTrace()
                    }
                }
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return false
    }

    /**
     * 順序下載
     * 需要參數:url
     * 必須在線程中調用,不然主線程會卡住,返回是否下載成功布爾值
     */
    fun sequentialDownload(vararg urls: String): Boolean {
        //失敗次數
        var failure = 0

        for (url in urls) {
            if (failure == 3) {
                return false
            }
            val fileNames = url.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
            if (fileNames.isNotEmpty()) {
                val file = createFilePath(fileNames[fileNames.size - 1])
                if (downloadFile(url, file)) {
                    failure = 0
                } else {
                    failure++
                }
            }
        }
        return true
    }

    /**
     * 順序下載
     * 需要參數:[progressBar]進度條 [url]url
     * 必須在線程中調用,不然主線程會卡住,返回是否下載成功布爾值
     */
    fun sequentialDownload(progressBar: ProgressBar, vararg urls: String): Boolean {
        //失敗次數
        var failure = 0
        var i = 0
        progressBar.max = 100

        for (url in urls) {
            if (failure == 3) {
                return false
            }
            val fileNames = url.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
            if (fileNames.isNotEmpty()) {
                val file = createFilePath(fileNames[fileNames.size - 1])
                if (downloadFile(url, file)) {
                    failure = 0
                    i++
                    val progress = (i * 1.0f / urls.size * 100).toInt()
                    progressBar.progress = progress
                } else {
                    failure++
                }
            }
        }
        return true
    }

    /**
     * 創建文件路徑
     */
    fun createFilePath(fileName: String): File {
        val path = Objects.requireNonNull<File>(httpApplication.getExternalFilesDir(DIRECTORY_PICTURES)).absolutePath
        val file = File(path, fileName)
        //如果有同名文件則刪除
        if (file.exists()) {
            file.delete()
        }
        file.createNewFile()
        return file
    }

    companion object {

        private lateinit var httpApplication: Application

        private const val MEDIA_TYPE = "application/json; charset=utf-8"

        private const val MAX_CACHE_SIZE = 1024 * 1024 * 50L// 50M 的緩存大小

        private const val DEFAULT_TIMEOUT = 15L

        fun getInstance(application: Application): HttpHelper {
            httpApplication = application
            return HttpHelperHolder.INSTANCE
        }
    }
}

 

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