前言:Okhttp3是一个强大的网络请求下载开源框架,本文将会对OkHttp3进行全面解析以及在代码中穿插butterknife的使用。
我将对OkHttp3进行下面六种用途的讲解:
OkHttp3的简单封装
对于网络加载库,那么最常见的肯定就是http get请求了,比如获取一个网页的内容。
//我们想像为生成一个客户端
OkHttpClient client = new OkHttpClient();
String url = "http://192.168.1.189:5000/user/info?id=1";
Request request = new Request.Builder()
.url(url)
.build();
//请求加入调度
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("MainActivity","失败------"+e.getLocalizedMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String result = response.body().string();
Log.d("MainActivity","成功:"+result);
if(response.body() !=null){
response.body().close();
}
}
});
我们得注意几点:
@Override
public void onResponse(final Response response) throws IOException
{
final String res = response.body().string();
runOnUiThread(new Runnable()
{
@Override
public void run()
{
mTv.setText(res);
}
});
}
在平时的学习中,我以及碰到好几次开源框架,如Fresco,OkHttp的代码用如下形式:
Request request = new Request.Builder()
.url(url)
.build();
我们对Request的源码进行分析,看看内部如何设计:
public final class Request {
//静态内部类,所以可以用类访问,以及类直接调用构造器:new Request.Builder()来得到一个builder对象。
public static class Builder{
//方法返回一个Builder对象
public Builder url(String url) {
if (url == null) throw new IllegalArgumentException("url == null");
// Silently replace websocket URLs with HTTP URLs.
if (url.regionMatches(true, 0, "ws:", 0, 3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
url = "https:" + url.substring(4);
}
HttpUrl parsed = HttpUrl.parse(url);
if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
return url(parsed);
}
//最后面调用build()方法,返回Request实例
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}
}
通过对上面源码的分析,我们可以在以后对自己的工具类的封装中使用其思路,达到顺畅舒服的代码语法。
下面的代码中将会穿插butterknife知识的讲解,butterknife把我们从繁琐的findViewById()以及事件的监听中解放出来,这里是它的官网butterknife。
POST请求和GET请求不一样的是:POST请求将要上传几个key-value键值对。这几个key-value键值对处于请求报文最后面的body中。其实在GET请求中也有一些key-value键值对,但其处于URL后面,以?key1=value&key2&value2的结构出现。下面是POST方法的使用实例:
public class LoginActivity extends AppCompatActivity {
//直接把组建绑定给id
@BindView(R.id.etxt_username)
EditText mEtxtUsername;
@BindView(R.id.etxt_password)
EditText mEtxtPassword;
@BindView(R.id.btn_login)
Button mBtnLogin;
private OkHttpClient httpClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//将activity与组建绑定,而上一句把布局activity与布局绑定
ButterKnife.bind(this);
httpClient= new OkHttpClient();
}
//直接在id上设置点击事件
@OnClick(R.id.btn_login)
public void onClick() {
String username = mEtxtUsername.getText().toString().trim();
String password = mEtxtPassword.getText().toString().trim();
loginWithForm(username,password);
}
private void loginWithForm(String username, String password) {
String url = Config.API.BASE_URL+"login";
//比GET方法多了一个RequestBody类,该类将提交参数加入
RequestBody body = new FormBody.Builder()
.add("username",username)
.add("password",password)
.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("LoginActivity","请求服务器出差");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if(response.isSuccessful()){
String json = response.body().string();
try {
//将json字符串转换为JSONObject对象,从中取出value
JSONObject jsonObj = new JSONObject(json);
final String message = jsonObj.optString("message");
final int success = jsonObj.optInt("success");
//如果需要实现对UI的操作,则需要使用runOnUiThread方法
runOnUiThread(new Runnable() {
@Override
public void run() {
if(success==1)
Toast.makeText(LoginActivity.this,"登录成功",Toast.LENGTH_LONG).show();
else
Toast.makeText(LoginActivity.this,message,Toast.LENGTH_LONG).show();
}
});
} catch (JSONException e) {
e.printStackTrace();
}
}
}
});
}
}
JSONObject jsonObj = new JSONObject();
try {
jsonObj.put("username",username);
jsonObj.put("password",password);
} catch (JSONException e) {
e.printStackTrace();
}
String jsonParams =jsonObj.toString();
Log.d("LoginActivity","json params = "+jsonParams);
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),jsonParams);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
public class FileDownloadActivity extends AppCompatActivity {
public String url ="http://112.124.22.238:8081/course_api/css/net_music.apk";
//定义下载后的文件名
public String fileName = "net_music.apk";
@BindView(R.id.btn_download)
Button mBtnDownload;
@BindView(R.id.progressBar)
ProgressBar mProgressBar;
private OkHttpClient httpClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_download);
ButterKnife.bind(this);
requestPermission();
initOKhttp();
}
private void initOKhttp() {
httpClient = new OkHttpClient();
}
//当点击download时调用downloadAPK()方法
@OnClick(R.id.btn_download)
public void onClick() {
downloadAPK();
}
private void downloadAPK() {
Request request = new Request.Builder()
.url(url)
.build();
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("LoginActivity","请求文件出差");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//与前面俩种案例比较,区别在于对输入流的处理
writeFile(response);
}
});
}
//在Handler类中调用handleMessage方法来对UI进行处理
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what==1){
int progress = msg.arg1;
mProgressBar.setProgress(progress);
}
}
};
private void writeFile(Response response) {
//输入流和输出流之间共接一个内存
InputStream is =null;
FileOutputStream fos = null;
//从response中得到输入流,inputStream
is = response.body().byteStream();
//对文件的处理,得到文件路径,创建文件
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
Log.d("FileDownloadActivity","path:"+path);
//创建文件
File file = new File(path,fileName);
try {
fos = new FileOutputStream(file);
byte[] bytes = new byte[1024];
int len =0;
//提前的到文件的大小
long totalSize = response.body().contentLength();
long sum =0;
while ((len =is.read(bytes)) !=-1){
fos.write(bytes);
sum +=len;
int progress = (int) ((sum * 1.0f / totalSize) * 100);
//把message标示为1,上文中msg.what ==1来区分信息
Message msg = mHandler.obtainMessage(1);
msg.arg1 = progress;
//对UI的处理只能在UI线程中执行,所以把msg传给Handler处理
mHandler.sendMessage(msg);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
//文件流打开时,必须关闭! inputstream,outputStream
try {
if(is !=null){
is.close();
}
if(fos !=null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
response.body().byteStream
转换为输入流,接下来就是我们常写的读取输入流,写入文件的操作。这里注意Handler的使用,在Handler类中调用handleMessage方法,在方法内部对UI进行操作。在文件操作处,mHandler.sendMessage(msg);
将msg传到UI线程处。