您的位置:首页 > 理论基础 > 计算机网络

Android 的OkHttp 网络请求框架的学习封装

2016-10-28 09:42 549 查看
自述:在此以前,自己从来没有写过博客,今天是第一次写,真心是有点小激动,正要下笔却不知道应该从何说起,若是写的不好,请各位见谅吧!关于网络请求,我自认为自己是一个菜鸟,没有任何经验,之前做的项目都是别人封装好,告诉我怎么调用就好了。至于怎么封装逻辑的,真是一窍不通,可近来比较闲,就学习封装了一下OkHttp 。

OkHttp 分为同步和异步请求;请求方式常用的有 get和post两种方式,封装请求的大致步骤为:

1、首先 创建 一个mOkHttpClient = new OkHttpClient()对象;

2、构建Request请求对象(根据get和post不同的请求方式分别创建);

3、如果是 post请求还需要 构建 请求参数 Params,RequestBody requestBody = buildFormData(params);  builder.post(requestBody).build;;

4、进行网络异步请求 mOkHttpClient.newCall(request).enqueue(new Callback() {} ),如果是同步请求,则改为 Response response = mOkHttpClient.newCall(request).execute()进行 


具体实现就不细说了,直接上代码如下:

public class OkHttpManager {

private static OkHttpManager mOkHttpManager;

private OkHttpClient mOkHttpClient;

private Gson mGson;

private Handler handler;

private OkHttpManager() {
mOkHttpClient = new OkHttpClient();
mOkHttpClient.newBuilder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS);
mGson = new Gson();
handler = new Handler(Looper.getMainLooper());
}

//创建 单例模式(OkHttp官方建议如此操作)
public static OkHttpManager getInstance() {
if (mOkHttpManager == null) {
mOkHttpManager = new OkHttpManager();
}
return mOkHttpManager;
}

/***********************
* 对外公布的可调方法
************************/

public void getRequest(String url, final BaseCallBack callBack) {
Request request = buildRequest(url, null, HttpMethodType.GET);
doRequest(request, callBack);
}

public void postRequest(String url, final BaseCallBack callBack, Map<String, String> params) {
Request request = buildRequest(url, params, HttpMethodType.POST);
doRequest(request, callBack);
}

public void postUploadSingleImage(String url, final BaseCallBack callback, File file, String fileKey, Map<String, String> params) {
Param[] paramsArr = fromMapToParams(params);

try {
postAsyn(url, callback, file, fileKey, paramsArr);
} catch (IOException e) {
e.printStackTrace();
}

}

public void postUploadMoreImages(String url, final BaseCallBack callback, File[] files, String[] fileKeys, Map<String, String> params) {
Param[] paramsArr = fromMapToParams(params);

try {
postAsyn(url, callback, files, fileKeys, paramsArr);
} catch (IOException e) {
e.printStackTrace();
}

}

/***********************
* 对内方法
************************/
//单个文件上传请求 不带参数
private void postAsyn(String url, BaseCallBack callback, File file, String fileKey) throws IOException {
Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, null);
doRequest(request, callback);
}

//单个文件上传请求 带参数
private void postAsyn(String url, BaseCallBack callback, File file, String fileKey, Param... params) throws IOException {
Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, params);
doRequest(request, callback);
}

//多个文件上传请求 带参数
private void postAsyn(String url, BaseCallBack callback, File[] files, String[] fileKeys, Param... params) throws IOException {
Request request = buildMultipartFormRequest(url, files, fileKeys, params);
doRequest(request, callback);
}

//异步下载文件
public void asynDownloadFile(final String url, final String destFileDir, final BaseCallBack callBack) {
final Request request = buildRequest(url, null, HttpMethodType.GET);
callBack.OnRequestBefore(request); //提示加载框
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
callBack.onFailure(call, e);
}

@Override
public void onResponse(Call call, Response response) throws IOException {
// callBack.onResponse(response);

InputStream is = null;
byte[] buf = new byte[1024*2];
final long fileLength = response.body().contentLength();
int len = 0;
long readLength = 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);
readLength += len;
int curProgress = (int) (((float) readLength / fileLength) * 100);
Log.e("lgz", "onResponse: >>>>>>>>>>>>>" + curProgress + ", readLength = " + readLength + ", fileLength = " + fileLength);
callBack.inProgress(curProgress, fileLength, 0);
}
fos.flush();
//如果下载文件成功,第一个参数为文件的绝对路径
callBackSuccess(callBack, call, response, file.getAbsolutePath());
} catch (IOException e) {
callBackError(callBack, call, response.code());
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
try {
if (fos != null)
fos.close();
} catch (IOException e) {
}
}
}
});

}

//构造上传图片 Request
private Request buildMultipartFormRequest(String url, File[] files, String[] fileKeys, Param[] params) {
params = validateParam(params);
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
for (Param param : params) {
builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + param.key + "\""),
RequestBody.create(MediaType.parse("image/png"), 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();
}

//Activity页面所有的请求以Activity对象作为tag,可以在onDestory()里面统一取消,this
public void cancelTag(Object tag) {
for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {
if (tag.equals(call.request().tag())) {
call.cancel();
}
}
for (Call call : mOkHttpClient.dispatcher().runningCalls()) {
if (tag.equals(call.request().tag())) {
call.cancel();
}
}
}

private String guessMimeType(String path) {
FileNameMap fileNameMap = URLConnection.getFileNameMap();
String contentTypeFor = fileNameMap.getContentTypeFor(path);
if (contentTypeFor == null) {
contentTypeFor = "application/octet-stream";
}
return contentTypeFor;
}

private String getFileName(String path) {
int separatorIndex = path.lastIndexOf("/");
return (separatorIndex < 0) ? path : path.substring(separatorIndex + 1, path.length());
}

private Param[] fromMapToParams(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 void doRequest(Request request, final BaseCallBack callBack) {
callBack.OnRequestBefore(request);
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
callBack.onFailure(call, e);
}

@Override
public void onResponse(Call call, Response response) throws IOException {
callBack.onResponse(response);
String result = response.body().string();
if (response.isSuccessful()) {

if (callBack.mType == String.class) {
// callBack.onSuccess(call, response, result);
callBackSuccess(callBack, call, response, result);
} else {
try {
Object object = mGson.fromJson(result, callBack.mType);//自动转化为 泛型对象
// callBack.onSuccess(call, response, object);
callBackSuccess(callBack, call, response, object);
} catch (JsonParseException e) {
//json解析错误时调用
callBack.onEror(call, response.code(), e);
}

}
} else {
callBack.onEror(call, response.code(), null);
}

}

});

}

//创建 Request对象
private Request buildRequest(String url, Map<String, String> params, HttpMethodType methodType) {

Request.Builder builder = new Request.Builder();
builder.url(url);
if (methodType == HttpMethodType.GET) {
builder.get();
} else if (methodType == HttpMethodType.POST) {
RequestBody requestBody = buildFormData(params);
builder.post(requestBody);
}
return builder.build();
}

//构建请求所需的参数表单
private RequestBody buildFormData(Map<String, String> params) {
FormBody.Builder builder = new FormBody.Builder();
builder.add("platform", "android");
builder.add("version", "1.0");
builder.add("key", "123456");
if (params != null) {
for (Map.Entry<String, String> entry : params.entrySet()) {
builder.add(entry.getKey(), entry.getValue());
}
}
return builder.build();
}

private void callBackSuccess(final BaseCallBack callBack, final Call call, final Response response, final Object object) {
handler.post(new Runnable() {
@Override
public void run() {
callBack.onSuccess(call, response, object);
}
});

}

private void callBackError(final BaseCallBack callBack, final Call call, final int code) {
handler.post(new Runnable() {
@Override
public void run() {
callBack.onEror(call, code, null);
}
});

}

private Param[] validateParam(Param[] params) {
if (params == null)
return new Param[0];
else
return params;
}

public static class Param {
public Param() {
}

public Param(String key, String value) {
this.key = key;
this.value = value;
}

String key;
String value;
}

enum HttpMethodType {
GET, POST
}

} 其中的 BaseCallBack回调机制封装如下:
public abstract class BaseCallBack<T> {
public Type mType;

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 BaseCallBack() {
mType = getSuperclassTypeParameter(getClass());
}

protected abstract void OnRequestBefore(Request request);

protected abstract void onFailure(Call call, IOException e);

protected abstract void onSuccess(Call call, Response response, T t);

protected abstract void onResponse(Response response);

protected abstract void onEror(Call call, int statusCode, Exception e);

protected abstract void inProgress(int progress, long total , int id);
}
上面这个类OkHttpManager 是我根据网络上各家资源学习封装好的,copy进代码可以直接使用,并且根据okhttp3.0以后的版本,对之前的一下请求参数设置进行了最新的修改,具体如下:

1、设置请求超时参数;  
  

okhttp3.0以前的版本是这样设置的 

new OkHttpClient(); 

mHttpClient.setConnectTimeout(10, TimeUnit.SECONDS);

mHttpClient.setReadTimeout(10,TimeUnit.SECONDS);

  mHttpClient.setWriteTimeout(30,TimeUnit.SECONDS);

之后的版本是这样设置的:

new OkHttpClient.Builder()  

         .readTimeout(READ_TIMEOUT,TimeUnit.SECONDS)//设置读取超时时间  

         .writeTimeout(WRITE_TIMEOUT,TimeUnit.SECONDS)//设置写的超时时间  

          .connectTimeout(CONNECT_TIMEOUT,TimeUnit.SECONDS)//设置连接超时时间 

2、post方式请求时,构建表单对象参数;

okhttp3.0以前的版本是这样构建的:new  FormEncodingBuilder()对象,然后向里面add (key,value)参数;

之后的版本更改为:FormBody body = new FormBody.Builder(),.add(key, value);即是FormEncodingBuilder已被FormBody取代;

至于BaseCallBack类,根据请求数据的功能的不同,还需要对此进行封装,集成自己需要的方法实现;

一、进行一般的数据加载请求,可直接调用如下:

模拟用户登录:

Map<String, String> params = new HashMap<String, String>();
params.put("Mobile", username.getText().toString());
params.put("PassWord", password.getText().toString());

OkHttpManager.getInstance().postRequest(Constants.LOGIN_URL, new LoadCallBack<String>(getActivity()) {
@Override
protected void onSuccess(Call call, Response response, String s) {
Log.e("lgz", "onSuccess = " + s);
Toast.makeText(getActivity(), "登录成功!", Toast.LENGTH_LONG).show();
}

@Override
protected void onEror(Call call, int statusCode, Exception e) {
Log.e("lgz", "Exception = " + e.toString());
}
}
, params);
上面登录请求中 就用到了自己根据需要再次封装的Callback类的继承实现,LoadCallBack<T>类:
//添加对请求时对话框的处理
public abstract class LoadCallBack<T> extends BaseCallBack<T> {
private Context context;
private SpotsDialog spotsDialog;

public LoadCallBack(Context context) {
this.context = context;
spotsDialog = new SpotsDialog(context);
}

private void showDialog() {
spotsDialog.show();
}

private void hideDialog() {
if (spotsDialog != null) {
spotsDialog.dismiss();
}
}

public void setMsg(String str) {
spotsDialog.setMessage(str);
}

public void setMsg(int resId) {
spotsDialog.setMessage(context.getString(resId));
}

@Override
protected void OnRequestBefore(Request request) {
showDialog();

}

@Override
protected void onFailure(Call call, IOException e) {
hideDialog();
}

@Override
protected void onResponse(Response response) {
hideDialog();
}

@Override
protected void inProgress(int progress, long total, int id) {

}其实这个类就是对BaseCallBack再次继承实现;
二、下载文件,并显示进度条对话框的请求操作:

下载一张图片:

OkHttpManager.getInstance().asynDownloadFile("http://www.7mlzg.com/uploads/bwf_1477419976.jpg", FILE_PATH, new FileCallBack<String>(getActivity()) {
@Override
protected void onResponse(Response response) {

}

@Override
protected void onSuccess(Call call, Response response, String s) {
super.onSuccess(call, response, s);
Log.e("lgz", "status = : " + s);
Toast.makeText(getActivity(), "下载成功", Toast.LENGTH_LONG).show();
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.fromFile(new File(s));//广播通知系统图集更新
intent.setData(uri);
getActivity().sendBroadcast(intent);
}
});

上面下载图片中,就用到了自己根据需要再次继承Callback类封装得到的FileCallBack<T>类如下:
public abstract class FileCallBack<T> extends BaseCallBack<T> {

private Context mContext;

private ProgressDialog mProgressDialog;

public FileCallBack(Context context) {
mContext = context;
initDialog();
}

private void initDialog(){
mProgressDialog = new ProgressDialog(mContext);
mProgressDialog.setTitle("下载中...");
mProgressDialog.setCanceledOnTouchOutside(true);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setMax(100);
}

private void hideDialog() {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
}

@Override
protected void OnRequestBefore(Request request) {
mProgressDialog.show();
}

@Override
protected void onFailure(Call call, IOException e) {
hideDialog();
}

@Override
protected void onSuccess(Call call, Response response, T t) {
Log.e("lgz", "onSuccess: >>>>>>>>>>>>>");
hideDialog();
}

@Override
protected void onEror(Call call, int statusCode, Exception e) {
hideDialog();
}

@Override
protected void inProgress(int progress, long total, int id) {
Log.e("lgz", "inProgress: >>>>>>>>>>>>>"+progress);
mProgressDialog.setProgress(progress);

}
}

三、最后说一下使用okhttp的配置:
在Android Studio 中,直接在build.gradle文件里配置 :compile 'com.squareup.okhttp3:okhttp:3.4.1'

在Eclipse里需要导入jar包使用,下载最新jar;

当然了,这里只是对Okhttp常用的一些功能进行了封装处理,使用的都是异步请求方式,至于同步操作,我个人觉得不是很常用,使用时需要开启一个线程,不然会阻塞UI线程的;最后,这只是一个简单的学习,其中要是有不足和错误,还希望大家留言批评指正,谢谢!

                                    
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: