阅读 847

OkHttp3入门-手把手教你如何用

总体概述

准备工作
简单创建
GET请求
POST请求
下载(显示进度)
上传(显示进度)
其他
复制代码

OkHttp是一个处理网络请求的开源项目; 安卓端轻量级框架; 用于替代HttpUrlConnection和Apache HttpClient;

准备工作

添加依赖

//网络请求OkHttp3
implementation 'com.squareup.okhttp3:okhttp:4.3.1'
复制代码

设置网络权限

<uses-permission android:name="android.permission.INTERNET" />
复制代码

API级别28或更高级别手机,在http请求时(非Https请求)会有错误提示,并且请求不到数据。

CLEARTEXT communication to www.baidu.com not permitted by network security policy
复制代码

需要在AndroidManifest的application中设置android:usesCleartextTraffic="true"

创建

okhttp提供有同步、异步两种请求方式,差别不大 同步请求时在请求过程中线程处于堵塞状态,不建议放在主线程中运行,容易出现ANR(Application Not responding)(应用程序没有响应)。

简单创建四步走

1、创建OkHttpClient对象
2、通过Builder模式创建Request对象
3、通过request的对象去构造得到一个Call对象
4、执行请求,调用call.enqueue()执行异步请求,调用call .execute()执行同步请求
复制代码

异步请求

OkHttpClient client = new OkHttpClient();	//实例化OkHttpClient 对象
Request request = new Request.Builder()		//创建Request对象的Builder模式
		.url("http://www.baidu.com")	//设置访问的网址
		//.get()		//设置为GET请求  默认为GET请求,可以忽略
		.build();	//创建Request对象
Call call = client.newCall(request);	//通过request的对象去构造得到一个Call对象
call.enqueue(new Callback() {	//调用call的enqueue(),进行异步请求
	@Override
	public void onFailure(Call call, IOException e) {
		//错误回调,通过e.getMessage()可以拿到错误信息
		Log.d("onFailure","onFailure: "+e.getMessage());
	}

	@Override
	public void onResponse(Call call, Response response) throws IOException {
		//成功回调,在这里我们可以拿到服务器返回的数据
		Log.d("onResponse","onResponse: "+response.body().string());
	}
});
复制代码

同步请求

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
         .url("http://www.baidu.com")
         .get()
         .build();
 Call call = client.newCall(request);
 Response response = null; 
 try {
     response = call .execute();//调用call的execute()进行同步请求,在请求过程中处于堵塞状态,建议写在子线程中
     Log.d("onResponse","onResponse: "+response.body().string());
 } catch (IOException e) {
     e.printStackTrace();
     Log.d("onFailure", "onFailure: "+e.getMessage());
 }
复制代码
onResponse回调有一个参数是response为响应数据
如果返回的数据是字符串,可以通过response.body().string()得到
如果返回的数据是二进制字节数组,可以通过response.body().bytes()得到
如果返回的数据是inputStream,可以通过response.body().byteStream()得到
复制代码

中止请求

调用call的cancel()进行中止,停止请求 call.cancel();

具体介绍

OkHttp支持PUT,DELETE,POST,GET等请求; 文件的上传下载 以下都使用异步请求进行

GET请求

GET请求比较简单,就是上面的简单四步

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
		.url("")
		.get()
		.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
	@Override
	public void onFailure(@NotNull Call call, @NotNull IOException e) {

	}
	@Override
	public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {

	}
});
复制代码

POST请求

POST请求支持提交文件,流,string,表单等等 简单的POST请求(提交表单)

OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder()//创建表单
		.add("key","value")		//传入需要提交的数据
		.add("key","value")		//传入需要提交的数据
		.build();		//创建请求体
Request request = new Request.Builder()	
		.url("")		//设置提交数据的网址
		.post(requestBody)	//设置为POST请求,传入请求体
		.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {	//异步请求
	@Override
	public void onFailure(@NotNull Call call, @NotNull IOException e) {

	}
	@Override
	public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {

	}
});
复制代码

下载(保存到本地)

其实很简单,就是GET请求或POST请求 只是将返回的数据转为流(response.body().byteStream()),再通过InputStream(), OutputStream()保存本地即可。

简单的下载请求(示例)

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
        .url("http://image.baidu.com/search/down?tn=download&ipn=dwnl&word=download" +
                        "&ie=utf8&fr=result&url=http%3A%2F%2Fc.hiphotos.baidu.com%2Fzhidao%2" +
                        "Fpic%2Fitem%2Fd009b3de9c82d1587e249850820a19d8bd3e42a9.jpg&thumburl=" +
                        "http%3A%2F%2Fimg1.imgtn.bdimg.com%2Fit%2Fu%3D3675415932%2C4054970339%26" +
                        "fm%3D26%26gp%3D0.jpg")	//百度上的图片
        .get()
        .build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(@NotNull Call call, @NotNull IOException e) {
		Log.d("onFailure", "onFailure: " + e.getMessage());
    }
    @Override
    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
        String path = Environment.getExternalStorageDirectory().getPath()+"/Download";//文件路径,根目录Download目录下
        File file = new File(path,"image.jpg");//设置文件路劲及文件名
        InputStream is = response.body().byteStream();
        BufferedInputStream bis = new BufferedInputStream(is);
        FileOutputStream fos = new FileOutputStream(new File(path));
        byte[] bytes = new byte[1024];
        int len;
        Log.d("onResponse", "onResponse: 开始");
        while ((len=is.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }
        fos.flush();
        Log.d("onResponse", "onResponse: 结束");
        is.close();
        bis.close();
        fos.close();
    }
});
复制代码

准备工作

权限获取 获取SD卡的存取权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
复制代码

Android 6.0 注意动态授权 Google在 Android 6.0 开始引入了权限申请机制,使用危险权限时需要动态的申请并得到用户的授权才能使用。存取权限属于危险权限。

如何动态获取? 这里提供一个工具类,用于动态授权

public class PermissionUtil {
    public static String READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE;     //内存读取
    public static String WRITE_EXTERNAL_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE;   //内存写入
    
    public static void getPermissions(Activity activity, String... permission) {
        
        List<String> permissions = new ArrayList<>();
        //此处做动态权限申请
        //判断系统是否大于等于Android 6.0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            for(int i=0;i<permission.length;i++){
                int request = ContextCompat.checkSelfPermission(activity, permission[i]);
                //判断是否未获取权限
                if (request != PackageManager.PERMISSION_GRANTED)
                    permissions.add(permission[i]);
            }
            if (permissions.size()>0) {//缺少权限,进行权限申请
                //当前上下文;一个权限数组;一个唯一的请求码(0~65535的16位数)
                ActivityCompat.requestPermissions(activity,  permissions.toArray(new String[permissions.size()]), 0XFF);
            } else {
                //权限同意 已全部授权
            }
        } else {
            //低于api 23 不需要动态授权
        }
    }

}
复制代码

在MainActivity的onCreate()中使用,进行动态授权

PermissionUtil.getPermissions(this, PermissionUtil.READ_EXTERNAL_STORAGE, PermissionUtil.WRITE_EXTERNAL_STORAGE);
复制代码

在这里插入图片描述

显示下载进度

下载一定要有下载进度的 显示下载进度有两种方法:

  1. 在onResponse回调中实时获取
  2. 对网络响应ResponseBody进行来代理

第一种比较简单(仅限当前可用,与其他框架联用可能会失效,如:Retrofit+OkHttp3+RxJava2联合使用时会失效,因为Retrofit的关系)

第二种有点复杂,涉及到拦截器,直接贴代码

第二种:对网络响应ResponseBody进行来代理 activity_main

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="下载" />
</LinearLayout>
复制代码

MainActivity

public class MainActivity extends AppCompatActivity {

    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Android 6.0 及以上系统需要动态申请,PermissionUtil工具已经在上面贴出,这里进行动态申请
        PermissionUtil.getPermissions(this, PermissionUtil.READ_EXTERNAL_STORAGE, PermissionUtil.WRITE_EXTERNAL_STORAGE);

        progressBar = findViewById(R.id.progressBar);
        //设置按钮点击事件
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                download();//点击后进行下载
            }
        });

    }

    public void download(){
        String path = Environment.getExternalStorageDirectory().getPath() + "/Download/qq.exe";//保存的路径及文件名
        String url = "https://qd.myapp.com/myapp/qqteam/pcqq/PCQQ2020.exe";//这里给的是QQPC版的下载地址,文件较大
        //设置下载路径和下载网址,开始下载
        OkHttpHelper.getInstance().setDownloadUrl(url).startDownload(path,new OkHttpHelper.DownloadCallback() {
            @Override
            public void start(final long max) {
            	//这里处于子线程,需返回主线程更新UI
                progressBar.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(MainActivity.this, "开始下载", Toast.LENGTH_SHORT).show();
                        progressBar.setMax((int) max);
                    }
                });
            }

            @Override
            public void loading(final long progress) {
                progressBar.post(new Runnable() {
                    @Override
                    public void run() {
                        progressBar.setProgress((int) progress);
                    }
                });
            }

            @Override
            public void complete(String msg) {

            }

            @Override
            public void fail(String message) {

            }
        });
    }
}
复制代码

OkHttpHelper

public class OkHttpHelper {
    private OkHttpClient client;
    private Request request;

    private static OkHttpHelper instance = null;
    private OkHttpHelper() {
        initOkHttp();//初始化
    }

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

    private void initOkHttp() {
        client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .build();
    }
    //设置问GET请求,设置下载网址
    OkHttpHelper setDownloadUrl(String url) {
        request = new Request.Builder()
                .url(url)
                //.header("RANGE","")//断点续传时需要使用的
                .get()
                .build();
        return this;
    }
    //设置下载路径,开始下载
    public void startDownload(final String path,final DownloadCallback callback) {
        if (client == null) {
            initOkHttp();
        }
        OkHttpClient okHttpClient = client.newBuilder().addInterceptor(new MyInterceptor(callback)).build();//插入自定义的MyInterceptor()拦截器
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                callback.fail(e.getMessage());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                download(response,path);
                callback.complete("完成");
            }
        });
    }
    //创建MyInterceptor类,实现Interceptor 拦截器
    static class MyInterceptor implements Interceptor {
        DownloadCallback callback;
        public MyInterceptor(DownloadCallback callback) {
            super();
            this.callback = callback;
        }

        @NotNull
        @Override
        public Response intercept(@NotNull Chain chain) throws IOException {
            Response response = chain.proceed(chain.request());
            return response.newBuilder().body(new MyResponseBody(response,callback)).build();
        }
    }
    //创建MyResponseBody 类,继承 ResponseBody类
    static class MyResponseBody extends ResponseBody {
        Response response;
        DownloadCallback callback;

        MyResponseBody(Response response,DownloadCallback callback) {
            super();
            this.response = response;
            this.callback = callback;
        }

        @Override
        public long contentLength() {
            //获取文件的总字节数
            callback.start(response.body().contentLength());
            return response.body().contentLength();
        }

        @Nullable
        @Override
        public MediaType contentType() {
            //可以获取文件的类型
            return response.body().contentType();
        }

        @NotNull
        @Override
        public BufferedSource source() {
            contentLength();
            contentType();
            //可以获取文件资源
            return Okio.buffer(new ForwardingSource(response.body().source()) {
                long bytesLength=0;
                @Override
                public long read(@NotNull Buffer sink, long byteCount) throws IOException {
                    //获取下载进度
                    final long bytesRead = super.read(sink, byteCount);
                    bytesLength += bytesRead;
                    callback.loading(bytesLength);
                    return bytesRead;
                }
            });
        }
    }
    //注:MyResponseBody里的三个接口contentLength,contentType,source,并不会程序自己主动调用执行,需要人为被动执行;
    //source接口的执行,在程序执行到download方法的“InputStream is = response.body().byteStream();”时才会被调用;
    // 然后我们自己调用contentLength(),contentType()方法去执行,
    // 或者我们在download方法的“InputStream is = response.body().byteStream();”上面添加两行代码response.body().contentLength();,response.body().contentType();
    //下载,执行下载、保存
    public static void download(Response response,String path) throws IOException {
        //response.body().contentLength();
        //response.body().contentType();
        InputStream is = response.body().byteStream();//拿到资源,转为字节流
        BufferedInputStream bis = new BufferedInputStream(is);
        FileOutputStream fos = new FileOutputStream(new File(path));//设置需要保存的位置,及文件
        byte[] bytes = new byte[1024];
        int len;
        while ((len = is.read(bytes)) != -1) {
            fos.write(bytes, 0, len);//保存
        }
        fos.flush();
        is.close();
        bis.close();
        fos.close();
    }
    //一些必要的接口
    public interface DownloadCallback {	//各种下载需要的接口
        //开始下载
        void start(long max);			//传入文件总字节数
        //正在下载
        void loading(long progress);	//传入已下载的字节数
        //下载完成
        void complete(String msg);		//传入成功信息
        //请求失败
        void fail( String message);		//传入错误信息
    }
}   
复制代码

注:
1、MyResponseBody里的三个接口方法contentLength(),contentType(),source(),并不会程序自己主动调用执行,需要人为被动执行;
2、source()的执行,是在程序执行到download方法的“InputStream is = response.body().byteStream();”时才会被调用;
3、然后我们自己调用contentLength(),contentType()去执行;
4、或者我们在download方法里添加两行代码response.body().contentLength();,response.body().contentType();,也会执行。

第一种:在onResponse回调中实时获取
修改OkHttpHelper 即可

public class OkHttpHelper {
    private static OkHttpHelper instance = null;
    private OkHttpClient client;
    private Request request;

    private OkHttpHelper() {
        initOkHttp();//初始化
    }

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

    OkHttpHelper setDownloadUrl(String url) {
        request = new Request.Builder()
                .url(url)
                //.header("RANGE","")//断点续传时需要使用的
                .get()
                .build();
        return this;
    }

    private void initOkHttp() {
        client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .build();
    }

    void startDownload(final String path, final DownloadCallback callback) {
        if (client == null) {
            initOkHttp();
        }
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                callback.fail(e.getMessage());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                downlaod(response, path, callback);
                callback.complete("完成");
            }
        });
    }

    public static void downlaod(Response response, String path, DownloadCallback callback) throws IOException {
        long max = response.body().contentLength();
        callback.start(max);
        InputStream is = response.body().byteStream();
        BufferedInputStream bis = new BufferedInputStream(is);
        FileOutputStream fos = new FileOutputStream(new File(path));
        byte[] bytes = new byte[1024];
        int len;
        long length = 0;//记录已下载的字节数
        while ((len = is.read(bytes)) != -1) {
            length += len;
            fos.write(bytes, 0, len);
            callback.loading(length);
        }
        fos.flush();
        is.close();
        bis.close();
        fos.close();
    }

    public interface DownloadCallback {
        //开始下载
        void start(long max);//传入文件总字节数

        //正在下载
        void loading(long progress);//传入已下载的字节数

        //下载完成
        void complete(String msg);

        //请求失败
        void fail(String message);
    }
}
复制代码

上传

文件上传

上传文件之前要设置上传文件的类型
例:MediaType.parse("text/plain; charset=utf-8")

text/html				html
text/plain				文本文档
text/xml				xml
image/gif				gif
image/jpeg			jpg
image/png			png
application/xhtml-xml		XHTML
application/json				json
application/pdf					pdf
application/msword				word文档
application/octet-stream		二进制流
复制代码

文件上传(简单示例)

OkHttpClient client = new OkHttpClient();//实例化
//拿到上传的文件
File file = new File(Environment.getExternalStorageDirectory(),"/Download/qq.exe");
//设置上传类型
MediaType type = MediaType.parse("");
//设置请求体
RequestBody body = new MultipartBody.Builder()
		.setType(MultipartBody.FORM)
		.addFormDataPart("","")
		//将文件类型和文件放入请求体
		.addFormDataPart("","",RequestBody.create(file, type))
		.build();
//RequestBody body = RequestBody.create(file, type);
//设置上传网址(要支持上传的网址),放入请求体
Request request = new Request.Builder()
		.url("")
		.post(body)
		.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {//异步请求
	@Override
	public void onFailure(@NotNull Call call, @NotNull IOException e) {

	}
	@Override
	public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {

	}
});
复制代码

涉及到的类与方法

new RequestBody();
	RequestBody.create(byte[]);
	RequestBody.create(File, MediaType);		//上传文件是用到
	RequestBody.create(byte[], MediaType);	//上传二进制字节时用到
	RequestBody.create(String, MediaType);	//上传文本时用到
	RequestBody.create(ByteString, MediaType);	//上传字节流时用到
	RequestBody.create(byte[], MediaType,int offset);			// offset   从offset开始上传
	RequestBody.create(byte[], MediaType,int offset,int byteCount );		// offset, byteCount  从offset开始,到offset后byteCount字节结束,上传
	
new MultipartBody();
	new MultipartBody.Builder();
		builder.setType(MediaType);  //MultipartBody.FORM 设置类型是表单
		builder.addFormDataPart(String, String);		//添加数据
		builder.addFormDataPart(String, String, RequestBody);		//添加上传的文件及必要信息
		builder.build();		//返回RequestBody 
		
new MediaType();
	MediaType.parse(String);	//设置上传的文件的类型
复制代码

上传监听(显示上传进度)

对网络请求RequestBody进行来代理

这里直接上代码

activity_main 同上

MainActivity

public class MainActivity extends AppCompatActivity {

    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        PermissionUtil.getPermissions(this, PermissionUtil.READ_EXTERNAL_STORAGE, PermissionUtil.WRITE_EXTERNAL_STORAGE);

        progressBar = findViewById(R.id.progressBar);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                upload();
            }
        });

    }

    public void upload() {
        Map<String, String> map = new HashMap<>();
        String path = Environment.getExternalStorageDirectory().getPath() + "/Download/123.jpg";
        File file = new File(path);
        String url = "";
        OkHttpHelper.getInstance().setUploadUrl(url).startUpload(map, file, new OkHttpHelper.UploadCallback() {
            @Override
            public void start(long max) {

            }

            @Override
            public void loading(long progress) {

            }

            @Override
            public void complete(String msg) {

            }

            @Override
            public void fail(String message) {

            }
        });
    }
}
复制代码

OkHttpHelper

public class OkHttpHelper {
    private static OkHttpHelper instance = null;
    private OkHttpClient client;
    private Request request;

    private OkHttpHelper() {
        initOkHttp();//初始化
    }

    static OkHttpHelper getInstance() {//单例模式
        if (instance == null) {
            synchronized (OkHttpHelper.class) {
                if (instance == null) {
                    instance = new OkHttpHelper();
                }
            }
        }
        return instance;
    }
    
    public OkHttpHelper setUploadUrl(String url) {
        request = new Request.Builder()
                .url(url)
                .build();
        return this;
    }

    public void startUpload(Map<String, String> map, File file,final UploadCallback callback) {
    	//重构Request,在.post()中传入的请求体
        Request request = this.request.newBuilder().post(new MyRequestBody(setUploadBody(map, file),callback)).build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                callback.fail(e.getMessage());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                callback.complete(response.body().string());
            }
        });
    }
	
    private static class MyRequestBody extends RequestBody {
        RequestBody body = null;
        UploadCallback callback;
        public MyRequestBody(MultipartBody multipartBody, UploadCallback callback) {
            super();
            body = multipartBody;
            this.callback = callback;
        }

        @Nullable
        @Override
        public MediaType contentType() {
            return body.contentType();
        }

        @Override
        public long contentLength() throws IOException {
            callback.start(body.contentLength());
            return body.contentLength();
        }

        @Override
        public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException {
            //包装
            Sink sk = sink(bufferedSink);
            bufferedSink = Okio.buffer(sk);
            //写入
            body.writeTo(bufferedSink);
            //必须调用flush,否则最后一部分数据可能不会被写入
            bufferedSink.flush();

        }
        /**
         * 写入,回调进度接口
         *
         * @param sink Sink
         * @return Sink
         */
        private Sink sink(Sink sink) {
            return new ForwardingSink(sink) {
                long bytesWritten = 0L;
                @Override
                public void write(Buffer source, long byteCount) throws IOException {
                    super.write(source, byteCount);
                    //获取上传进度
                    bytesWritten += byteCount;
                    callback.loading(bytesWritten);
                }
            };
        }

    }
    //将需要上传的数据进行打包
    private MultipartBody setUploadBody(Map<String, String> map, File file) {
        MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        RequestBody fileBody = RequestBody.create(file, MediaType.parse("image/png"));
        if (map != null && map.size() > 0) {
            for (String key : map.keySet()) {
                String value = map.get(key);
                if (value != null) {
                    builder.addFormDataPart(key, value);
                }
            }
        }
        return builder.addFormDataPart("file", "fileName", fileBody).build();
    }

    public interface UploadCallback {
        //开始上传
        void start(long max);//传入文件总字节数

        //正在上传
        void loading(long progress);//传入已下载的字节数

        //上传完成
        void complete(String msg);

        //请求失败
        void fail(String message);
    }
}
复制代码

其他

拦截器

拦截器是一种强大的机制,可以监视、重写和重试调用。

//这是一个最简单的拦截器
static class MyInterceptor implements Interceptor {
    @NotNull
    @Override
    public Response intercept(@NotNull Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);
        return response;
		//return chain.proceed(chain.request());
    }
}
	    
//如何使用
OkHttpHelper client = new OkHttpClient.Builder()
		.addInterceptor(new MyInterceptor())		//注册应用拦截器
		.addNetworkInterceptor()//注册网络拦截器
		.build();
复制代码

调用chain.proceed(request)是每个拦截器实现的关键部分。这种简单的方法是所有HTTP工作发生的地方,它会产生一个响应来满足请求。如果chain.proceed(request)被多次调用,则必须关闭先前的响应主体。(官方翻译)

应用拦截器
无需担心中间响应,例如重定向和重试。
即使从缓存提供HTTP响应,也总是被调用一次。
遵守应用程序的原始意图。不关心OkHttp注入的标头,例如If-None-Match。
允许短路而不是Chain.proceed()。
允许重试并多次致电Chain.proceed()。

网络拦截器
能够对重定向和重试之类的中间响应进行操作。
不会为使网络短路的缓存响应调用。
观察数据,就像通过网络传输数据一样。
访问Connection带有请求的。

重试及重定向拦截器 RetryAndFollowUpInterceptor
桥接拦截器 BridgeInterceptor
缓存拦截器 CacheInterceptor
连接拦截器 ConnectInterceptor
读写拦截器 CallServerInterceptor

日志打印 LoggerInterceptor
打印网络请求的头信息
打印请求的网址
缓存:cache-control, max-age=xxx

可能涉及到的类与方法

new Cache(File,long,FileSystem); 		//设置缓存数使用,long有效缓存时长
new Cache(File,long);		//设置缓存数使用

new OkHttpClient();
	newBuilder();自定义一个共享的OkHttpClient实例
	
	new OkHttpClient.Builder()
		builder.connectTimeout(Long, TimeUnit);		//long 设置连接超时时长,TimeUnit 设置时间单位(时,分,秒)
		builder.readTimeout(Long, TimeUnit);				//long 设置读取超时时长,
		builder.writeTimeout(Long, TimeUnit);			//long 设置写入超时时长,
		builder.addInterceptor(Interceptor);		//设置拦截器
		builder.cache(Cache);							//设置缓存
		builder.build();		//返回 OkHttpClient
	
new MediaType();
	MediaType.parse(String);
	
new RequestBody();
	RequestBody.create(byte[]);
	RequestBody.create(File, MediaType);		//上传文件是用到
	RequestBody.create(byte[], MediaType);	//上传二进制字节时用到
	RequestBody.create(String, MediaType);	//上传文本时用到
	RequestBody.create(ByteString, MediaType);	//上传字节流时用到
	RequestBody.create(byte[], MediaType,int offset);			// offset   从offset开始上传
	RequestBody.create(byte[], MediaType,int offset,int byteCount);		// offset, byteCount  从offset开始,到offset后byteCount字节结束,上传

new FormBody();		//设置表单
	new FormBody.Builder();
		formBody.add(String, String);
		formBody.build();		//返回RequestBody  
	
new MultipartBody();		
	new MultipartBody.Builder();
		builder.setType(MediaType);  //MultipartBody.FORM 设置类型是表单
		builder.addFormDataPart(String, String);		//添加数据
		builder.addFormDataPart(String, String, RequestBody);
		builder.build();		//返回RequestBody 
	
new Request();
	new Request.Builder();
		builder.url(String);
		builder.post(RequestBody);
		builder.addHeader(String, String);  		//设置请求头
		builder.method(String, RequestBody)		//设置请求类型(GET,POST)和请求体
		builder.build();
		
new Call();
	call.execute();					//同步请求
	call.enqueue(Callback);	//异步请求
	call.cancel();					//请求终止

new Response();
	response.body();
	response.body().string();
	response.body().bytes();
	response.body().byteStream();
复制代码

看到这里,如果觉得不错的可以点个赞,鼓励鼓励我