OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据
OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据
我们这片博文就来聊聊这个反响很不错的OkHttp了,标题是我恶搞的,本篇将着重详细的分析,探索OkHttp这个框架的使用和封装
一.追其原理
Android系统提供了两种HTTP通信类
- HttpURLConnection
- HttpClient
Google推荐使用HttpURLConnection,这个没必要多说,事实上,我这篇写的应该算是比较晚了,很多优秀的博文都已经提出了这些观点了,那我也就不好意思重复的说废话了,不过我还是得吐槽一下,HttpURLConnection是在是太难用了,而且功能也实在是太少了,虽然Github上封装的框架还真是不少,不过依然不是特别好用,而Google自己也在寻求解决的办法,如果你看过android4.4的源码,你就应该知道,Google把HttpURLConnection替换成了OkHttp,而OkHttp走到现在,已经是相对来说,比较成熟的框架了,那我们为何不去使用它呢?而且现在学习OkHttp的资料和文章实在是太多了,根本不需要什么学习成本的,搜索一下,马上就有一大堆,既然如此,我们今儿个就来看看这个花姑凉长什么样吧!各位小司机,跟着老司机一起上车吧!
注意,我们使用的IDE是Android Studio
二.使用准备
肯定要配置一下啦,我们首先新建一个工程——OkHttpGo,这名字好听,我就不加demo或者test了,这样显得有点low,项目我们基于5.0 Lollipop来开发
而关于OkHttp的官方介绍,大家可以移步这里
- http://square.github.io/okhttp/
如果想看源码,可以去Github上
- https://github.com/square/okhttp
我们既然要使用,就根据github上来吧,加入依赖,把依赖添加到build.gradle中,当然,他是提供jar的,你如果用Eclipse获取喜欢用jar,你也可以直接下载jar
compile 'com.squareup.okhttp3:okhttp:3.3.1'
因为我们会用到图片解析,所以可以加上Picasso的依赖,关于它的介绍,可以移步
- http://square.github.io/picasso/
这个库,我下篇博文会介绍到,这里你只要知道是这么添加依赖和使用就可以了
compile 'com.squareup.picasso:picasso:2.5.2'
记住,网络的使用,是需要添加权限的哦!
<!--网络权限--> <uses-permission android:name="android.permission.INTERNET"/>
行,大致的配置就到这里OK了,如果你还有什么不清楚的,可以去他们官网或者github上瞧一瞧,看一看,这里再送上一下下载jar的地址吧!
- OkHttp 2.7.5 Jar下载
OkHttp内部依赖了Okio,这里也提供了jar下载地址
- Okio 1.8.0 jar下载
三.图片加载
我们先从图片加载说起,最起码先定义一下布局呀
<ImageView android:id="@+id/iv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher" /> <Button android:id="@+id/btn_iv" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="图片加载" />
非常简陋的一个布局,就一个button和一个imageview,现在我们就是点击按钮,然后解析显示在控件上,这对于OkHttp来说,应该是怎么使用的呢?注意,现在演示的,都还只是没有封装的前提下,我们首先做一些准备工作
//成功状态 private static final int SUCCESS_STATUS = 1; //失败状态 private static final int FAIL_STATUS = 2; //图片链接 private String url = "http://d.3987.com/qiz_141118/004.jpg";
这里我定义了两个常量,分别是解析成功失败的状态,又定义了一张图片的链接,图片是网上的以上美女图片,好的,这些都准备好了,现在我们就可以来书写OkHttp相关的类了
//图片解析 btn_iv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //初始化OkHttp OkHttpClient client = new OkHttpClient(); //构建Request,解析链接,这里可选get/post方法 final Request request = new Request.Builder().get().url(url).build(); //添加到请求队列 client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { //失败 Log.i(TAG, "解析失败"); } @Override public void onResponse(Call call, Response response) throws IOException { //成功 Message message = handler.obtainMessage(); //判断,成功就传值 if (response.isSuccessful()) { message.what = SUCCESS_STATUS; message.obj = response.body().bytes(); handler.sendMessage(message); } else { handler.sendEmptyMessage(FAIL_STATUS); } } }); } });
可以看到,它使用和Volley有点类似,个人感觉,其实也就是那么几个步骤,首先初始化,然后设置一下乱七八糟的属性,最后添加到队列中,返回两个回调,成功和失败,是吧,我们这个时候就直接用handler去发消息了
//子线程 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case SUCCESS_STATUS: //拿值 byte[] result = (byte[]) msg.obj; //图片加载 Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length); //设置图片 iv.setImageBitmap(bitmap); break; case FAIL_STATUS: //失败 Log.i(TAG, "解析失败"); break; } } };
得出来的效果,用一张图来表示就绰绰有余了
OK,图片解析的就已经实现了
四.图片裁剪
现在呢,我们可以看到是一个美女的图片,但是如果我们图片比较大,而我们不需要这么大,比如长这样?
如果我们不想要这么大的图片,又或者说,我们想要一张正方形一样整齐的图片,我们该怎么去做?还记得我们添加的picasso图片框架吗?他就可以做到,我们可以用它的功能写一个工具类来帮助我们裁剪,这个工具类写起来也没有多麻烦
package com.lgl.okhttpgo; import android.graphics.Bitmap; import com.squareup.picasso.Transformation; /** * 裁剪图片 * Created by LGL on 2016/6/19. */ public class TailorImageView implements Transformation { @Override public Bitmap transform(Bitmap source) { //得到原图片的大小,取最小值 int size = Math.min(source.getWidth(), source.getHeight()); //长大于宽,还是宽大于长 int x = (source.getWidth() - size) / 2; int y = (source.getHeight() - size) / 2; //创建新的bitmap Bitmap bitmap = Bitmap.createBitmap(source, x, y, size, size); if (bitmap != source) { //回收 source.recycle(); } return bitmap; } @Override public String key() { return "lgl"; } }
我们使用的话,就在我们解析成功的时候调用就可以了,
case SUCCESS_STATUS: //拿值 byte[] result = (byte[]) msg.obj; //图片加载 Bitmap bitmap = new TailorImageView().transform(BitmapFactory.decodeByteArray(result, 0, result.length)); //设置图片 iv.setImageBitmap(bitmap); break;
我们可以是这样的
正方形就搞定了,欧耶!
五.网络框架封装
事实上,我们上面所说的,都还是有些许繁杂了,毕竟我们不应该重复的去写这么多麻烦的代码,对吧,现在我们来对他进行一个封装,而对于OkHttp,他有以下的几个功能
- 一般的get请求
- 一般的post请求
- 基于Http的文件上传
- 文件下载
- 加载图片
- 支持请求回调,直接返回对象,对象集合
- 支持session的保持
我们要封装一个OkHttp的话,也就是围绕着他的这几个功能来二次开发了,好的,小司机们,我们继续开车吧!污污污污污…..
我们写一个OkHttpUtils,其实还算是比较简单的,因为我们实际上没写多少内容
package com.lgl.okhttpgo; import android.graphics.Bitmap; import android.os.Handler; import android.os.Looper; import android.util.Log; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import okhttp3.Call; import okhttp3.Callback; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /** * OkHttp的封装工具类 * Created by LGL on 2016/6/19. */ public class OkHttpUtils { //TAG private static final String TAG = OkHttpUtils.class.getSimpleName(); //声明客户端 private OkHttpClient client; //防止多个线程同时访问所造成的安全隐患 private volatile static OkHttpUtils okHttpUtils; //定义提交类型Json private static final MediaType JSON = MediaType.parse("application/json;charset=utf-8"); //定义提交类型String private static final MediaType STRING = MediaType.parse("text/x-markdown;charset=utf-8"); //子线程 private Handler handler; //构造方法 private OkHttpUtils() { //初始化 client = new OkHttpClient(); handler = new Handler(Looper.getMainLooper()); } //单例模式 public static OkHttpUtils getInstance() { OkHttpUtils okUtils = null; if (okHttpUtils == null) { //线程同步 synchronized (OkHttpUtils.class) { if (okUtils == null) { okUtils = new OkHttpUtils(); okHttpUtils = okUtils; } } } return okUtils; } /** * 请求的返回结果是json字符串 * * @param jsonValue * @param callBack */ private void onsuccessJsonStringMethod(final String jsonValue, final FuncJsonString callBack) { handler.post(new Runnable() { @Override public void run() { if (callBack != null) { try { //解析json callBack.onResponse(jsonValue); } catch (Exception e) { } } } }); } /** * 求的返回结果是json对象 * * @param jsonValue * @param callBack */ private void onsuccessJsonObjectMethod(final String jsonValue, final FuncJsonObject callBack) { handler.post(new Runnable() { @Override public void run() { if (callBack != null) { try { callBack.onResponse(new JSONObject(jsonValue)); } catch (JSONException e) { e.printStackTrace(); } } } }); } /** * 求的返回结果是json数组 * * @param data * @param callBack */ private void onsuccessJsonByteMethod(final byte[] data, final FuncJsonObjectByte callBack) { handler.post(new Runnable() { @Override public void run() { if (callBack != null) { callBack.onResponse(data); } } }); } /** * 同步请求,不是很常用,因为会阻塞线程 * * @param url * @return */ public String syncGetByURL(String url) { //构建一个Request请求 Request request = new Request.Builder().url(url).build(); Response response = null; try { //同步请求数据 response = client.newCall(request).execute(); if (response.isSuccessful()) { return response.body().string(); } } catch (Exception e) { } return null; } /** * 请求指定的url,返回的结果是json字符串 * * @param url * @param callback */ public void syncJsonStringByURL(String url, final FuncJsonString callback) { final Request request = new Request.Builder().url(url).build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.i(TAG, "解析失败"); } //解析成功 @Override public void onResponse(Call call, Response response) throws IOException { if (response != null && response.isSuccessful()) { onsuccessJsonStringMethod(response.body().string(), callback); } } }); } /** * 返回字符串json的接口 */ interface FuncJsonString { //处理我们返回的结果 void onResponse(String result); } /** * 返回json对象的接口 */ interface FuncJsonObject { //处理我们返回的结果 void onResponse(JSONObject jsonObject); } /** * 返回json对象的接口 */ interface FuncJsonObjectByte { //处理我们返回的结果 void onResponse(byte[] result); } /** * 返回json对象的接口 */ interface FuncJsonObjectBitmap { //处理我们返回的结果 void onResponse(Bitmap bitmap); } }
这里可以看到,我们基本上没做什么东西,对吧,只是把常用的方法都复写作了一些简单的操作而已,而且我们的注释也写的十分详细,你需要扩展的话,直接扩展就好了,这样,我们去验证一下,xml中加上
<TextView android:id="@+id/tv_json" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="json数据" /> <Button android:id="@+id/btn_json" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="解析json数据" />
好的,什么初始化的我就不写出来了,直接看点击事件
//解析json btn_json.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //单例初始化 OkHttpUtils okHttpUtils = OkHttpUtils.getInstance(); /** * 地址 * 成功回调 */ okHttpUtils.syncJsonStringByURL(json_url, new OkHttpUtils.FuncJsonString() { @Override public void onResponse(String result) { tv_json.setText(result); Log.i(TAG,""+result); } }); } });
可以看到,是不是非常的简单就OK了。我们只要定义传url进入就可以了,而接口,我们使用的是豆瓣的接口
//json地址 private String json_url = "https://api.douban.com/v2/book/1220562";
这样我们可以看下运行结果
当然,我也是只提供一种思路罢了,这个封装类并不完善,还需要你自己根据需求来实施,好的,我这里也就继续来优化一下了
六.封装优化
前面可以看到,我们已经封装好了一个工具类,但是并不完善,现在呢,我们就完善的封装一下,当然,也只是针对功能点去优化,比如刚才我们只封装了一个解析返回json字符串,现在我们来一个解析直接返回一个json对象,嘿嘿,怎么做呢?
/** * 请求指定的url,返回的结果是json对象 * * @param url * @param callback */ public void syscJsonObjectByURL(String url, final FuncJsonObject callback) { final Request request = new Request.Builder().url(url).build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.i(TAG, "解析失败"); } @Override public void onResponse(Call call, Response response) throws IOException { if (response != null && response.isSuccessful()) { onsuccessJsonObjectMethod(response.body().string(), callback); } } }); }
跟之前的其实很类似,同样的,我们可以返回byte字节数组
/** * 请求指定的url,返回的结果是byte字节数组 * * @param url * @param callback */ public void syscGetByteByURL(String url, final FuncJsonObjectByte callback) { final Request request = new Request.Builder().url(url).build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.i(TAG, "解析失败"); } @Override public void onResponse(Call call, Response response) throws IOException { if (response != null && response.isSuccessful()) { onsuccessJsonByteMethod(response.body().bytes(), callback); } } }); }
我们也看到了,我们还剩下Bitmap,我们还自带裁剪功能哦,哈哈
/** * 请求指定的url,返回的结果是Bitmap * @param url * @param callback */ public void syscDownloadImageByURL(String url, final FuncJsonObjectBitmap callback){ final Request request = new Request.Builder().url(url).build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.i(TAG, "解析失败"); } @Override public void onResponse(Call call, Response response) throws IOException { if (response != null && response.isSuccessful()) { byte [] data = response.body().bytes(); Bitmap bitmap = new TailorImageView().transform(BitmapFactory.decodeByteArray(data,0,data.length)); callback.onResponse(bitmap); } } }); }
到这里,基本的get封装就应该差不多写完了,但是别忘了,论post的重要性,既然如此,那我们就继续封装,首先实现的一个功能
post提交表单数据
开发中,也是有诸多需要post的地方的,毕竟安全性,传输都是个很不错的选择,我们继续写方法
/** * 向服务器提交表单 * * @param url 提交地址 * @param params 提交数据 * @param callback 提交回调 */ public void sendDatafForClicent(String url, Map<String, String> params, final FuncJsonObject callback) { //表单对象,包含input开始的操作 FormBody.Builder from = new FormBody.Builder(); //键值对不为空,他的值也不为空 if (params != null && !params.isEmpty()) { for (Map.Entry<String, String> entry : params.entrySet()) { //装载表单值 from.add(entry.getKey(), entry.getValue()); } } RequestBody body = from.build(); //post提交 Request request = new Request.Builder().url(url).post(body).build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.i(TAG, "解析失败"); } @Override public void onResponse(Call call, Response response) throws IOException { if (response != null && response.isSuccessful()) { onsuccessJsonObjectMethod(response.body().string(), callback); } } }); }
这里没有服务端,所以就不能测试了,我这里也就教大家怎么使用就好了,首先xml中定义一个按钮
<Button android:id="@+id/btn_post" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="POST表单提交" />
我们可以直接看他的点击事件
//post表单提交 btn_post.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { OkHttpUtils okHttpUtils = OkHttpUtils.getInstance(); //服务端的地址 String post_url = ""; //map集合 HashMap<String, String> map = new HashMap<String, String>(); map.put("username", "LGL"); map.put("password", "12345678"); okHttpUtils.sendDatafForClicent(post_url, map, new OkHttpUtils.FuncJsonObject() { @Override public void onResponse(JSONObject jsonObject) { //输出结果 Log.i(TAG, jsonObject.toString()); } }); } });
OK,这样就提交了表单,这就是一个完整的封装过程了,如果你问,那我们普通的请求怎么办呢?额,你看了这么久还不熟悉他的套路?我嘴角微微一笑,你的司机之路还很长啊,咳咳,跑题了,如果大家还有什么疑问的话,可以去鸿洋那里看看,我相信现在很多的文章都将了很多的基本使用的,所以我也不是想怎么去讲解析json什么的
- Android OkHttp完全解析 是时候来了解OkHttp了
2016/6/20补充:
Post提交数据,应该是这样判断的
if (params != null && !params.isEmpty())
有个!的判断,在Demo里面是没有的,现在及时改正了
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Android Crash的跟踪方法,使用腾讯Bugly来捕捉一些疑难杂症,让我们APP稳定上线
Android Crash的跟踪方法,使用腾讯Bugly来捕捉一些疑难杂症,让我们APP稳定上线 我们在开发中常常会注意到一些Crash,这正是很头疼的,而且Crash会带来很多意想不到的状态,很恶心,所以今天,我们来着重研究一下Crash,同时,我们也将使用第三方SDK,也就是腾讯的Bugly来扑捉这些Crash,让他无所遁形。 一.糟糕的Android Crash 作为开发人员,很容易就能碰到Crash,但是作为用户,我相信也有很大部分人碰到过Crash,这些原因,也正是Android的平台差异化和适配所导致的,而且,你要是你安装一个软件就Crash,我想你会立马把他卸载掉。同时心理嘲笑了一下开发人员,有些用户还有可能会去商店给你差评之类的,这就不多说了,反正会直接影响到你APP的口碑和市场竞争能力,你要是一些用户很多的APP的话,你还得考虑用户留存和收入的问题了 而在以前,我们是怎么处理Crash的呢?一般有三个补救措施吧,感觉,当然,还有其他,待补充: 1.等待用户反馈,这样很被动 2.了解出错原因,让用户提供日志和使用场景,不靠谱,攒人品吧 3.提示用户反馈,这样对用户体验有...
- 下一篇
Unity与安卓开发的一些路径知识
文章目录[点击展开](?)[+] APK安装之后找不到路径 公司的测试机(安卓)基本都是不带SD卡的。 APK在安卓手机上安装之后,使用手机助手类的软件打开文件管理,打开内置SDK卡/Android/data/ 在这个目录下却发现找不到以应用的包名(com.xxx.xxx) 开头的文件夹,那比如要打开这个目录查看里面的文件呢? 但是却能看到一些其它APP的目录,那么请检查以下设置: 1、打开File-Build Settings- 选择Player Settings,请确认已经切换到了Android 平台,找到Configuration这一部分设置 2、Install Location 选择 Automatic Write Permission 选择 External (SDCard) 3、重新打包APK,并安装,就可以在文件管理中找到这个目录了。 安卓的写入路径 比如你想在安装目录下创建一个目录并往里面写入文件,路径建议这样写:(和windows下的路径符号不同,而是和浏览器中网络的路径符号相同) string savePath = Application.persistentDa...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS8安装Docker,最新的服务器搭配容器使用
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2全家桶,快速入门学习开发网站教程
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Windows10,CentOS7,CentOS8安装Nodejs环境