轉(zhuǎn)載:http://www.itdecent.cn/p/c478d7a20d03
OkHttp可以說是如今最為流行的網(wǎng)絡(luò)請(qǐng)求框架之一,今天來探究下OkHttp 的使用方法,包括Get 請(qǐng)求、Post 請(qǐng)求、上傳下載文件、上傳下載圖片等功能
在使用OKHttp之前,首先要先了解如下幾個(gè)比較核心的類:
- OkHttpClient:客戶端對(duì)象
- Request:訪問請(qǐng)求,Post請(qǐng)求中需要包含RequestBody
- RequestBody:請(qǐng)求數(shù)據(jù),在Post請(qǐng)求中用到
- Response:即網(wǎng)絡(luò)請(qǐng)求的響應(yīng)結(jié)果
- MediaType:數(shù)據(jù)類型,用來表明數(shù)據(jù)是json,image,pdf等一系列格式
- client.newCall(request).execute():同步的請(qǐng)求方法
- client.newCall(request).enqueue(Callback callBack):異步的請(qǐng)求方法,但Callback是執(zhí)行在子線程中的,因此不能在此進(jìn)行UI更新操作
在使用前需要先在項(xiàng)目中添加OkHttp的依賴庫(kù),在對(duì)應(yīng)的Module的gradle中添加如下語句
compile 'com.squareup.okhttp3:okhttp:3.6.0'
此外,OkHttp內(nèi)部依賴另一個(gè)開源庫(kù)OkIo,所以也要將它導(dǎo)入進(jìn)來
compile 'com.squareup.okio:okio:1.11.0'
然后同步項(xiàng)目即可OkHttp的GitHub地址是:OkHttp
OkIo的GitHub地址是:OkIo
一、Get 請(qǐng)求
最常見的網(wǎng)絡(luò)請(qǐng)求方式可以說就是Get請(qǐng)求了,這里來獲取我在簡(jiǎn)書的個(gè)人主頁的網(wǎng)頁內(nèi)容
public void get(View view) {
OkHttpClient client = new OkHttpClient();
//構(gòu)造Request對(duì)象
//采用建造者模式,鏈?zhǔn)秸{(diào)用指明進(jìn)行Get請(qǐng)求,傳入Get的請(qǐng)求地址
Request request = new Request.Builder().get().url("http://www.itdecent.cn/u/9df45b87cfdf").build();
Call call = client.newCall(request);
//異步調(diào)用并設(shè)置回調(diào)函數(shù)
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
ToastUtil.showToast(GetActivity.this, "Get 失敗");
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
final String responseStr = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_result.setText(responseStr);
}
});
}
});
}
需要注意的是,異步調(diào)用的回調(diào)函數(shù)是在子線程當(dāng)中的,因?yàn)樾枰肏andler或者runOnUiThread來更新UI
這里使用到了一個(gè)Toast工具類,用戶判斷當(dāng)前線程是否是主線程,是的話則直接彈出Toast,否則利用runOnUiThread來彈出Toast
public class ToastUtil {
public static void showToast(final Activity activity, final String message) {
if ("main".equals(Thread.currentThread().getName())) {
Log.e("ToastUtil", "在主線程");
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
} else {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.e("ToastUtil", "不在主線程");
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
}
});
}
}
}
此外,在回調(diào)函數(shù)的以下方法中
public void onResponse(Call call, final Response response)
如果希望獲得返回的是字符串,則可以使用
response.body().string()
如果需要的是字節(jié)數(shù)組,則使用
response.body().bytes()
如果需要的是輸入流,則使用
response.body().byteStream()
二、Post 類型
在OkHttp中用Post方法向服務(wù)器發(fā)送一個(gè)請(qǐng)求體時(shí),請(qǐng)求體需要是一個(gè)RequestBody。這個(gè)請(qǐng)求體可以是:
- key-value:鍵值對(duì)類型
- String:字符串類型
- Form:類似于Html的表單數(shù)據(jù)提交
- Stream:流類型
- File:文件類型
三、Post 鍵值對(duì)
public void postParameter(View view) {
OkHttpClient client = new OkHttpClient();
//構(gòu)建FormBody,傳入要提交的參數(shù)
FormBody formBody = new FormBody
.Builder()
.add("username", "initObject")
.add("password", "initObject")
.build();
final Request request = new Request.Builder()
.url("http://www.itdecent.cn/")
.post(formBody)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
ToastUtil.showToast(PostParameterActivity.this, "Post Parameter 失敗");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseStr = response.body().string();
ToastUtil.showToast(PostParameterActivity.this, "Code:" + String.valueOf(response.code()));
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_result.setText(responseStr);
}
});
}
});
}
這里來向簡(jiǎn)書的網(wǎng)站主頁提交兩個(gè)參數(shù)值,可以通過Post來實(shí)現(xiàn)模擬登錄的功能。當(dāng)然這里只是模擬了Post操作而已,提交參數(shù)后會(huì)跳轉(zhuǎn)到404頁面
在
public void onResponse(Call call, Response response)
方法中通過
response.code()
可以得到整數(shù)類型的結(jié)果碼:404
四、Post 字符串
在上面的例子中Post傳遞的是參數(shù)對(duì),有時(shí)候我們會(huì)有要傳送字符串的需要,比如向服務(wù)器發(fā)送一個(gè)JSON字符串。那么就可以用如下方法:
public void postString(View view) {
OkHttpClient client = new OkHttpClient();
//RequestBody中的MediaType指定為純文本,編碼方式是utf-8
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;charset=utf-8"),
"{username:admin;password:admin}");
final Request request = new Request.Builder()
.url("http://www.itdecent.cn/")
.post(requestBody)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
ToastUtil.showToast(PostStringActivity.this, "Post String 失敗");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseStr = response.body().string();
ToastUtil.showToast(PostStringActivity.this, "Code:" + String.valueOf(response.code()));
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_result.setText(responseStr);
}
});
}
});
}
五、Post 表單
通過查看網(wǎng)站登錄頁的Html源代碼,通常都可以查看到如下格式的登錄表單
<form id="fm1" action="" method="post">
<input id="username" name="username" type="text"/>
<input id="password" name="password" type="password"/>
</form>
這里使用到 MuiltipartBody 來構(gòu)建一個(gè)RequestBody,這是 RequestBody 的一個(gè)子類,提交表單數(shù)據(jù)就是利用這個(gè)類來實(shí)現(xiàn)的
public void postForm(View view) {
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("username", "葉應(yīng)是葉")
.addFormDataPart("password", "葉應(yīng)是葉")
.build();
final Request request = new Request.Builder()
.url("http://www.itdecent.cn/")
.post(requestBody)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
ToastUtil.showToast(PostFormActivity.this, "Post Form 失敗");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseStr = response.body().string();
ToastUtil.showToast(PostFormActivity.this, "Code:" + String.valueOf(response.code()));
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_result.setText(responseStr);
}
});
}
});
}
六、Post 流
public void postStreaming(View view) {
final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new RequestBody() {
@Override
public MediaType contentType() {
return MEDIA_TYPE_MARKDOWN;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8("Numbers\n");
sink.writeUtf8("-------\n");
for (int i = 2; i <= 997; i++) {
sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
}
}
private String factor(int n) {
for (int i = 2; i < n; i++) {
int x = n / i;
if (x * i == n) return factor(x) + " × " + i;
}
return Integer.toString(n);
}
};
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(requestBody)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
ToastUtil.showToast(PostStreamingActivity.this, "Post Streaming 失敗");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseStr = response.body().string();
ToastUtil.showToast(PostStreamingActivity.this, "Code:" + String.valueOf(response.code()));
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_result.setText(responseStr);
}
});
}
});
}
七、Post 文件
public void postFile(View view) {
OkHttpClient client = new OkHttpClient();
final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");
File file = new File("README.md");
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
ToastUtil.showToast(PostFileActivity.this, "Post File 失敗");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseStr = response.body().string();
ToastUtil.showToast(PostFileActivity.this, "Code:" + String.valueOf(response.code()));
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_result.setText(responseStr);
}
});
}
});
}
八、下載圖片
public void downImage(View view) {
OkHttpClient client = new OkHttpClient();
final Request request = new Request
.Builder()
.get()
.url("http://avatar.csdn.net/B/0/1/1_new_one_object.jpg")
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
ToastUtil.showToast(DownImageActivity.this, "下載圖片失敗");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream inputStream = response.body().byteStream();
//將圖片顯示到ImageView中
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
runOnUiThread(new Runnable() {
@Override
public void run() {
iv_result.setImageBitmap(bitmap);
}
});
//將圖片保存到本地存儲(chǔ)卡中
File file = new File(Environment.getExternalStorageDirectory(), "image.png");
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] temp = new byte[128];
int length;
while ((length = inputStream.read(temp)) != -1) {
fileOutputStream.write(temp, 0, length);
}
fileOutputStream.flush();
fileOutputStream.close();
inputStream.close();
}
});
}
指定圖片地址并下載成功后,獲取圖片的輸入流,先用Bitmap decodeStream(InputStream is)方法將輸入流轉(zhuǎn)為Bitmap然后顯示出來,然后將圖片保存到本地存儲(chǔ)卡根目錄下
記得要申請(qǐng)存儲(chǔ)卡的寫權(quán)限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />