Android开发之OkHttp的使用

本篇记录的是Android开发中OkHttp框架的使用。OkHttp是Java中用于网络请求的一个库,主页为:http://square.github.io/okhttp/, github地址为:https://github.com/square/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就可以了。

参考:http://blog.csdn.net/lmj623565791/article/details/47911083

Demo下载

时间: 2024-11-18 13:49:26

Android开发之OkHttp的使用的相关文章

Android开发之Tween(补间动画)完全解析(下)

欢迎转载,转载请注明出处:http://blog.csdn.net/dmk877/article/details/51980734 在上一篇文章中,我们详细讨论了Tween动画的xml的实现以及interpolator的使用,相信通过上篇文章大家对Tween动画的xml属性的配置会有一个详细的理解,当然这篇文章也是承接上篇文章,所以强烈建议先阅读上篇文章:Android开发之Tween(补间动画)完全解析(上),这篇文章将从代码的角度实现上篇文章的效果.如有疑问请留言,如有谬误欢迎批评指正. T

Android开发之JSON使用

Android开发之JSON使用 今天在论坛看到有不少朋友问关于json的问题,所以想写一篇关于android中使用json的博客. 首先 json是什么 JSON的全称是JavaScript Object Notation,从这里可以看到它源于JavaScript,它采用文本形式体现.比如 {"name":"zhangsan","age":20,"wife":"如花"} 这个简单表示了一个对象,在java

Android开发之bindService()侦听service内部状态

在Android开发之bindService()通信的基础上,实现bindService()方法侦听service内部状态. 实现侦听service内部状态,使用的是回调机制 1.首先实现一个接口 1 public static interface CallBack{ 2 void onDataChange(String data); 3 } 2. 1 private CallBack callBack=null; 2 public void setCallBack(CallBack callB

Android开发之Fragment详解

Android开发之Fragment学习 1.简介: Fragment是Android 3.0引入的新API. Fragment代表了 Activity的子模块,因此可以把Fragment理解成Activity片段.Fragment用于自己的生命周期,也可以接受它自己的输入事件. Fragment必须被"嵌入" Activity中使用,因此虽然Fragment也拥有自己的生命周期,但Fragment的生命周期会受它所在的Activity的生命周期的控制.例如,当Activity暂停时,

Android开发之SpannableString详解

在实际的应用开发过程中经常会遇到,在文本的不同部分显示一些不同的字体风格的信息如:文本的字体.大小.颜色.样式.以及超级链接等.一般情况下,TextView中的文本都是一个样式,对于类似的情况,可以借助SpannableString或SpannableStringBuilder对象来实现以上设置. SpannableString与SpannableStringBuilder都可以将某段文本设置成一个Span,在Android中,Span表示一段文本的效果,例如,链接形式.图像.带背景的文本等.只

Android开发之Html类详解

在进行Android开发中经常回忽略Html类.这个类其实很简单,就是将HTML标签文本解析成普通的样式文本.下面就让我么看一下这个类的具体介绍. 类结构: java.lang.Object    ? android.text.Html 类概述: 这个类用于处理的HTML字符串并将其转换成可显示的样式文本.但并不是所有的HTML标记的支持. 公有方法: 说其简单是应为它就有四个方法: Public Methods static String escapeHtml(CharSequence tex

Android开发之View重写相关API-onLayout,onMeasure,MeasureSpec

 1.onLayout android.view.ViewGroup protected void onLayout(boolean changed, int l, int t, int r, int b) 执行layout操作时调用onLayout方法.View要给它的每个Child设定size和position.拥有Children的子类需要重写onLayout方法并且调用每个Child的layout方法. 参数changed表示view的size或position发生变化.参数l, t,

android开发之MediaPlayer+Service MP3播放器

http://blog.csdn.net/zzy916853616/article/details/6450753 [java] view plaincopy import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.List; import android.app.ListActivity; import android.apps.service.Player

Android开发之Handler和Looper的关系

关于Handler的总结. Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理. Handler:处理者,负责Message的发送及处理.使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等. MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行.当然,存放Message并非实际意义的保存,而