Android开发之OkHttp的使用

来源:互联网 发布:godaddy域名续费价格 编辑:程序博客网 时间:2024/06/10 18:02

本篇记录的是Android开发中OkHttp框架的使用,下面介绍OkHttp库的用法,本篇会给出OkHttp的使用demo,demo中包含了常用的get请求、post请求、文件的上传和下载,demo运行的效果如下图所示:

\\\\\

下面上代码一一说明:

要使用OkHttp,必须在项目中先导入OkHttp,在app模块的build.gradle文件中,加入下面的代码:

dependencies {    compile fileTree(dir: 'libs', include: ['*.jar'])    testCompile 'junit:junit:4.12'    compile 'com.android.support:appcompat-v7:23.1.1'    compile 'com.squareup.okhttp3:okhttp:3.2.0'}
这样就将OkHttp导入到项目中了。

 

(1)GET请求

最简单的GET请求用法如下:

 

//简单的Get请求,不带参数public void simpleGetClick(View view) {    okHttpClient = new OkHttpClient();    Request request = new Request.Builder()            .url("http://192.168.1.170:8088/okhttp/test_simple_get.php")            .build();    okHttpClient.newCall(request).enqueue(callback);}
如果请求中要添加Header头和参数,可以用下面的方式:

 

 

//带参数的Get请求public void addParamGetClick(View view) {    okHttpClient = new OkHttpClient();    Request request = new Request.Builder()            .addHeader("token", "asdlfjkasdljfaskdjfalsjkljalk")  //请求头中加入参数            .url("http://192.168.1.170:8088/okhttp/test_param_get.php?username=zhangsan&phone=13888888888") //携带参数            .build();    okHttpClient.newCall(request).enqueue(callback);}

需要注意的是,上面的代码中,callback是请求后的回调接口,代码如下:

//请求后的回调接口private Callback callback = new Callback() {    @Override    public void onFailure(Call call, IOException e) {        setResult(e.getMessage(), false);    }    @Override    public void onResponse(Call call, Response response) throws IOException {        setResult(response.body().string(), true);    }};
这个回调接口需要注意的是,onResponse和onFailure都不是在UI线程中执行的,所以如果我们要在onResponse或onFailure中进行UI相关的操作,需要在UI线程中进行。

(2)POST请求

比较简单的POST请求,用法如下:

 

//简单的带参数和Header的post请求public void simplePostClick(View view) {    okHttpClient = new OkHttpClient();    RequestBody requestBody = new FormBody.Builder()            .add("username", "wangwu")            .add("password", "hello12345")            .add("gender", "female")            .build();    Request request = new Request.Builder()            .url("http://192.168.1.170:8088/okhttp/test_simple_post.php")            .post(requestBody)            .addHeader("token", "helloworldhelloworldhelloworld")            .build();    okHttpClient.newCall(request).enqueue(callback);}
这里我们需要先构造一个RequestBody,然后把需要携带的参数放到RequestBody中,然后使用这个RequestBody构建一个Request请求,最后将这个请求放入队列中执行

 

如果我们的POST请求稍微复杂点,比如携带的参数既有文本类型的,又有文件类型的,那么可以用下面的方式来请求:

 

//带文本参数和文件参数的post请求public void filePostClick(View view) {    RequestBody fileBody = RequestBody.create(MediaType.parse("text/plain; charset=utf-8"), tempFile);    RequestBody requestBody = new MultipartBody.Builder()            .setType(MultipartBody.FORM)            .addFormDataPart("username", "wangwu")            .addFormDataPart("password", "hello12345")            .addFormDataPart("gender", "female")            .addFormDataPart("file", "info.txt", fileBody)            .build();    Request request = new Request.Builder()            .url("http://192.168.1.170:8088/okhttp/test_param_post.php")            .post(requestBody)            .addHeader("token", "helloworldhelloworldhelloworld")            .build();    okHttpClient.newCall(request).enqueue(callback);}
上面的代码中,tempFile是一个文本文件,为了POST提交文件和一些其他的参数,我们使用MultipartBody来构建一个请求体,需要注意的是,因为POST的内容含有文件,所以我们必须为这个请求体设置setType(MultipartBody.FORM)

(3)文件的上传

文件上传并显示进度,这个代码稍微有些复杂,下面直接上代码:

package com.test.testokhttp;import android.os.Environment;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;import java.io.File;import java.io.IOException;import java.util.concurrent.TimeUnit;import okhttp3.Call;import okhttp3.Callback;import okhttp3.Interceptor;import okhttp3.MediaType;import okhttp3.MultipartBody;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.RequestBody;import okhttp3.Response;import okhttp3.ResponseBody;import okio.Buffer;import okio.BufferedSink;import okio.BufferedSource;import okio.ForwardingSink;import okio.ForwardingSource;import okio.Okio;import okio.Sink;import okio.Source;public class UploadActivity extends AppCompatActivity {    private OkHttpClient okHttpClient;    private TextView resultTextView;    private ProgressBar progressBar;    private File tempFile;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_upload);        setTitle("上传文件并显示进度");        resultTextView = (TextView) findViewById(R.id.result_textview);        progressBar = (ProgressBar) findViewById(R.id.progress_bar);        progressBar.setMax(100);        okHttpClient = new OkHttpClient.Builder()                .readTimeout(30, TimeUnit.SECONDS)                .connectTimeout(10, TimeUnit.SECONDS)                .writeTimeout(60, TimeUnit.SECONDS)                .build();    }    //点击按钮开始上传文件    public void startUploadClick(View view) {        tempFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test.pdf");        RequestBody requestBody = new MultipartBody.Builder()                .setType(MultipartBody.FORM)                .addFormDataPart("file", "test.pdf", RequestBody.create(MediaType.parse("application/pdf; charset=utf-8"), tempFile))                .build();        ProgressRequestBody progressRequestBody = new ProgressRequestBody(requestBody, progressListener);        Request request = new Request.Builder()                .url("http://192.168.1.170:8088/okhttp/test_upload_file.php")                .post(progressRequestBody)                .build();        okHttpClient.newCall(request).enqueue(callback);    }    //通过实现进度回调接口中的方法,来显示进度    private ProgressListener progressListener = new ProgressListener() {        @Override        public void update(long bytesRead, long contentLength, boolean done) {            int progress = (int) (100.0 * bytesRead / contentLength);            progressBar.setProgress(progress);        }    };    //请求后的回调方法    private Callback callback = new Callback() {        @Override        public void onFailure(Call call, IOException e) {            setResult(e.getMessage(), false);        }        @Override        public void onResponse(Call call, Response response) throws IOException {            setResult(response.body().string(), true);        }    };    //显示请求返回的结果    private void setResult(final String msg, final boolean success) {        runOnUiThread(new Runnable() {            @Override            public void run() {                if (success) {                    Toast.makeText(UploadActivity.this, "请求成功", Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(UploadActivity.this, "请求失败", Toast.LENGTH_SHORT).show();                }                resultTextView.setText(msg);            }        });    }    //自定义的RequestBody,能够显示进度    public class ProgressRequestBody extends RequestBody {        //实际的待包装请求体        private final RequestBody requestBody;        //进度回调接口        private final ProgressListener progressListener;        //包装完成的BufferedSink        private BufferedSink bufferedSink;        /**         * 构造函数,赋值         *         * @param requestBody      待包装的请求体         * @param progressListener 回调接口         */        public ProgressRequestBody(RequestBody requestBody, ProgressListener progressListener) {            this.requestBody = requestBody;            this.progressListener = progressListener;        }        /**         * 重写调用实际的响应体的contentType         *         * @return MediaType         */        @Override        public MediaType contentType() {            return requestBody.contentType();        }        /**         * 重写调用实际的响应体的contentLength         *         * @return contentLength         * @throws IOException 异常         */        @Override        public long contentLength() throws IOException {            return requestBody.contentLength();        }        /**         * 重写进行写入         *         * @param sink BufferedSink         * @throws IOException 异常         */        @Override        public void writeTo(BufferedSink sink) throws IOException {            if (bufferedSink == null) {                //包装                bufferedSink = Okio.buffer(sink(sink));            }            //写入            requestBody.writeTo(bufferedSink);            //必须调用flush,否则最后一部分数据可能不会被写入            bufferedSink.flush();        }        /**         * 写入,回调进度接口         *         * @param sink Sink         * @return Sink         */        private Sink sink(Sink sink) {            return new ForwardingSink(sink) {                //当前写入字节数                long bytesWritten = 0L;                //总字节长度,避免多次调用contentLength()方法                long contentLength = 0L;                @Override                public void write(Buffer source, long byteCount) throws IOException {                    super.write(source, byteCount);                    if (contentLength == 0) {                        //获得contentLength的值,后续不再调用                        contentLength = contentLength();                    }                    //增加当前写入的字节数                    bytesWritten += byteCount;                    //回调                    progressListener.update(bytesWritten, contentLength, bytesWritten == contentLength);                }            };        }    }    //进度回调接口    interface ProgressListener {        void update(long bytesRead, long contentLength, boolean done);    }}
上面需要注意的是,上传文件需要实现自定义的RequestBody,也就是上面的ProgressRequestBody,在ProgressRequestBody中获取上传的进度。

(4)文件的下载

下载和上传类似,区别在于,需要我们实习自定义的ResponseBody而不是RequestBody了,下面上代码:

import android.os.Environment;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.util.concurrent.TimeUnit;import okhttp3.Call;import okhttp3.Callback;import okhttp3.Interceptor;import okhttp3.MediaType;import okhttp3.OkHttpClient;import okhttp3.Request;import okhttp3.Response;import okhttp3.ResponseBody;import okio.Buffer;import okio.BufferedSource;import okio.ForwardingSource;import okio.Okio;import okio.Source;public class DownloadActivity extends AppCompatActivity {    private OkHttpClient okHttpClient;    private TextView resultTextView;    private ProgressBar progressBar;    private File tempFile;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_download);        setTitle("下载文件并显示进度");        okHttpClient = new OkHttpClient.Builder()                .addNetworkInterceptor(new Interceptor() {                    @Override public Response intercept(Interceptor.Chain chain) throws IOException {                        Response originalResponse = chain.proceed(chain.request());                        return originalResponse.newBuilder()                                .body(new ProgressResponseBody(originalResponse.body(), progressListener))                                .build();                    }                })                .connectTimeout(5, TimeUnit.SECONDS)                .readTimeout(300, TimeUnit.SECONDS)                .writeTimeout(30, TimeUnit.SECONDS)                .build();        resultTextView = (TextView) findViewById(R.id.result_textview);        progressBar = (ProgressBar) findViewById(R.id.progress_bar);        tempFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + System.currentTimeMillis() + ".pdf");    }    //下载文件    public void startDownloadClick(View view) {        Request request = new Request.Builder()                .url("http://192.168.1.170:8088/okhttp/test.pdf")                .build();        okHttpClient.newCall(request).enqueue(callback);    }    private ProgressListener progressListener = new ProgressListener() {        @Override        public void update(long bytesRead, long contentLength, boolean done) {            int progress = (int) (100.0 * bytesRead / contentLength);            progressBar.setProgress(progress);        }    };    //请求后的回调方法    private Callback callback = new Callback() {        @Override        public void onFailure(Call call, IOException e) {            setResult(e.getMessage(), false);        }        @Override        public void onResponse(Call call, Response response) throws IOException {            if(response != null) {                //下载完成,保存数据到文件                InputStream is = response.body().byteStream();                FileOutputStream fos = new FileOutputStream(tempFile);                byte[] buf = new byte[1024];                int hasRead = 0;                while((hasRead = is.read(buf)) > 0) {                    fos.write(buf, 0, hasRead);                }                fos.close();                is.close();                setResult("下载成功", true);            }        }    };    //显示请求返回的结果    private void setResult(final String msg, final boolean success) {        runOnUiThread(new Runnable() {            @Override            public void run() {                if (success) {                    Toast.makeText(DownloadActivity.this, "请求成功", Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(DownloadActivity.this, "请求失败", Toast.LENGTH_SHORT).show();                }                resultTextView.setText(msg);            }        });    }        //自定义的ResponseBody,在其中处理进度    private static class ProgressResponseBody extends ResponseBody {        private final ResponseBody responseBody;        private final ProgressListener progressListener;        private BufferedSource bufferedSource;        public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {            this.responseBody = responseBody;            this.progressListener = progressListener;        }        @Override public MediaType contentType() {            return responseBody.contentType();        }        @Override public long contentLength() {            return responseBody.contentLength();        }        @Override public BufferedSource source() {            if (bufferedSource == null) {                bufferedSource = Okio.buffer(source(responseBody.source()));            }            return bufferedSource;        }        private Source source(Source source) {            return new ForwardingSource(source) {                long totalBytesRead = 0L;                @Override public long read(Buffer sink, long byteCount) throws IOException {                    long bytesRead = super.read(sink, byteCount);                    // read() returns the number of bytes read, or -1 if this source is exhausted.                    totalBytesRead += bytesRead != -1 ? bytesRead : 0;                    progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);                    return bytesRead;                }            };        }    }    //进度回调接口    interface ProgressListener {        void update(long bytesRead, long contentLength, boolean done);    }}
如果我们在项目中直接使用上面的代码来进行http请求的话,势必会比较麻烦,所以这里我们需要封装上面的代码,尽量在项目中能用简短的代码完成网络请求。另外,一个项目中肯定会有很多个网络请求,我们没必要在每次网络请求中都创建一个OkHttpClient对象,所有的请求公用一个OkHttpClient就可以了。
0 0