网络框架-okhttp
来源:互联网 发布:js prompt() 循环 编辑:程序博客网 时间:2024/06/05 18:45
okhttp是什么?简而言之就是一款优秀的网络框架。
能实现的功能?Get、Post请求,文件上传和下载等等….
从基本功能的调用,看看一篇关于okhttp的封装:
// http Get操作OkHttpClient client = new OkHttpClient();String run(String url) throws IOException { Request request = new Request.Builder() .url(url) .build(); Response response = client.newCall(request).execute(); return response.body().string();}
//http Post提交Json数据public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");OkHttpClient client = new OkHttpClient();String post(String url, String json) throws IOException { RequestBody body = RequestBody.create(JSON, json); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).execute(); return response.body().string();}
由于okhttp要在子线程中进行操作,所以要对其进行封装,在github搜索okhttp回看到很多封装库,一个事鸿洋的okhttputils,另一个叫okhttp-OkGo,两个star都在3000以上以上了,可见都很好,可以看一下,依赖指定的jar包,或着clone下来,代码里可以看到几种功能的调用。
先看,前两天我看到的一个okhttp的封装工具:
/** * Description : OkHttp网络连接封装工具类 * Author : lauren * Email : lauren.liuling@gmail.com * Blog : http://www.liuling123.com * Date : 15/12/17 */public class OkHttpUtils { private static final String TAG = "OkHttpUtils"; private static OkHttpUtils mInstance; private OkHttpClient mOkHttpClient; private Handler mDelivery; private OkHttpUtils() { mOkHttpClient = new OkHttpClient(); mOkHttpClient.setConnectTimeout(10, TimeUnit.SECONDS); mOkHttpClient.setWriteTimeout(10, TimeUnit.SECONDS); mOkHttpClient.setReadTimeout(30, TimeUnit.SECONDS); //cookie enabled mOkHttpClient.setCookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)); mDelivery = new Handler(Looper.getMainLooper()); } private synchronized static OkHttpUtils getmInstance() { if (mInstance == null) { mInstance = new OkHttpUtils(); } return mInstance; } private void getRequest(String url, final ResultCallback callback) { final Request request = new Request.Builder().url(url).build(); deliveryResult(callback, request); } private void postRequest(String url, final ResultCallback callback, List<Param> params) { Request request = buildPostRequest(url, params); deliveryResult(callback, request); } private void deliveryResult(final ResultCallback callback, Request request) { mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Request request, final IOException e) { sendFailCallback(callback, e); } @Override public void onResponse(Response response) throws IOException { try { String str = response.body().string(); if (callback.mType == String.class) { sendSuccessCallBack(callback, str); } else { Object object = JsonUtils.deserialize(str, callback.mType); sendSuccessCallBack(callback, object); } } catch (final Exception e) { LogUtils.e(TAG, "convert json failure", e); sendFailCallback(callback, e); } } }); } private void sendFailCallback(final ResultCallback callback, final Exception e) { mDelivery.post(new Runnable() { @Override public void run() { if (callback != null) { callback.onFailure(e); } } }); } private void sendSuccessCallBack(final ResultCallback callback, final Object obj) { mDelivery.post(new Runnable() { @Override public void run() { if (callback != null) { callback.onSuccess(obj); } } }); } private Request buildPostRequest(String url, List<Param> params) { 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(); } /**********************对外接口************************/ /** * get请求 * @param url 请求url * @param callback 请求回调 */ public static void get(String url, ResultCallback callback) { getmInstance().getRequest(url, callback); } /** * post请求 * @param url 请求url * @param callback 请求回调 * @param params 请求参数 */ public static void post(String url, final ResultCallback callback, List<Param> params) { getmInstance().postRequest(url, callback, params); } /** * http请求回调类,回调方法在UI线程中执行 * @param <T> */ 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]); } /** * 请求成功回调 * @param response */ public abstract void onSuccess(T response); /** * 请求失败回调 * @param e */ public abstract void onFailure(Exception e); } /** * post请求参数类 */ public static class Param { String key; String value; public Param() { } public Param(String key, String value) { this.key = key; this.value = value; } }}
如果不靠工具类,自己去过一遍,因为要在子线程中调用,可能会new一个Thread,启动一个线程,或着来一个asynctask,也方便了更新ui与主线程交互handler了,代码就不写了,刚刚说的很容易实现。由于okhttp提供了一个enqueue这个异步方法,因此省略了,start一个Thread的操作。网络请求成功后,由非主线程–>主线程,需要一个handler.sendMessage()或着handler.post(new Runnable()),runOnUiThread();不在acitivity里或着fragment中,还需要一个接口的监听去返回成功和失败两个方法。成功返回的是结果的值,实体类、String字符串等等。(不看代码我可能还想不出来那么多,哈哈,还是直接分析代码)
private OkHttpUtils() { //okhttp初始化,配置信息,同时创建一个handler对象 mOkHttpClient = new OkHttpClient(); mOkHttpClient.setConnectTimeout(10, TimeUnit.SECONDS); mOkHttpClient.setWriteTimeout(10, TimeUnit.SECONDS); mOkHttpClient.setReadTimeout(30, TimeUnit.SECONDS); //cookie enabled mOkHttpClient.setCookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER)); mDelivery = new Handler(Looper.getMainLooper()); }
//单例模式,但这样些不太好。 private synchronized static OkHttpUtils getmInstance() { if (mInstance == null) { mInstance = new OkHttpUtils(); } return mInstance; } //另一种方式的单例模式 private static OkHttpUtils mInstance=null; private static OkHttpUtils getmInstance(){ if(mInstance==null){ synchronized(OkHttpUtils.class){ if(mInstance==null){ mInstance=new OkHttpUtils(); } } }return mInstance;}
Get请求:
/** * get请求 * @param url 请求url * @param callback 请求回调 */ //get方法的调用,在里面对get操作进一步封装,同时指定一个回调 public static void get(String url, ResultCallback callback) { getmInstance().getRequest(url, callback); }
//拼接得到request对象,提交网络操作在方法deliveryResult中进行 private void getRequest(String url, final ResultCallback callback) { final Request request = new Request.Builder().url(url).build(); deliveryResult(callback, request); }
//实现真正的提交网络操作,在异步中进行enqueue(params)private void deliveryResult(final ResultCallback callback, Request request) { mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Request request, final IOException e) { //请求失败,回调自定义接口失败的方法。 sendFailCallback(callback, e); } @Override public void onResponse(Response response) throws IOException { try { String str = response.body().string(); if (callback.mType == String.class) { sendSuccessCallBack(callback, str); } else { Object object = JsonUtils.deserialize(str, callback.mType); //请求成功,回调自定义接口成功的方法。 sendSuccessCallBack(callback, object); } } catch (final Exception e) { LogUtils.e(TAG, "convert json failure", e); sendFailCallback(callback, e); } } }); }
//对应上面两个回调方法,用handler.post实现与主线程数据交互 private void sendFailCallback(final ResultCallback callback, final Exception e) { mDelivery.post(new Runnable() { @Override public void run() { if (callback != null) { callback.onFailure(e); } } }); } private void sendSuccessCallBack(final ResultCallback callback, final Object obj) { mDelivery.post(new Runnable() { @Override public void run() { if (callback != null) { callback.onSuccess(obj); } } }); }
看上面代码中的callback这个参数,是在deliveryResult方法中传递的参数,需要我们调用get或者post时,去生成一个callback,看看这个类:
//抽象类,封装了两个方法,当要拿到这个类的引用时,需要复写这两个方法,看下面调用 /** * http请求回调类,回调方法在UI线程中执行 * @param <T> */ 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]); } /** * 请求成功回调 * @param response */ public abstract void onSuccess(T response); /** * 请求失败回调 * @param e */ public abstract void onFailure(Exception e); }
//主线程中调用okhttp工具类进行网络请求//这里就是上面两个方法的回调,使用handler后变为在主线程中 OkHttpUtils.ResultCallback<String> loadNewsCallback = new OkHttpUtils.ResultCallback<String>() { @Override public void onSuccess(String response) { List<NewsBean> newsBeanList = NewsJsonUtils.readJsonNewsBeans(response, getID(type)); listener.onSuccess(newsBeanList); } @Override public void onFailure(Exception e) { listener.onFailure("load news list failure.", e); } };
Post请求:
和Get请求不同的在于requertBody的不同,Post请求要提交一些数据参数之类,另外Post使用起来更安全。
/** * post请求 * @param url 请求url * @param callback 请求回调 * @param params 请求参数 */ //和Get请求一样,去进一步封装 public static void post(String url, final ResultCallback callback, List<Param> params) { getmInstance().postRequest(url, callback, params); }
//与get不同的是,多了一个List的参数,这里是用来传递拼接的字段。 private void postRequest(String url, final ResultCallback callback, List<Param> params) { //调用buildPostRequest方法,去拼接 Request request = buildPostRequest(url, params); //执行网络操作 deliveryResult(callback, request); }
//遍历list集合,使用FormEncodingBuilder去add键值对private Request buildPostRequest(String url, List<Param> params) { 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(); }
/** * post请求参数类 */ //List集合中的参数类 public static class Param { String key; String value; public Param() { } public Param(String key, String value) { this.key = key; this.value = value; } }
下面和Get操作一样了,执行网络操作,区别就在于上面的requestbody的不同。同样的调用方法:deliveryResult()
private void deliveryResult(final ResultCallback callback, Request request) { mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Request request, final IOException e) { sendFailCallback(callback, e); } @Override public void onResponse(Response response) throws IOException { try { String str = response.body().string(); if (callback.mType == String.class) { sendSuccessCallBack(callback, str); } else { Object object = JsonUtils.deserialize(str, callback.mType); sendSuccessCallBack(callback, object); } } catch (final Exception e) { LogUtils.e(TAG, "convert json failure", e); sendFailCallback(callback, e); } } }); }
实现文件下载和上传:
依赖一个jar包工具类:详细信息点击看这里:https://github.com/jeasonlzy/okhttp-OkGo
compile 'com.lzy.net:okgo:+' //版本号使用 + 可以自动引用最新版compile 'com.lzy.widget:imagepicker:0.3.2' //这个是一个图片选择器工具类compile 'com.github.bumptech.glide:glide:3.6.1'
仿微信图片选择imagepicker:https://github.com/jeasonlzy/ImagePicker
文件下载:
/** * 文件下载 * * @param view */ public void download(View view) { OkGo.get(Urls.URL_DOWNLOAD) .tag(this) .headers("header1", "headerValue1") .params("paramq", "paramValue1") .execute(new FileCallback() { @Override public void onBefore(BaseRequest request) { super.onBefore(request); bt_download.setText("下载中...."); } @Override public void onSuccess(File file, Call call, Response response) { bt_download.setText("下载成功"); tv_download_speed.setText("--"); } @Override public void onError(Call call, Response response, Exception e) { super.onError(call, response, e); bt_download.setText("下载失败"); tv_download_speed.setText("--"); } @Override public void downloadProgress(long currentSize, long totalSize, float progress, long networkSpeed) { super.downloadProgress(currentSize, totalSize, progress, networkSpeed); String downloadLength = Formatter.formatFileSize(getApplicationContext(), currentSize); String totalLength = Formatter.formatFileSize(getApplicationContext(), totalSize); tv_current_size.setText(downloadLength); tv_total_size.setText(totalLength); tv_current_progress.setText((int) (progress * 100) + "%"); String netSpeed = Formatter.formatFileSize(getApplicationContext(), networkSpeed); tv_download_speed.setText(netSpeed + "/S"); load_progressbar.setProgress((int) (progress * 100)); } }); }
可以设置多个头部header参数和params参数信息,这里是任意设置的,可以自定义callback,实现依赖jar包中的AbsCallback。AbsCallback里有几个回调方法:代码如下:
public abstract class AbsCallback<T> implements Converter<T> { /** 请求网络开始前,UI线程 */ public void onBefore(BaseRequest request) { } /** 对返回数据进行操作的回调, UI线程 */ public abstract void onSuccess(T t, Call call, Response response); /** 缓存成功的回调,UI线程 */ public void onCacheSuccess(T t, Call call) { } /** 请求失败,响应错误,数据解析错误等,都会回调该方法, UI线程 */ public void onError(Call call, Response response, Exception e) { } /** 缓存失败的回调,UI线程 */ public void onCacheError(Call call, Exception e) { } /** 网络失败结束之前的回调 */ public void parseError(Call call, Exception e) { } /** 请求网络结束后,UI线程 */ public void onAfter(T t, Exception e) { if (e != null) e.printStackTrace(); } /** * Post执行上传过程中的进度回调,get请求不回调,UI线程 * * @param currentSize 当前上传的字节数 * @param totalSize 总共需要上传的字节数 * @param progress 当前上传的进度 * @param networkSpeed 当前上传的速度 字节/秒 */ public void upProgress(long currentSize, long totalSize, float progress, long networkSpeed) { } /** * 执行下载过程中的进度回调,UI线程 * * @param currentSize 当前下载的字节数 * @param totalSize 总共需要下载的字节数 * @param progress 当前下载的进度 * @param networkSpeed 当前下载的速度 字节/秒 */ public void downloadProgress(long currentSize, long totalSize, float progress, long networkSpeed) { }}
这里FileCallback的代码如下:
public abstract class FileCallback extends AbsCallback<File> { private FileConvert convert; //文件转换类 public FileCallback() { this(null); } public FileCallback(String destFileName) { this(null, destFileName); } public FileCallback(String destFileDir, String destFileName) { convert = new FileConvert(destFileDir, destFileName); convert.setCallback(this); } @Override public File convertSuccess(Response response) throws Exception { File file = convert.convertSuccess(response); response.close(); return file; }}
上传代码如下:
/** * 文件上传 * * @param view */ public void upload(View view) { List<File> files = new ArrayList<>(); if (imageItems != null && imageItems.size() > 0) { for (ImageItem imageItem : imageItems) { files.add(new File(imageItem.path)); } } try { OkGo.post(Urls.URL_FORM_UPLOAD) .headers("header1", "heardvalue1") .headers("header2", "heardvalue2") .headers("header3", "heardvalue3") //看服务器那边要求去拼接了。 .params("params1", "value1") .params("params2", "value2") .params("params3", "value3") .addFileParams("file", files) //传个集合,一个key,对应一个集合文件上传。 // .params("file1",new File("imagePath1")) // .params("file2",new File("imagePath2")) // .params("file3",new File("imagePath3")) .execute(new StringCallback() { @Override public void onBefore(BaseRequest request) { super.onBefore(request); bt_upload.setText("正在上传中...."); } @Override public void onSuccess(String s, Call call, Response response) { bt_upload.setText("上传成功"); upload_speed.setText("--"); } @Override public void onError(Call call, Response response, Exception e) { super.onError(call, response, e); bt_upload.setText("上传失败"); upload_speed.setText("--"); } @Override public void upProgress(long currentSize, long totalSize, float progress, long networkSpeed) { super.upProgress(currentSize, totalSize, progress, networkSpeed); String uploadSize=Formatter.formatFileSize(MainActivity.this,currentSize); String totalSizeNum=Formatter.formatFileSize(MainActivity.this,totalSize); upload_current_progress.setText(uploadSize+"/"+totalSizeNum); upload_percentage.setText((int) (progress * 100) + "%"); String netSpeed = Formatter.formatFileSize(getApplicationContext(), networkSpeed); upload_speed.setText(netSpeed); upload_progressbar.setProgress((int) (progress * 100)); } }); } catch (Exception e) { e.printStackTrace(); } }
这块和下载逻辑差不错,headersr和params都可以有多对,在上传时,服务端都会要求拼接许多header和一些params键值对,代码那里有标注,可以直接传一个list集合,里面放图片路径就可以了。图片的选择是跳到相册页,这里就用到了刚刚依赖的imagePicker
跳转到相册代码:
/** * 去相册选择图片 * * @param view */ public void choose(View view) { Intent intent = new Intent(this, ImageGridActivity.class); startActivityForResult(intent, IMAGE_PICKER); }
回调代码:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == IMAGE_PICKER && resultCode == ImagePicker.RESULT_CODE_ITEMS) { if (data != null) { StringBuilder sb = new StringBuilder(); imageItems = (ArrayList<ImageItem>) data.getSerializableExtra(ImagePicker.EXTRA_RESULT_ITEMS); for (int i = 0; i < imageItems.size(); i++) { if (i == imageItems.size() - 1) { sb.append("图片" +(i+1) + ":" + imageItems.get(i).path); } else { sb.append("图片" + (i+1) + ":" + imageItems.get(i).path + ","); } } tv_image_path.setText(sb.toString()); } } }
这里需要注意的是,依赖的imagepicker需要配置初始化,不清楚点击看这里:
还可以按照我这个demo里的配置来,当然我也是按照它这里配置的:
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); ImagePicker imagePicker = ImagePicker.getInstance(); imagePicker.setImageLoader(new GlideImageLoad()); //设置图片加载器 imagePicker.setShowCamera(true); //显示拍照按钮 imagePicker.setCrop(true); //允许裁剪(单选才有效) imagePicker.setSaveRectangle(true); //是否按矩形区域保存 imagePicker.setSelectLimit(9); //选中数量限制 imagePicker.setStyle(CropImageView.Style.RECTANGLE); //裁剪框的形状 imagePicker.setFocusWidth(800); //裁剪框的宽度。单位像素(圆形自动取宽高最小值) imagePicker.setFocusHeight(800); //裁剪框的高度。单位像素(圆形自动取宽高最小值) imagePicker.setOutPutX(1000);//保存文件的宽度。单位像素 imagePicker.setOutPutY(1000);//保存文件的高度。单位像素 }}
GlideImageLoad代码:
@Override public void displayImage(Activity activity, String path, ImageView imageView, int width, int height) { //Glide的配置去加载图片 Glide.with(activity.getApplicationContext()).load(path) .placeholder(R.mipmap.default_image) .error(R.mipmap.default_image) .override(width,height) .into(imageView); } @Override public void clearMemoryCache() { //这里去清除缓存 }
这样配置就完成了,最后是xml文件:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="2dp" tools:context="user.example.com.okgosample.acitivity.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="10dp" > <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_weight="1" > <TextView android:id="@+id/tv_current_size" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="-" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="/" /> <TextView android:id="@+id/tv_total_size" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="-M" /> </LinearLayout> <TextView android:id="@+id/tv_current_progress" android:layout_width="0dp" android:layout_height="wrap_content" android:text="--%" android:layout_weight="1" /> <TextView android:id="@+id/tv_download_speed" android:layout_width="0dp" android:layout_height="wrap_content" android:text="kb/s" android:layout_weight="1" /> </LinearLayout> <ProgressBar android:id="@+id/load_progressbar" android:layout_width="match_parent" android:layout_height="wrap_content" style="?android:attr/progressBarStyleHorizontal" android:max="100" android:progress="0" android:visibility="visible" /> <Button android:id="@+id/bt_download" android:onClick="download" android:text="down_load" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ProgressBar android:id="@+id/upload_progressbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" style="?android:attr/progressBarStyleHorizontal" android:max="100" android:progress="0" android:visibility="visible" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="10dp"> <TextView android:id="@+id/upload_current_progress" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="-/M" /> <TextView android:id="@+id/upload_percentage" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="-%" /> <TextView android:id="@+id/upload_speed" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="kb/s" /> </LinearLayout> <Button android:id="@+id/bt_choose_images" android:onClick="choose" android:text="选择图片" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/bt_upload" android:onClick="upload" android:text="up_down" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_image_path" android:layout_width="wrap_content" android:layout_height="wrap_content" /></LinearLayout>
最后再来张效果图吧,效果还不错: (这里是实现3张图片上传,和下载一个文件)
结论: okGo封装类中提供了许多强大的很实用的功能,具体需要哪块就去实现那块吧,demo就不上传了,可以把okGo源码clone下来,里面还是很详细。有问题就拍砖,多多指教~~
- okhttp网络请求框架
- 网络请求框架OKHttp
- OkHttp网络请求框架
- 网络请求okhttp框架
- 网络框架-retrofit,okhttp
- 网络框架-okhttp
- OkHttp网络请求框架
- okhttp(网络框架)
- 网络请求 框架okhttp
- okhttp,Android网络框架学习之OKHttp
- Android 网络框架OKHttp学习
- Retrofit+okhttp网络框架介绍
- android 网络框架解析--OKHttp
- okhttp网络框架的使用
- Retrofit+okhttp网络框架介绍
- Android网络框架-OkHttp使用
- Android网络框架-OkHttp使用
- Android网络框架OKHttp初解
- 技术之美[程序人生]我是怎么招聘程序员的
- Live mode support -- running user data on a ramdisk (tmpfs) is broken in Android 7.0. Need a fix.
- 求两个整数的最大公约数
- L1-008. 求整数段和
- 2017年ZJUT校赛-Problem A: 画图游戏——博弈论
- 网络框架-okhttp
- com.android.internal.policy.DecorContext
- Unity Editor 基础篇(四):Handles
- LintCode 两两交换链表中的节点
- Codeforces #297 (Div. 2) A. Vitaliy and Pie (水
- hid鼠标绝对坐标报告
- NSThread
- CodeForces 124C【连通块】
- 解决github网站 git push或者git clone代码速度太慢的方法