Android网络请求框架很多,为什么我选择的是Okhttp?
也许是不经意之间听到Android4.4的源码里面都用了她,又或者是她支持Google开发的SPDY协议,
又或者她来自鼎鼎大名的Square?
其实我也不知道,就想试一下;
在使用过程出现很多问题,相比于那些成熟的网络框架, 体现出来的是OkHttp相当于HttpClient和HttpURLConnection这种底层一点的,而其余的所有网络请求框架基本都是对HttpClient和HttpURLConnection封装,所以很多框架都在网络请求上可以用她来作为底层,Volley可以用她再来作为底层的网络请求,Fresco也可以,在Fresco的一个Demo中,Fresco+OkHttp速度最快,不知道是不是偶然,我自己测试是如此,等等其他很多。
在当前项目中使用的是这个,所以为了方便处理,做了一些封装,有自己的封装也有参考网上资料的,整合力一下。
先看几个请求方式和请求结果 gif是按照顺序来操作
文件上传的log细节
文件下载的log细节
先看看对OkHttp使用的封装
public class OkHttpUtil {
public static final OkHttpClient mOkHttpClient = new OkHttpClient();
static {
mOkHttpClient.setConnectTimeout(30, TimeUnit.SECONDS);
mOkHttpClient.setWriteTimeout(20, TimeUnit.SECONDS);
mOkHttpClient.setReadTimeout(30, TimeUnit.SECONDS);
}
//自定义接口 目的是在调用的地方可以直接更新UI
public interface MyCallBack {
void onFailure(Request request, IOException e);
void onResponse(String json);
}
//配置OKHttp的Cookie策略,有些地方需要做一些cookie相关的操作
public static void setCookie(CookieManager cookieManager) {
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
mOkHttpClient.setCookieHandler(cookieManager);
}
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
/**
* 异步回调方式 请求 自定义回调接口 结果运行在UI线程
*/
public static void postData2Server(final Activity activity, Request request, final MyCallBack myCallBack) {
final String url = request.urlString();
try {
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(final Request request, final IOException e) {
if (activity == null) {
return;
}
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
myCallBack.onFailure(request, e);
}
});
}
@Override
public void onResponse(Response response) throws IOException {
final String json = response.body().string();
if (activity == null) {
return;
}
//直接更新UI
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
myCallBack.onResponse(json);
}
});
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Daemon
* -----------------一开始没有发现好的封装对于上传和下载
* 就用的下面的两个方法 后面发现在OKHttp的sample中有相关的例子 然后网上也有人写 就借鉴过来了
* --------------------所以这两个就没用了-----------------------------------------------
*/
/**
* 下载显示进度的方法1
*
* @param url
*/
public static void dowloadProgress(Context context, final String url) {
Request request = new Request.Builder().url(url)
.build();
new AsyncDownloader(context).execute(request);
}
private static class AsyncDownloader extends AsyncTask<Request, Long, Boolean> {
private Context context;
public AsyncDownloader(Context context) {
this.context = context;
}
@Override
protected Boolean doInBackground(Request... params) {
Call call = mOkHttpClient.newCall(params[0]);
try {
Response response = call.execute();
if (response.code() == 200) {
InputStream inputStream = null;
OutputStream output = null;
try {
inputStream = response.body().byteStream();
File file = new File(context.getCacheDir(), "download.liubo");
output = new FileOutputStream(file);
byte[] buff = new byte[1024 * 4];
long downloaded = 0;
long target = response.body().contentLength();
publishProgress(0L, target);
while (true) {
int readed = inputStream.read(buff);
if (readed == -1) {
break;
}
//write buff
output.write(buff, 0, readed);
downloaded += readed;
publishProgress(downloaded, target);
if (isCancelled()) {
return false;
}
}
output.flush();
return downloaded == target;
} catch (IOException ignore) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (output != null) {
output.close();
}
}
} else {
return false;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
@Override
protected void onProgressUpdate(Long... values) {
LogUtils.e(String.format("%d / %d", values[0], values[1]));
}
@Override
protected void onPostExecute(Boolean result) {
//下载完成后
}
}
/**
* 上传文件方法1 进度显示
*/
public static RequestBody createCustomRequestBody(final MediaType contentType, final File file) {
return new RequestBody() {
@Override
public MediaType contentType() {
return contentType;
}
@Override
public long contentLength() {
return file.length();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
Source source = null;
try {
source = Okio.source(file);
//sink.writeAll(source);
Buffer buf = new Buffer();
Long remaining = contentLength();
for (long readCount; (readCount = source.read(buf, 2048)) != -1; ) {
sink.write(buf, readCount);
LogUtils.e("source size: " + contentLength() + " remaining bytes: " + (remaining -= readCount));
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
}
}
这里是一些OKHttp使用中的一些操作
//如果json body这么构建
RequestBody body = RequestBody.create(JSON, json);
如果获取sessionID
Map<String, List<String>> map = new HashMap<String, List<String>>();
//获取cookie 然后打印 绑定
try {
BaseRequest.Cookies = response.headers().get("Set-Cookie");
Logger.getLogger().e(response.headers().toString());
map = mOkHttpClient.getCookieHandler().get(URI.create(url), response.headers().toMultimap());
for (String key : map.keySet()) {
syso(key + " -- " + map.get(key).toString());
if ("Cookie".equals(key)) {
sessionId = map.get(key).get(0);
}
}
} catch (IOException e) {
e.printStackTrace();
}
在请求的时候带上cookie 在Request构建的时候加上头部就行
.addHeader("cookie", sessionId)
其实很简单 只是新增一个接口 让其可以UI更新
其实大家看到了 上面有两个方法一个是 下载的进度显示和一个上传的进度显示 这是以前使用的 也能达到效果
后面有一天在微博看到相关的封装 对已上传和下载的进度显示 就集成进来项目中
红线部分是对于上传和下载的进度显示的类和接口
这里的话 去看这个吧 我是从这个看到的写的挺好的
http://blog.youkuaiyun.com/sbsujjbcy/article/details/48194701
或者这个也行 官方的demo
https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/com/squareup/okhttp/recipes/Progress.java
然后看看在整个项目中的一些设置
有BaseActivity的ondestyoy中的 请求取消
public class BaseActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void onDestroy() {
super.onDestroy();
OkHttpUtil.mOkHttpClient.cancel(this);
}
}
//MyApplication的oncreate中的okhttp的cookie的设置
public class MyApplication extends Application {
private static Context mInstance;
@Override
public void onCreate() {
super.onCreate();
mInstance = getBaseContext();
//cookie策略 让OkHttp接受所有的cookie
CookieManager cookieManager = new CookieManager();
OkHttpUtil.setCookie(cookieManager);
}
public static Context getContext() {
return mInstance;
}
}
实际运用
/**
* 数据获取Get请求
*/
Request request
= new Request.Builder().url("http://www.baidu.com")
.tag(this)
.build();
OkHttpUtil.postData2Server(this, request, new OkHttpUtil.MyCallBack() {
@Override
public void onFailure(Request request, IOException e) {
}
@Override
public void onResponse(String json) {
showData(json);
}
});
/**
* 数据上传 Post请求
*/
RequestBody requestBody = new FormEncodingBuilder()
.add("action_flag", "user_login")
.add("mobile_phone", "18566757335")
.add("user_password", "liubo123")
.build();
Request request = new Request.Builder()
.post(requestBody)
.url("登陆接口")
.tag(this)
.build();
OkHttpUtil.postData2Server(this, request, new OkHttpUtil.MyCallBack() {
@Override
public void onFailure(Request request, IOException e) {
}
@Override
public void onResponse(String json) {
showData(json);
}
});
/**
* 图片下载
*/
UIProgressResponseListener uiProgressResponseListener = new UIProgressResponseListener() {
@Override
public void onUIResponseProgress(long bytesRead, long contentLength, boolean done) {
LogUtils.e("bytesRead:" + bytesRead);
LogUtils.e("contentLength:" + contentLength);
LogUtils.e("done:" + done);
if (contentLength != -1) {
// LogUtils.e( (100 * bytesRead) / contentLength + "% done");
}
LogUtils.e("================================");
//ui层回调
tvshow.setText("下载进度 " + ((100 * bytesRead) / contentLength) + "%");
}
};
//构造请求
final Request request1 = new Request.Builder()
.url("网上资源接口")
.build();
//包装Response使其支持进度回调
ProgressHelper.addProgressResponseListener(OkHttpUtil.mOkHttpClient, uiProgressResponseListener)
.newCall(request1)
.enqueue(new Callback() {
@Override
public void onFailure(Request request, IOException e) {
Log.e("TAG", "error ", e);
}
@Override
public void onResponse(Response response) throws IOException {
InputStream inputStream = null;
OutputStream output = null;
try {
inputStream = response.body().byteStream();
file = new File(getCacheDir(), "download.jpg");
output = new FileOutputStream(file);
byte[] buff = new byte[1024 * 4];
while (true) {
int readed = inputStream.read(buff);
if (readed == -1) {
break;
}
//write buff
output.write(buff, 0, readed);
}
output.flush();
} catch (IOException e) {
file = null;
e.printStackTrace();
} finally {
if (inputStream != null) {
inputStream.close();
}
if (output != null) {
output.close();
}
}
}
}
);
/**
* 图文上传
*/
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
if (file == null) {
ToastUtil.showToast("先下载图片在试着上传吧");
return;
}
tvshow.setText("");
MultipartBuilder mb = new MultipartBuilder();
mb.type(MultipartBuilder.FORM);
mb.addFormDataPart("user_id", "74");
mb.addFormDataPart("user_head", "user_head.jpg", RequestBody.create(MEDIA_TYPE_PNG, file));
RequestBody requestBody = mb.build();
UIProgressRequestListener progressListener = new UIProgressRequestListener() {
@Override
public void onUIRequestProgress(long bytesWrite, long contentLength, boolean done) {
Log.e("TAG", "bytesWrite:" + bytesWrite);
Log.e("TAG", "contentLength" + contentLength);
Log.e("TAG", (100 * bytesWrite) / contentLength + " % done ");
Log.e("TAG", "done:" + done);
Log.e("TAG", "================================");
tvshow.setText("上传进度 " + ((100 * bytesWrite) / contentLength) + "%");
}
};
Request request = new Request.Builder()
.url("上传的接口")
.post(new ProgressRequestBody(requestBody, progressListener))
.tag(this)
.build();
OkHttpUtil.postData2Server(this, request, new OkHttpUtil.MyCallBack() {
@Override
public void onFailure(Request request, IOException e) {
}
@Override
public void onResponse(String json) {
}
});
//数据的显示
private void showData(String json) {
tvshow.setText("");
tvshow.setText(json);
}
以上就是四个例子的使用方法
关于Json的上传和cookie的使用在OkHttpUtil的注释里面有介绍
因为我写demo的效果都是用的公司的接口 所以上面把接口都给取消 需要的自己把接口加上去才能使用
demo上传到http://download.youkuaiyun.com/detail/liubo080852/9094387
有需要的可以下载哈