Okhttp使用和原始碼分析二(OkHttp3.x用法)
上一篇介紹了OkHttp2.x的用法,這一篇文章我們來對照OkHttp2.x版本來看看,OkHttp3使用起來有那些變化?
1.使用前準備,Android Studio 配置gradle:
compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'com.squareup.okio:okio:1.7.0'
新增網路許可權:
<uses-permission android:name="android.permission.INTERNET"/>
2.非同步GET請求
private void getAsynHttp() {
mOkHttpClient=new OkHttpClient();
Request.Builder requestBuilder = new Request.Builder().url("http://www.baidu.com");
//可以省略,預設是GET請求
requestBuilder.method("GET",null);
Request request = requestBuilder.build();
Call mcall= mOkHttpClient.newCall(request);
mcall.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (null != response.cacheResponse()) {
String str = response.cacheResponse().toString();
Log.i("yzw" , "cache---" + str);
} else {
response.body().string();
String str = response.networkResponse().toString();
Log.i("yzw", "network---" + str);
}
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "請求成功", Toast.LENGTH_SHORT).show();
}
});
}
});
}
與2.x版本並沒有什麼不同,比較鬱悶的是回撥仍然不在UI執行緒。
2.非同步POST請求
OkHttp3非同步POST請求和OkHttp2.x有一些差別就是沒有FormEncodingBuilder這個類,替代它的是功能更加強大的FormBody:
private void postAsynHttp() {
mOkHttpClient=new OkHttpClient();
RequestBody formBody = new FormBody.Builder()
.add("size", "10")
.build();
Request request = new Request.Builder()
.url("http://api.1-blog.com/biz/bizserver/article/list.do")
.post(formBody)
.build();
Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String str = response.body().string();
Log.i("yzw", str);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "請求成功", Toast.LENGTH_SHORT).show();
}
});
}
});
}
3.非同步上傳檔案
上傳檔案本身也是一個POST請求,上一篇沒有講,這裡我們補上。首先定義上傳檔案型別:
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
將sdcard根目錄的wangshu.txt檔案上傳到伺服器上:
private void postAsynFile() {
mOkHttpClient=new OkHttpClient();
File file = new File("/sdcard/wangshu.txt");
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
.build();
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i("yzw",response.body().string());
}
});
}
當然如果想要改為同步的上傳檔案只要呼叫 mOkHttpClient.newCall(request).execute()就可以了。
當然不要忘了新增如下許可權:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
4.非同步下載檔案
下載檔案同樣在上一篇沒有講到,實現起來比較簡單,在這裡下載一張圖片,我們得到Response後將流寫進我們指定的圖片檔案中就可以了。
private void downAsynFile() {
mOkHttpClient = new OkHttpClient();
String url = "http://img.my.csdn.net/uploads/201603/26/1458988468_5804.jpg";
Request request = new Request.Builder().url(url).build();
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) {
InputStream inputStream = response.body().byteStream();
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(new File("/sdcard/wangshu.jpg"));
byte[] buffer = new byte[2048];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
}
fileOutputStream.flush();
} catch (IOException e) {
Log.i("wangshu", "IOException");
e.printStackTrace();
}
Log.d("wangshu", "檔案下載成功");
}
});
}
5.非同步上傳Multipart檔案
這種場景很常用,我們有時會上傳檔案同時還需要傳其他型別的欄位,OkHttp3實現起來很簡單,需要注意的是沒有伺服器接收我這個Multipart檔案,所以這裡只是舉個例子,具體的應用還要結合實際工作中對應的伺服器。
首先定義上傳檔案型別:
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private void sendMultipart(){
mOkHttpClient = new OkHttpClient();
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "yzw")
.addFormDataPart("image", "yzw.png",
RequestBody.create(MEDIA_TYPE_PNG, new File("/sdcard/yzw.jpg")))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + "...")
.url("https://api.imgur.com/3/image")
.post(requestBody)
.build();
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i("yzw", response.body().string());
}
});
}
6.設定超時時間和快取
和OkHttp2.x有區別的是不能通過OkHttpClient直接設定超時時間和快取了,而是通過OkHttpClient.Builder來設定,通過builder配置好OkHttpClient後用builder.build()來返回OkHttpClient,所以我們通常不會呼叫new OkHttpClient()來得到OkHttpClient,而是通過builder.build():
File sdcache = getExternalCacheDir();
int cacheSize = 10 * 1024 * 1024;
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.cache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
OkHttpClient mOkHttpClient=builder.build();
7.關於取消請求和封裝
取消請求仍舊可以呼叫call.cancel(),只是這一版中增加了一個cancelAll()的方法:
/**
* 取消某單個請求任務
*/
public void cancelRequest(CharSequence url) {
try {
//任務排程中正在排隊的任務
for (Call call : okHttpClient.dispatcher().queuedCalls()) {
if (call.request().tag().equals(url))
call.cancel();
}
//任務排程中正在執行的任務
for (Call call : okHttpClient.dispatcher().runningCalls()) {
if (call.request().tag().equals(url))
call.cancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 取消全部請求任務
*/
public void cancelAllRequest() {
try {
okHttpClient.dispatcher().cancelAll();
} catch (Exception e) {
e.printStackTrace();
}
}
8.封裝
public class OkHttp3Engine {
private static OkHttp3Engine mInstance;
private OkHttpClient okHttpClient = null;
private int DEFAULT_HTTP_TIMEOUT = 15_000;
private int SIZE_OF_CACHE = 5 * 1024 * 1024;
private CharSequence StringEncode = "utf-8";
private static final MediaType Json = MediaType.parse("application/json; charset=utf-8");
private static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");
private CharSequence POST = "post";
private CharSequence GET = "get";
private Handler handler = null;
private boolean hasCache = true;
public OkHttp3Engine() {
super();
handler = new Handler(Looper.getMainLooper());
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
builder.readTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
builder.writeTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
okHttpClient = builder.build();
}
public static OkHttp3Engine getInstance() {
if (mInstance == null) {
synchronized (OkHttp3Engine.class) {
if (mInstance == null) {
mInstance = new OkHttp3Engine();
}
}
}
return mInstance;
}
public <T> void fetchStringPost(CharSequence url, T resultBean, Map<CharSequence, CharSequence> map, FetchDataInterface fetchDatainterface) {
fetchString(url.toString(), resultBean, map, POST, false, null, fetchDatainterface);
}
public <T> void fetchStringGet(CharSequence url, T resultBean, Map<CharSequence, CharSequence> map, FetchDataInterface fetchDatainterface) {
fetchString(url.toString(), resultBean, map, GET, false, null, fetchDatainterface);
}
public <T> void fetchStringAsyncPost(CharSequence url, T resultBean, Map<CharSequence, CharSequence> map, FetchDataInterface fetchDatainterface) {
AsyncfetchString(url.toString(), resultBean, map, POST, false, null, fetchDatainterface);
}
public <T> void fetchStringAsyncGet(CharSequence url, T resultBean, Map<CharSequence, CharSequence> map, FetchDataInterface fetchDatainterface) {
AsyncfetchString(url.toString(), resultBean, map, GET, false, null, fetchDatainterface);
}
public <T> void fetchStringPostJson(CharSequence url, T resultBean, CharSequence json, FetchDataInterface fetchDatainterface) {
fetchString(url.toString(), resultBean, null, POST, true, json, fetchDatainterface);
}
public <T> void fetchStringAsyncPostJson(CharSequence url, T resultBean, CharSequence json, FetchDataInterface fetchDatainterface) {
AsyncfetchString(url.toString(), resultBean, null, POST, true, json, fetchDatainterface);
}
public void setOkHttpCache(boolean hasCache, Context mContext) {
this.hasCache = hasCache;
File cacheDirectory = new File(mContext.getExternalCacheDir(), "HttpCache");
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
builder.readTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
builder.writeTimeout(DEFAULT_HTTP_TIMEOUT, TimeUnit.SECONDS);
if (okHttpClient != null) {
okHttpClient.dispatcher().cancelAll();
}
if (hasCache) {
Cache cache = new Cache(cacheDirectory, SIZE_OF_CACHE);
okHttpClient = builder.cache(cache).build();
} else {
okHttpClient = builder.build();
}
}
public boolean isHasCache() {
return hasCache;
}
/**
* 取消某單個請求任務
*/
public void cancelRequest(CharSequence url) {
try {
//任務排程中正在排隊的任務
for (Call call : okHttpClient.dispatcher().queuedCalls()) {
if (call.request().tag().equals(url))
call.cancel();
}
//任務排程中正在執行的任務
for (Call call : okHttpClient.dispatcher().runningCalls()) {
if (call.request().tag().equals(url))
call.cancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 取消全部請求任務
*/
public void cancelAllRequest() {
try {
okHttpClient.dispatcher().cancelAll();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 回撥介面
*/
public interface FetchDataInterface {
<T> void successful(T result);
void failed(CharSequence message);
}
/**
* 同步請求
*/
private <T> void fetchString(CharSequence url, T resultBean, Map<CharSequence, CharSequence> map, CharSequence method, boolean isSendJson, CharSequence json, FetchDataInterface fetchDatainterface) {
Request request = null;
try {
if (method.equals(POST)) {
RequestBody body = null;
if (isSendJson) {
body = RequestBody.create(Json, json.toString());
} else {
//pMap.put("param", JSONObject.toJSONString(map));
body = mapToRequestBody(map);
}
if (isHasCache())
request = new Request.Builder().tag(url).url(new URI(url.toString()).toURL()).post(body).build();
else {
request = new Request.Builder().tag(url).url(new URI(url.toString()).toURL()).cacheControl(CacheControl.FORCE_CACHE).post(body).build();
}
} else {
//CharSequence param = setMapToParam(map).toString();
if (isHasCache())
request = new Request.Builder().tag(url).url(new URI(builderGetUri(url.toString(), map).toString()).toURL()).get().build();
else {
request = new Request.Builder().tag(url).url(new URI(builderGetUri(url.toString(), map).toString()).toURL()).cacheControl(CacheControl.FORCE_CACHE).get().build();
}
}
Response response = okHttpClient.newCall(request).execute();
if (null == response || !response.isSuccessful()) {
sendCallBack("respponse isnull", false, fetchDatainterface);
return;
}
byte[] bytes = response.body().bytes();
if (null == bytes) {
sendCallBack("responsebody isnull", false, fetchDatainterface);
return;
}
String result = new String(bytes, StringEncode.toString());
if (TextUtils.isEmpty(result)) {
sendCallBack("strcode isnull", false, fetchDatainterface);
return;
}
/**
* 判斷返回的是否是json格式的資料
*/
JSONObject jsonString = JSON.parseObject(result);
T t = (T) JSON.parseObject(jsonString.toString(), resultBean.getClass());
sendCallBack(t, true, fetchDatainterface);
} catch (Exception e) {
e.printStackTrace();
sendCallBack(e.getMessage(), false, fetchDatainterface);
}
}
/**
* 非同步請求
*/
private <T> void AsyncfetchString(final CharSequence url, final T resultBean, Map<CharSequence, CharSequence> map, CharSequence method, boolean isSendJson, final CharSequence json, final FetchDataInterface fetchDatainterface) {
Request request = null;
try {
if (method.equals(POST)) {
RequestBody body = null;
if (isSendJson) {
body = RequestBody.create(Json, json.toString());
} else {
//pMap.put("param", JSONObject.toJSONString(map));
body = mapToRequestBody(map);
}
if (isHasCache())
request = new Request.Builder().tag(url).url(new URI(url.toString()).toURL()).post(body).build();
else
request = new Request.Builder().tag(url).url(new URI(url.toString()).toURL()).cacheControl(CacheControl.FORCE_CACHE).post(body).build();
} else {
//CharSequence param = setMapToParam(map).toString();
if (isHasCache())
request = new Request.Builder().tag(url).url(new URI(builderGetUri(url.toString(), map).toString()).toURL()).get().build();
else
request = new Request.Builder().tag(url).url(new URI(builderGetUri(url.toString(), map).toString()).toURL()).cacheControl(CacheControl.FORCE_CACHE).get().build();
}
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
if (null == response || !response.isSuccessful()) {
sendCallBack("response isnull", false, fetchDatainterface);
return;
}
byte[] bytes = response.body().bytes();
if (null == bytes) {
sendCallBack("responsebody isnull", false, fetchDatainterface);
return;
}
String result = new String(bytes, StringEncode.toString());
if (TextUtils.isEmpty(result)) {
sendCallBack("strcode isnull", false, fetchDatainterface);
return;
}
/**
* 判斷返回的是否是json格式的資料
*/
try {
JSONObject jsonString = JSON.parseObject(result);
T t = (T) JSON.parseObject(jsonString.toString(), resultBean.getClass());
sendCallBack(t, true, fetchDatainterface);
} catch (Exception e) {
e.printStackTrace();
sendCallBack(e.getMessage(), false, fetchDatainterface);
}
}
@Override
public void onFailure(Call call, IOException e) {
sendCallBack(e.getMessage(), false, fetchDatainterface);
}
});
} catch (Exception e) {
e.printStackTrace();
sendCallBack(e.getMessage(), false, fetchDatainterface);
}
}
private RequestBody mapToRequestBody(Map<CharSequence, CharSequence> kv) throws IOException {
FormBody.Builder formBody = new FormBody.Builder();
for (CharSequence key : kv.keySet()) {
formBody.add(key.toString(), kv.get(key).toString());
}
return formBody.build();
}
private CharSequence setMapToParam(Map<CharSequence, CharSequence> hashMap) {
StringBuilder param = new StringBuilder();
if (hashMap == null || hashMap.size() == 0) {
return "";
}
for (Map.Entry<CharSequence, CharSequence> entry : hashMap.entrySet()) {
// 如果請求引數中有中文,需要進行URLEncoder編碼
if (entry.getValue() != null) {
try {
param.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue().toString(), StringEncode.toString()));
param.append("&");
} catch (Exception e) {
e.printStackTrace();
}
}
}
if (param.length() > 0) {
param.deleteCharAt(param.length() - 1);
}
return param.toString();
}
private CharSequence builderGetUri(CharSequence uri, Map<CharSequence, CharSequence> map) {
StringBuilder sb = new StringBuilder();
try {
while (uri.toString().endsWith("/")) {
try {
uri = uri.toString().substring(0, uri.length() - 1);
} catch (Exception e) {
e.printStackTrace();
}
}
sb.append(uri).append("?");
if (map != null && map.size() != 0) {
for (Map.Entry<CharSequence, CharSequence> entry : map.entrySet()) {
// 如果請求引數中有中文,需要進行URLEncoder編碼
sb.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue().toString(), StringEncode.toString()));
sb.append("&");
}
sb.deleteCharAt(sb.length() - 1);
}
} catch (Exception e) {
e.printStackTrace();
}
Log.d("builderGetUri", sb.toString());
return sb.toString();
}
/**
* 檔案上傳
*
* @param url 上傳的伺服器地址
* @param file 上傳的本地檔案
* @param params 上傳檔案時帶的引數
* @param fetchDatainterface 上傳完成後的回撥介面
*/
public void uploadFile(final CharSequence url, File file, Map<String, String> params, final FetchDataInterface fetchDatainterface) {
Request request = new Request.Builder().headers(Headers.of(params)).url(url.toString()).post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file)).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
sendCallBack(e.getMessage(), false, fetchDatainterface);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
sendCallBack(response.body().string(), true, fetchDatainterface);
}
});
}
/**
* 檔案下載
*
* @param url 下載的伺服器地址
* @param localSavePath 下載後儲存的本地地址
* @param fetchDatainterface 下載完成後的回撥介面
*/
public void downloadFile(final String url, final String localSavePath, final FetchDataInterface fetchDatainterface) throws Exception {
Request request = new Request.Builder().url(url).build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
sendCallBack(e.getMessage(), false, fetchDatainterface);
}
@Override
public void onResponse(Call call, Response response) {
try {
InputStream inputStream = response.body().byteStream();
FileOutputStream fileOutputStream = new FileOutputStream(new File(localSavePath));
byte[] buffer = new byte[2048];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
}
inputStream.close();
fileOutputStream.flush();
fileOutputStream.close();
sendCallBack(localSavePath, true, fetchDatainterface);
} catch (IOException e) {
Log.i("downloadFile", "IOException");
e.printStackTrace();
}
Log.d("downloadFile", "檔案下載成功");
}
});
}
/**
* 圖片下載並展示
*
* @param image 展示的控制元件
* @param url 下載的地址
*/
public void downloadImgShowImageView(final ImageView image, final String url) throws Exception {
Request request = new Request.Builder().url(url).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
image.setImageResource(0);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = response.body().byteStream();
ImageUtils.ImageSize actualImageSize = ImageUtils.getImageSize(is);
ImageUtils.ImageSize imageViewSize = ImageUtils.getImageViewSize(image);
int inSampleSize = ImageUtils.calculateInSampleSize(actualImageSize, imageViewSize);
try {
is.reset();
} catch (Exception e) {
Request request = new Request.Builder().url(url).build();
response = okHttpClient.newCall(request).execute();
is = response.body().byteStream();
}
BitmapFactory.Options ops = new BitmapFactory.Options();
ops.inJustDecodeBounds = false;
ops.inSampleSize = inSampleSize;
final Bitmap bm = BitmapFactory.decodeStream(is, null, ops);
image.post(new Runnable() {
@Override
public void run() {
image.setImageBitmap(bm);
}
});
is.close();
}
});
}
private <T> void sendCallBack(final T t, final boolean isSccessful, final FetchDataInterface fetchDatainterface) {
handler.post(new Runnable() {
@Override
public void run() {
if (isSccessful)
fetchDatainterface.successful(t);
else
fetchDatainterface.failed(t.toString());
}
});
}
}
我們以一個get請求為例使用:
OkHttp3Engine.getInstance().fetchStringAsyncGet("http://www.baidu.com", new String(), null, new OkHttp3Engine.FetchDataInterface() {
@Override
public <T> void successful(T result) {
Log.d("yzw", result.toString());
}
@Override
public void failed(CharSequence message) {
}
});