- 一般的get請求
- 一般的post請求
- 基於Http的文件上傳
- 文件下載
- 加載圖片
- 支持請求回調,直接返回對象、對象集合
- 支持session的保持
- 使用前,對於Android Studio的用戶,可以選擇添加:
compile 'com.squareup.okhttp:okhttp:2.4.0'
或者Eclipse的用戶,可以下載最新的jar okhttp he latest JAR ,添加依賴就可以用了。
注意:okhttp內部依賴okio,別忘了同時導入okio:
gradle: compile 'com.squareup.okio:okio:1.5.0'
二、使用教程
(一)Http Get
對了網絡加載庫,那麼最常見的肯定就是http get請求了,比如獲取一個網頁的內容。
//創建okHttpClient對象
OkHttpClient mOkHttpClient = new OkHttpClient();
//創建一個Request
final Request request = new Request.Builder()
.url("https://github.com/hongyangAndroid")
.build();
//new call
Call call = mOkHttpClient.newCall(request);
//請求加入調度
call.enqueue(new Callback()
{
@Override
public void onFailure(Request request, IOException e)
{
}
@Override
public void onResponse(final Response response) throws IOException
{
//String htmlStr = response.body().string();
}
});
-
以上就是發送一個get請求的步驟,首先構造一個Request對象,參數最起碼有個url,當然你可以通過Request.Builder設置更多的參數比如:
header
、method
等。 -
然後通過request的對象去構造得到一個Call對象,類似於將你的請求封裝成了任務,既然是任務,就會有
execute()
和cancel()
等方法。 -
最後,我們希望以異步的方式去執行請求,所以我們調用的是call.enqueue,將call加入調度隊列,然後等待任務執行完成,我們在Callback中即可得到結果。
看到這,你會發現,整體的寫法還是比較長的,所以封裝肯定是要做的,不然每個請求這麼寫,得累死。
ok,需要注意幾點:
-
onResponse回調的參數是response,一般情況下,比如我們希望獲得返回的字符串,可以通過
response.body().string()
獲取;如果希望獲得返回的二進制字節數組,則調用response.body().bytes()
;如果你想拿到返回的inputStream,則調用response.body().byteStream()
看到這,你可能會奇怪,竟然還能拿到返回的inputStream,看到這個最起碼能意識到一點,這裏支持大文件下載,有inputStream我們就可以通過IO的方式寫文件。不過也說明一個問題,這個
onResponse
執行的線程並不是UI線程。的確是的,如果你希望操作控件,還是需要使用handler等,例如:
@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);
}
});
}
- 我們這裏是異步的方式去執行,當然也支持阻塞的方式,上面我們也說了Call有一個
execute()
方法,你也可以直接調用call.execute()
通過返回一個Response
。
(二) Http Post 攜帶參數
看來上面的簡單的get請求,基本上整個的用法也就掌握了,比如post攜帶參數,也僅僅是Request的構造的不同。
Request request = buildMultipartFormRequest(
url, new File[]{file}, new String[]{fileKey}, null);
FormEncodingBuilder builder = new FormEncodingBuilder();
builder.add("username","張鴻洋");
Request request = new Request.Builder()
.url(url)
.post(builder.build())
.build();
mOkHttpClient.newCall(request).enqueue(new Callback(){});
大家都清楚,post的時候,參數是包含在請求體中的;所以我們通過FormEncodingBuilder。添加多個String鍵值對,然後去構造RequestBody,最後完成我們Request的構造。
後面的就和上面一樣了。
(三)基於Http的文件上傳
接下來我們在介紹一個可以構造RequestBody的Builder,叫做MultipartBuilder
。當我們需要做類似於表單上傳的時候,就可以使用它來構造我們的requestBody。
File file = new File(Environment.getExternalStorageDirectory(), "balabala.mp4");
RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
RequestBody requestBody = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"username\""),
RequestBody.create(null, "張鴻洋"))
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"mFile\";
filename=\"wjd.mp4\""), fileBody)
.build();
Request request = new Request.Builder()
.url("http://192.168.1.103:8080/okHttpServer/fileUpload")
.post(requestBody)
.build();
Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback()
{
//...
});
上述代碼向服務器傳遞了一個鍵值對username:張鴻洋
和一個文件。我們通過MultipartBuilder的addPart方法可以添加鍵值對或者文件。
其實類似於我們拼接模擬瀏覽器行爲的方式,如果你對這塊不瞭解,可以參考:從原理角度解析Android (Java) http 文件上傳
ok,對於我們最開始的目錄還剩下圖片下載,文件下載;這兩個一個是通過回調的Response拿到byte[]然後decode成圖片;文件下載,就是拿到inputStream做寫文件操作,我們這裏就不贅述了。
關於用法,也可以參考泡網OkHttp使用教程
接下來我們主要看如何封裝上述的代碼。
三、封裝
由於按照上述的代碼,寫多個請求肯定包含大量的重複代碼,所以我希望封裝後的代碼調用是這樣的:
(一)使用
-
一般的get請求
OkHttpClientManager.getAsyn("https://www.baidu.com", new OkHttpClientManager.ResultCallback<String>()
{
@Override
public void onError(Request request, Exception e)
{
e.printStackTrace();
}
@Override
public void onResponse(String u)
{
mTv.setText(u);//注意這裏是UI線程
}
});
-
對於一般的請求,我們希望給個url,然後CallBack裏面直接操作控件。
-
文件上傳且攜帶參數
我們希望提供一個方法,傳入url,params,file,callback即可。
OkHttpClientManager.postAsyn("http://192.168.1.103:8080/okHttpServer/fileUpload",//
new OkHttpClientManager.ResultCallback<String>()
{
@Override
public void onError(Request request, IOException e)
{
e.printStackTrace();
}
@Override
public void onResponse(String result)
{
}
},//
file,//
"mFile",//
new OkHttpClientManager.Param[]{
new OkHttpClientManager.Param("username", "zhy"),
new OkHttpClientManager.Param("password", "123")}
);
鍵值對沒什麼說的,參數3爲file,參數4爲file對應的name,這個name不是文件的名字;
對應於http中的
<input type="file" name="mFile" >
對應的是name後面的值,即mFile.
3.文件下載
對於文件下載,提供url,目標dir,callback即可。
OkHttpClientManager.downloadAsyn(
"http://192.168.1.103:8080/okHttpServer/files/messenger_01.png",
Environment.getExternalStorageDirectory().getAbsolutePath(),
new OkHttpClientManager.ResultCallback<String>()
{
@Override
public void onError(Request request, IOException e)
{
}
@Override
public void onResponse(String response)
{
//文件下載成功,這裏回調的reponse爲文件的absolutePath
}
});
4.展示圖片
展示圖片,我們希望提供一個url和一個imageview,如果下載成功,直接幫我們設置上即可。
OkHttpClientManager.displayImage(mImageView,
"http://images.csdn.net/20150817/1.jpg");
內部會自動根據imageview的大小自動對圖片進行合適的壓縮。雖然,這裏可能不適合一次性加載大量圖片的場景,但是對於app中偶爾有幾個圖片的加載,還是可用的。四、整合Gson
很多人提出項目中使用時,服務端返回的是Json字符串,希望客戶端回調可以直接拿到對象,於是整合進入了Gson,完善該功能。
(一)直接回調對象
例如現在有個User實體類:
package com.zhy.utils.http.okhttp;
public class User {
public String username ;
public String password ;
public User() {
// TODO Auto-generated constructor stub
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public String toString()
{
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
服務端返回:
{"username":"zhy","password":"123"}
客戶端可以如下方式調用:
OkHttpClientManager.getAsyn("http://192.168.56.1:8080/okHttpServer/user!getUser",
new OkHttpClientManager.ResultCallback<User>()
{
@Override
public void onError(Request request, Exception e)
{
e.printStackTrace();
}
@Override
public void onResponse(User user)
{
mTv.setText(u.toString());//UI線程
}
});
我們傳入泛型User,在onResponse裏面直接回調User對象。
這裏特別要注意的事,如果在json字符串->實體對象
過程中發生錯誤,程序不會崩潰,onError
方法會被回調。
注意:這裏做了少許的更新,接口命名從StringCallback
修改爲ResultCallback
。接口中的onFailure
方法修改爲onError
。
(二) 回調對象集合
依然是上述的User類,服務端返回
[{"username":"zhy","password":"123"},{"username":"lmj","password":"12345"}]
則客戶端可以如下調用:
OkHttpClientManager.getAsyn("http://192.168.56.1:8080/okHttpServer/user!getUsers",
new OkHttpClientManager.ResultCallback<List<User>>()
{
@Override
public void onError(Request request, Exception e)
{
e.printStackTrace();
}
@Override
public void onResponse(List<User> us)
{
Log.e("TAG", us.size() + "");
mTv.setText(us.get(1).toString());
}
});
唯一的區別,就是泛型變爲List<User>
,ok
, 如果發現bug或者有任何意見歡迎留言。源碼
ok,基本介紹完了,對於封裝的代碼其實也很簡單,我就直接貼出來了,因爲也沒什麼好介紹的,如果你看完上面的用法,肯定可以看懂:
package com.zhy.utils.http.okhttp;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.widget.ImageView;
import com.google.gson.Gson;
import com.google.gson.internal.$Gson$Types;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.MultipartBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Created by zhy on 15/8/17.
*/
public class OkHttpClientManager
{
private static OkHttpClientManager mInstance;
private OkHttpClient mOkHttpClient;
private Handler mDelivery;
private Gson mGson;
private static final String TAG = "OkHttpClientManager";
private OkHttpClientManager()
{
mOkHttpClient = new OkHttpClient();
//cookie enabled
mOkHttpClient.setCookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER));
mDelivery = new Handler(Looper.getMainLooper());
mGson = new Gson();
}
public static OkHttpClientManager getInstance()
{
if (mInstance == null)
{
synchronized (OkHttpClientManager.class)
{
if (mInstance == null)
{
mInstance = new OkHttpClientManager();
}
}
}
return mInstance;
}
/**
* 同步的Get請求
*
* @param url
* @return Response
*/
private Response _getAsyn(String url) throws IOException
{
final Request request = new Request.Builder()
.url(url)
.build();
Call call = mOkHttpClient.newCall(request);
Response execute = call.execute();
return execute;
}
/**
* 同步的Get請求
*
* @param url
* @return 字符串
*/
private String _getAsString(String url) throws IOException
{
Response execute = _getAsyn(url);
return execute.body().string();
}
/**
* 異步的get請求
*
* @param url
* @param callback
*/
private void _getAsyn(String url, final ResultCallback callback)
{
final Request request = new Request.Builder()
.url(url)
.build();
deliveryResult(callback, request);
}
/**
* 同步的Post請求
*
* @param url
* @param params post的參數
* @return
*/
private Response _post(String url, Param... params) throws IOException
{
Request request = buildPostRequest(url, params);
Response response = mOkHttpClient.newCall(request).execute();
return response;
}
/**
* 同步的Post請求
*
* @param url
* @param params post的參數
* @return 字符串
*/
private String _postAsString(String url, Param... params) throws IOException
{
Response response = _post(url, params);
return response.body().string();
}
/**
* 異步的post請求
*
* @param url
* @param callback
* @param params
*/
private void _postAsyn(String url, final ResultCallback callback, Param... params)
{
Request request = buildPostRequest(url, params);
deliveryResult(callback, request);
}
/**
* 異步的post請求
*
* @param url
* @param callback
* @param params
*/
private void _postAsyn(String url, final ResultCallback callback, Map<String, String> params)
{
Param[] paramsArr = map2Params(params);
Request request = buildPostRequest(url, paramsArr);
deliveryResult(callback, request);
}
/**
* 同步基於post的文件上傳
*
* @param params
* @return
*/
private Response _post(String url, File[] files, String[] fileKeys, Param... params) throws IOException
{
Request request = buildMultipartFormRequest(url, files, fileKeys, params);
return mOkHttpClient.newCall(request).execute();
}
private Response _post(String url, File file, String fileKey) throws IOException
{
Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, null);
return mOkHttpClient.newCall(request).execute();
}
private Response _post(String url, File file, String fileKey, Param... params) throws IOException
{
Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, params);
return mOkHttpClient.newCall(request).execute();
}
/**
* 異步基於post的文件上傳
*
* @param url
* @param callback
* @param files
* @param fileKeys
* @throws IOException
*/
private void _postAsyn(String url, ResultCallback callback, File[] files, String[] fileKeys, Param... params) throws IOException
{
Request request = buildMultipartFormRequest(url, files, fileKeys, params);
deliveryResult(callback, request);
}
/**
* 異步基於post的文件上傳,單文件不帶參數上傳
*
* @param url
* @param callback
* @param file
* @param fileKey
* @throws IOException
*/
private void _postAsyn(String url, ResultCallback callback, File file, String fileKey) throws IOException
{
Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, null);
deliveryResult(callback, request);
}
/**
* 異步基於post的文件上傳,單文件且攜帶其他form參數上傳
*
* @param url
* @param callback
* @param file
* @param fileKey
* @param params
* @throws IOException
*/
private void _postAsyn(String url, ResultCallback callback, File file, String fileKey, Param... params) throws IOException
{
Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, params);
deliveryResult(callback, request);
}
/**
* 異步下載文件
*
* @param url
* @param destFileDir 本地文件存儲的文件夾
* @param callback
*/
private void _downloadAsyn(final String url, final String destFileDir, final ResultCallback callback)
{
final Request request = new Request.Builder()
.url(url)
.build();
final Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback()
{
@Override
public void onFailure(final Request request, final IOException e)
{
sendFailedStringCallback(request, e, callback);
}
@Override
public void onResponse(Response response)
{
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
try
{
is = response.body().byteStream();
File file = new File(destFileDir, getFileName(url));
fos = new FileOutputStream(file);
while ((len = is.read(buf)) != -1)
{
fos.write(buf, 0, len);
}
fos.flush();
//如果下載文件成功,第一個參數爲文件的絕對路徑
sendSuccessResultCallback(file.getAbsolutePath(), callback);
} catch (IOException e)
{
sendFailedStringCallback(response.request(), e, callback);
} finally
{
try
{
if (is != null) is.close();
} catch (IOException e)
{
}
try
{
if (fos != null) fos.close();
} catch (IOException e)
{
}
}
}
});
}
private String getFileName(String path)
{
int separatorIndex = path.lastIndexOf("/");
return (separatorIndex < 0) ? path : path.substring(separatorIndex + 1, path.length());
}
/**
* 加載圖片
*
* @param view
* @param url
* @throws IOException
*/
private void _displayImage(final ImageView view, final String url, final int errorResId)
{
final Request request = new Request.Builder()
.url(url)
.build();
Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback()
{
@Override
public void onFailure(Request request, IOException e)
{
setErrorResId(view, errorResId);
}
@Override
public void onResponse(Response response)
{
InputStream is = null;
try
{
is = response.body().byteStream();
ImageUtils.ImageSize actualImageSize = ImageUtils.getImageSize(is);
ImageUtils.ImageSize imageViewSize = ImageUtils.getImageViewSize(view);
int inSampleSize = ImageUtils.calculateInSampleSize(actualImageSize, imageViewSize);
try
{
is.reset();
} catch (IOException e)
{
response = _getAsyn(url);
is = response.body().byteStream();
}
BitmapFactory.Options ops = new BitmapFactory.Options();
ops.inJustDecodeBounds = false;
ops.inSampleSize = inSampleSize;
final Bitmap bm = BitmapFactory.decodeStream(is, null, ops);
mDelivery.post(new Runnable()
{
@Override
public void run()
{
view.setImageBitmap(bm);
}
});
} catch (Exception e)
{
setErrorResId(view, errorResId);
} finally
{
if (is != null) try
{
is.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
}
});
}
private void setErrorResId(final ImageView view, final int errorResId)
{
mDelivery.post(new Runnable()
{
@Override
public void run()
{
view.setImageResource(errorResId);
}
});
}
//*************對外公佈的方法************
public static Response getAsyn(String url) throws IOException
{
return getInstance()._getAsyn(url);
}
public static String getAsString(String url) throws IOException
{
return getInstance()._getAsString(url);
}
public static void getAsyn(String url, ResultCallback callback)
{
getInstance()._getAsyn(url, callback);
}
public static Response post(String url, Param... params) throws IOException
{
return getInstance()._post(url, params);
}
public static String postAsString(String url, Param... params) throws IOException
{
return getInstance()._postAsString(url, params);
}
public static void postAsyn(String url, final ResultCallback callback, Param... params)
{
getInstance()._postAsyn(url, callback, params);
}
public static void postAsyn(String url, final ResultCallback callback, Map<String, String> params)
{
getInstance()._postAsyn(url, callback, params);
}
public static Response post(String url, File[] files, String[] fileKeys, Param... params) throws IOException
{
return getInstance()._post(url, files, fileKeys, params);
}
public static Response post(String url, File file, String fileKey) throws IOException
{
return getInstance()._post(url, file, fileKey);
}
public static Response post(String url, File file, String fileKey, Param... params) throws IOException
{
return getInstance()._post(url, file, fileKey, params);
}
public static void postAsyn(String url, ResultCallback callback, File[] files, String[] fileKeys, Param... params) throws IOException
{
getInstance()._postAsyn(url, callback, files, fileKeys, params);
}
public static void postAsyn(String url, ResultCallback callback, File file, String fileKey) throws IOException
{
getInstance()._postAsyn(url, callback, file, fileKey);
}
public static void postAsyn(String url, ResultCallback callback, File file, String fileKey, Param... params) throws IOException
{
getInstance()._postAsyn(url, callback, file, fileKey, params);
}
public static void displayImage(final ImageView view, String url, int errorResId) throws IOException
{
getInstance()._displayImage(view, url, errorResId);
}
public static void displayImage(final ImageView view, String url)
{
getInstance()._displayImage(view, url, -1);
}
public static void downloadAsyn(String url, String destDir, ResultCallback callback)
{
getInstance()._downloadAsyn(url, destDir, callback);
}
//****************************
private Request buildMultipartFormRequest(String url, File[] files,
String[] fileKeys, Param[] params)
{
params = validateParam(params);
MultipartBuilder builder = new MultipartBuilder()
.type(MultipartBuilder.FORM);
for (Param param : params)
{
builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + param.key + "\""),
RequestBody.create(null, param.value));
}
if (files != null)
{
RequestBody fileBody = null;
for (int i = 0; i < files.length; i++)
{
File file = files[i];
String fileName = file.getName();
fileBody = RequestBody.create(MediaType.parse(guessMimeType(fileName)), file);
//TODO 根據文件名設置contentType
builder.addPart(Headers.of("Content-Disposition",
"form-data; name=\"" + fileKeys[i] + "\"; filename=\"" + fileName + "\""),
fileBody);
}
}
RequestBody requestBody = builder.build();
return new Request.Builder()
.url(url)
.post(requestBody)
.build();
}
private String guessMimeType(String path)
{
FileNameMap fileNameMap = URLConnection.getFileNameMap();
String contentTypeFor = fileNameMap.getContentTypeFor(path);
if (contentTypeFor == null)
{
contentTypeFor = "application/octet-stream";
}
return contentTypeFor;
}
private Param[] validateParam(Param[] params)
{
if (params == null)
return new Param[0];
else return params;
}
private Param[] map2Params(Map<String, String> params)
{
if (params == null) return new Param[0];
int size = params.size();
Param[] res = new Param[size];
Set<Map.Entry<String, String>> entries = params.entrySet();
int i = 0;
for (Map.Entry<String, String> entry : entries)
{
res[i++] = new Param(entry.getKey(), entry.getValue());
}
return res;
}
private static final String SESSION_KEY = "Set-Cookie";
private static final String mSessionKey = "JSESSIONID";
private Map<String, String> mSessions = new HashMap<String, String>();
private void deliveryResult(final ResultCallback callback, Request request)
{
mOkHttpClient.newCall(request).enqueue(new Callback()
{
@Override
public void onFailure(final Request request, final IOException e)
{
sendFailedStringCallback(request, e, callback);
}
@Override
public void onResponse(final Response response)
{
try
{
final String string = response.body().string();
if (callback.mType == String.class)
{
sendSuccessResultCallback(string, callback);
} else
{
Object o = mGson.fromJson(string, callback.mType);
sendSuccessResultCallback(o, callback);
}
} catch (IOException e)
{
sendFailedStringCallback(response.request(), e, callback);
} catch (com.google.gson.JsonParseException e)//Json解析的錯誤
{
sendFailedStringCallback(response.request(), e, callback);
}
}
});
}
private void sendFailedStringCallback(final Request request, final Exception e, final ResultCallback callback)
{
mDelivery.post(new Runnable()
{
@Override
public void run()
{
if (callback != null)
callback.onError(request, e);
}
});
}
private void sendSuccessResultCallback(final Object object, final ResultCallback callback)
{
mDelivery.post(new Runnable()
{
@Override
public void run()
{
if (callback != null)
{
callback.onResponse(object);
}
}
});
}
private Request buildPostRequest(String url, Param[] params)
{
if (params == null)
{
params = new Param[0];
}
FormEncodingBuilder builder = new FormEncodingBuilder();
for (Param param : params)
{
builder.add(param.key, param.value);
}
RequestBody requestBody = builder.build();
return new Request.Builder()
.url(url)
.post(requestBody)
.build();
}
public static abstract class ResultCallback<T>
{
Type mType;
public ResultCallback()
{
mType = getSuperclassTypeParameter(getClass());
}
static Type getSuperclassTypeParameter(Class<?> subclass)
{
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class)
{
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
public abstract void onError(Request request, Exception e);
public abstract void onResponse(T response);
}
public static class Param
{
public Param()
{
}
public Param(String key, String value)
{
this.key = key;
this.value = value;
}
String key;
String value;
}
}
轉載出處:http://blog.csdn.net/lmj623565791/article/details/47911083