今天學習了一下
OkHttp
,在這裏作個總結,但願能夠幫助到有須要的人,好了,廢話很少說,進入正題。
OkHttp是一個優秀的網絡請求框架,可能一說到網絡請求框架,可能不少人都會想到volley
,volley
是一個Google提供的網絡請求框架,個人博客裏也有一篇專門介紹volley
的博客,博客地址在此 Android網絡請求 ------ Volley的使用 那麼既然Google提供了網絡請求的框架,咱們爲何還要使用OkHttp
呢,原來是volley
是要依靠HttpCient
的,而Google在Android6.0
的SDK中去掉了HttpCient
,因此OkHttp
就開始愈來愈受你們的歡迎.今天咱們主要介紹
OkHttp
的Get
請求、Post
請求、上傳下載文件
、上傳下載圖片等功能
。html
固然在開始以前,咱們還要先在項目中添加OkHttp的依賴庫,至於怎麼在AndroidStudio中給項目添加OkHTTP依賴,這裏將再也不贅述。另外,OkHttp中使用了建造者模式,若是對建造者模式不瞭解,能夠看看這篇博客設計模式之建造者模式**java
添加OkHttp的依賴 在對應的Module的gradle中添加 compile 'com.squareup.okhttp3:okhttp:3.5.0' 而後同步一下項目便可
使用OkHttp進行
Get
請求只須要四步便可完成。
1 . 拿到OkHttpClient對象android
OkHttpClient client = new OkHttpClient();
2 . 構造Request對象json
Request request = new Request.Builder() .get() .url("https:www.baidu.com") .build();
這裏咱們採用建造者模式和鏈式調用指明是進行Get請求,並傳入Get請求的地址
若是咱們須要在get請求時傳遞參數,咱們能夠如下面的方式將參數拼接在url以後設計模式
https:www.baidu.com?username=admin&password=admin
3 . 將Request封裝爲Call服務器
Call call = client.newCall(request);
4 . 根據須要調用同步或者異步請求方法網絡
//同步調用,返回Response,會拋出IO異常 Response response = call.execute(); //異步調用,並設置回調函數 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Toast.makeText(OkHttpActivity.this, "get failed", Toast.LENGTH_SHORT).show(); } @Override public void onResponse(Call call, final Response response) throws IOException { final String res = response.body().string(); runOnUiThread(new Runnable() { @Override public void run() { contentTv.setText(res); } }); } });
第四步有一些須要注意的地方
runOnUiThread()
方法或者Handler
來處理是否是覺得上面就結束了,對的,OkHttp的Get請求步驟就這麼4步,可是當你試圖打開應用加載數據,但是發現並無加載到數據,這是一個簡單可是咱們常犯的錯誤.在AndroidManifest.xml中加入聯網權限app
<uses-permission android:name="android.permission.INTERNET" />
使用OkHttp進行Post
請求和進行Get
請求很相似,只須要五步便可完成。
1 . 拿到OkHttpClient對象框架
OkHttpClient client = new OkHttpClient();
2 . 構建FormBody,傳入參數異步
FormBody formBody = new FormBody.Builder() .add("username", "admin") .add("password", "admin") .build();
3 . 構建Request,將FormBody做爲Post方法的參數傳入
final Request request = new Request.Builder() .url("http://www.jianshu.com/") .post(formBody) .build();
4 . 將Request封裝爲Call
Call call = client.newCall(request);
5 . 調用請求,重寫回調方法
call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Toast.makeText(OkHttpActivity.this, "Post Failed", Toast.LENGTH_SHORT).show(); } @Override public void onResponse(Call call, Response response) throws IOException { final String res = response.body().string(); runOnUiThread(new Runnable() { @Override public void run() { contentTv.setText(res); } }); } });
通過上面的步驟一個post請求就完成了,固然上面的
url
參數和須要傳入的參數你們就要根據實際狀況來傳入,你會發現get和post請求的步驟很是像。
若是你已經掌握了上面的兩種基本的步驟,那下面的內容就比較簡單了
上面咱們的post的參數是經過構造一個FormBody
經過鍵值對的方式來添加進去的,其實post方法須要傳入的是一個RequestBody
對象,FormBody
是RequestBody
的子類,但有時候咱們經常會遇到要傳入一個字符串的需求,好比客戶端給服務器發送一個json字符串,那這種時候就須要用到另外一種方式來構造一個RequestBody
以下所示:
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), "{username:admin;password:admin}");
上面的MediaType咱們指定傳輸的是純文本,並且編碼方式是utf-8,經過上面的方式咱們就能夠向服務端發送json字符串啦。
注:關於MidiaType的類型你能夠百度搜索mime type查看相關的內容,這裏再也不贅述
理解了上面一個,下面這個就更簡單了,這裏咱們以上傳一張圖片爲例,固然你也能夠上傳一個txt什麼的文件,都是能夠的
其實最主要的仍是構架咱們本身的
RequestBody
,以下圖構建
File file = new File(Environment.getExternalStorageDirectory(), "1.png"); if (!file.exists()){ Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show(); } else { RequestBody requestBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file); }
這裏咱們將手機SD卡根目錄下的1.png
圖片進行上傳。代碼中的application/octet-stream
表示咱們的文件是任意二進制數據流
,固然你也能夠換成更具體的image/png
注:最後記得最重要的一點:添加存儲卡寫權限,在AndroidManifest.xml文件中添加以下代碼:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
咱們在網頁上常常會遇到用戶註冊的狀況,須要你輸入用戶名,密碼,還有上傳頭像,這其實就是一個表單,那麼接下來咱們看看如何利用OkHttp來進行表單提交。通過上面的學習,你們確定也懂,主要的區別就在於構造不一樣的RequestBody
傳遞給post
方法便可.
因爲咱們使用的是OkHttp3因此咱們還須要再導入一個包okio.jar才能繼續下面的內容,咱們須要在模塊的Gradle文件中添加以下代碼,而後同步一下項目便可
compile 'com.squareup.okio:okio:1.11.0'
這裏咱們會用到一個MuiltipartBody
,這是RequestBody
的一個子類,咱們提交表單就是利用這個類來構建一個RequestBody
,下面的代碼咱們會發送一個包含用戶民、密碼、頭像的表單到服務端
File file = new File(Environment.getExternalStorageDirectory(), "1.png"); if (!file.exists()){ Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show(); return; } RequestBody muiltipartBody = new MultipartBody.Builder() //必定要設置這句 .setType(MultipartBody.FORM) .addFormDataPart("username", "admin")// .addFormDataPart("password", "admin")// .addFormDataPart("myfile", "1.png", RequestBody.create(MediaType.parse("application/octet-stream"), file)) .build();
上面添加用戶民和密碼的部分和咱們上面學習的提交鍵值對的方法很像,咱們關鍵要注意如下幾點:
(1)若是提交的是表單,必定要設置setType(MultipartBody.FORM)
這一句
(2)提交的文件addFormDataPart()
的第一個參數,就上面代碼中的myfile
就是相似於鍵值對的鍵,是供服務端使用的,就相似於網頁表單裏面的name
屬性,例以下面:
<input type="file" name="myfile">
(3)提交的文件addFormDataPart()
的第二個參數文件的本地的名字,第三個參數是RequestBody
,裏面包含了咱們要上傳的文件的路徑以及MidiaType
(4)記得在AndroidManifest.xml
文件中添加存儲卡讀寫權限
除了上面的功能,咱們最經常使用的功能該有從網路上下載文件,咱們下面的例子將演示下載一個文件存放在存儲卡根目錄,從網絡下載一張圖片並顯示到ImageView中
1 . 從網絡下載一個文件(此處咱們如下載一張圖片爲例)
public void downloadImg(View view){ OkHttpClient client = new OkHttpClient(); final Request request = new Request.Builder() .get() .url("https://www.baidu.com/img/bd_logo1.png") .build(); Call call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e("moer", "onFailure: ");; } @Override public void onResponse(Call call, Response response) throws IOException { //拿到字節流 InputStream is = response.body().byteStream(); int len = 0; File file = new File(Environment.getExternalStorageDirectory(), "n.png"); FileOutputStream fos = new FileOutputStream(file); byte[] buf = new byte[128]; while ((len = is.read(buf)) != -1){ fos.write(buf, 0, len); } fos.flush(); //關閉流 fos.close(); is.close(); } }); }
你會發現步驟與進行通常的
Get
請求差異不大,惟一的區別在於咱們在回調函數中所作的事,咱們拿到了圖片的字節流,而後保存爲了本地的一張圖片
2 . 從網絡下載一張圖片並設置到ImageView中
其實學會了上面的步驟你徹底能夠將圖片下載到本地後再設置到ImageView中,固然下面是另外一種方法 這裏咱們使用BitmapFactory
的decodeStream
將圖片的輸入流直接轉換爲Bitmap
,而後設置到ImageView
中,下面只給出onResponse()
中的代碼.
@Override public void onResponse(Call call, Response response) throws IOException { InputStream is = response.body().byteStream(); final Bitmap bitmap = BitmapFactory.decodeStream(is); runOnUiThread(new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } }); is.close(); }
咱們一直都說,用戶體驗很重要,當咱們下載的文件比較大,而網速又比較慢的時候,若是咱們只是在後臺下載或上傳,沒有給用戶顯示一個進度,那將是很是差的用戶體驗,下面咱們就將簡單作一下進度的顯示,其實很是簡單的
1 . 顯示文件下載進度
這裏只是演示,我只是把進度顯示在一個TextView中,至於進度的獲取固然是在咱們的回調函數
onResponse()
中去獲取
(1)使用response.body().contentLength()
拿到文件總大小
(2)在while
循環中每次遞增咱們讀取的buf的長度
@Override public void onResponse(Call call, Response response) throws IOException { InputStream is = response.body().byteStream(); long sum = 0L; //文件總大小 final long total = response.body().contentLength(); int len = 0; File file = new File(Environment.getExternalStorageDirectory(), "n.png"); FileOutputStream fos = new FileOutputStream(file); byte[] buf = new byte[128]; while ((len = is.read(buf)) != -1){ fos.write(buf, 0, len); //每次遞增 sum += len; final long finalSum = sum; Log.d("pyh1", "onResponse: " + finalSum + "/" + total); runOnUiThread(new Runnable() { @Override public void run() { //將進度設置到TextView中 contentTv.setText(finalSum + "/" + total); } }); } fos.flush(); fos.close(); is.close(); }
2 . 顯示文件上傳進度
對於上傳的進度的處理會比較麻煩,由於具體的上傳過程是在RequestBody
中由OkHttp
幫咱們處理上傳,並且OkHttp
並無給咱們提供上傳進度的接口,這裏咱們的作法是自定義類繼承RequestBody
,而後重寫其中的方法,將其中的上傳進度經過接口回調暴露出來供咱們使用。
public class CountingRequestBody extends RequestBody { //實際起做用的RequestBody private RequestBody delegate; //回調監聽 private Listener listener; private CountingSink countingSink; /** * 構造函數初始化成員變量 * @param delegate * @param listener */ public CountingRequestBody(RequestBody delegate, Listener listener){ this.delegate = delegate; this.listener = listener; } @Override public MediaType contentType() { return delegate.contentType(); } @Override public void writeTo(BufferedSink sink) throws IOException { countingSink = new CountingSink(sink); //將CountingSink轉化爲BufferedSink供writeTo()使用 BufferedSink bufferedSink = Okio.buffer(countingSink); delegate.writeTo(bufferedSink); bufferedSink.flush(); } protected final class CountingSink extends ForwardingSink{ private long byteWritten; public CountingSink(Sink delegate) { super(delegate); } /** * 上傳時調用該方法,在其中調用回調函數將上傳進度暴露出去,該方法提供了緩衝區的本身大小 * @param source * @param byteCount * @throws IOException */ @Override public void write(Buffer source, long byteCount) throws IOException { super.write(source, byteCount); byteWritten += byteCount; listener.onRequestProgress(byteWritten, contentLength()); } } /** * 返回文件總的字節大小 * 若是文件大小獲取失敗則返回-1 * @return */ @Override public long contentLength(){ try { return delegate.contentLength(); } catch (IOException e) { return -1; } } /** * 回調監聽接口 */ public static interface Listener{ /** * 暴露出上傳進度 * @param byteWritted 已經上傳的字節大小 * @param contentLength 文件的總字節大小 */ void onRequestProgress(long byteWritted, long contentLength); } }
上面的代碼註釋很是詳細,這裏再也不解釋,而後咱們在寫具體的請求時還須要作以下變化
File file = new File(Environment.getExternalStorageDirectory(), "1.png"); if (!file.exists()){ Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show(); }else{ RequestBody requestBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file); } //使用咱們本身封裝的類 CountingRequestBody countingRequestBody = new CountingRequestBody(requestBody2, new CountingRequestBody.Listener() { @Override public void onRequestProgress(long byteWritted, long contentLength) { //打印進度 Log.d("pyh", "進度 :" + byteWritted + "/" + contentLength); } });
上面其實就是在原有的RequestBody
上包裝了一層,最後在咱們的使用中在post()
方法中傳入咱們的CountingRequestBody
對象便可。
以上就是一些
OkHttp
經常使用的總結,但願能夠幫助到須要的人