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的官方介绍,大家可以移步这里

如果想看源码,可以去Github上

我们既然要使用,就根据github上来吧,加入依赖,把依赖添加到build.gradle中,当然,他是提供jar的,你如果用Eclipse获取喜欢用jar,你也可以直接下载jar

compile 'com.squareup.okhttp3:okhttp:3.3.1'

因为我们会用到图片解析,所以可以加上Picasso的依赖,关于它的介绍,可以移步

这个库,我下篇博文会介绍到,这里你只要知道是这么添加依赖和使用就可以了

compile 'com.squareup.picasso:picasso:2.5.2'

记住,网络的使用,是需要添加权限的哦!

  <!--网络权限-->
  <uses-permission android:name="android.permission.INTERNET"/>

行,大致的配置就到这里OK了,如果你还有什么不清楚的,可以去他们官网或者github上瞧一瞧,看一看,这里再送上一下下载jar的地址吧!

OkHttp内部依赖了Okio,这里也提供了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什么的

2016/6/20补充:

Post提交数据,应该是这样判断的

 if (params != null && !params.isEmpty()) 

有个!的判断,在Demo里面是没有的,现在及时改正了

本文的Demo我会上传到CSDN,大家可以下载

下载地址:http://download.csdn.net/detail/qq_26787115/9553992

如果大家对我有什么兴趣或者对我博客有兴趣,可以加群一起聊聊

通往Android的神奇之旅:555974449

OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据的更多相关文章

  1. 基于ASP.Net +easyUI框架上传图片,实现图片上传,提交表单

    <body> <link href="../../Easyui/themes/easyui.css" rel="stylesheet" typ ...

  2. RX系列四 | RxAndroid | 加载图片 | 提交表单

    RX系列四 | RxAndroid | 加载图片 | 提交表单 说实话,学RxJava就是为了我们在Android中运用的更加顺手一点,也就是RxAndroid,我们还是先一步步来,学会怎么去用的比较 ...

  3. i利用图片按钮 和 input type="image" 为背景提交表单

    <img src="img/cancel.jpg" onclick="javascript:document.getElementByIdx_x('loginFor ...

  4. Yii框架用ajax提交表单时候报错Bad Request (#400): Unable to verify your data submission.

    提交表单报400错误,提示 "您提交的数据无法验证"原来是csrf验证的问题,因为表单是自己写的,在Yii框架中,为了防止csrf攻击,对post的表单数据封装了CSRF令牌验证. ...

  5. 解析form表单数据

    //解析form表单数据 function parseFormData(params) { var args = new Object(); for(var key in params){ if(!p ...

  6. java模拟表单上传文件,java通过模拟post方式提交表单实现图片上传功能实例

    java模拟表单上传文件,java通过模拟post方式提交表单实现图片上传功能实例HttpClient 测试类,提供get post方法实例 package com.zdz.httpclient; i ...

  7. [Python 从入门到放弃] 6. 文件与异常(二)

    本章所用test.txt文件可以在( [Python 从入门到放弃] 6. 文件与异常(一))找到并自行创建 现在有个需求,对test.txt中的文本内容进行修改: (1)将期间的‘:’改为‘ sai ...

  8. Quick Cocos (2.2.5plus)CoinFlip解析(MenuScene display AdBar二次封装)

    转载自:http://cn.cocos2d-x.org/tutorial/show?id=1621 从Samples中找到CoinFlip文件夹,复制其中的 res 和 script 文件夹覆盖新建工 ...

  9. 使用post方式提交表单如何获取图片数据及其他文本参数[NodeJS]

    当POST方式提交包含图片的表单时,如上传图片时,需要在<form>字段需要添加参数enctype="multipart/form-data",表明以二进制方式传输数据 ...

随机推荐

  1. 我在 Ubuntu 下使用 Sublime 编写 python 代码时遇到并解决的问题

    Ubuntu 下 Sublime 无法输入中文 解决方法如下: sudo apt-get update && sudo apt-get upgrade 克隆项目到本地 : git cl ...

  2. 如何搭建ssh服务?

    为了日后便于查询,本文所涉及到的所有命令集合如下: rpm -qa | grep openssh #查看是否安装了openssh软件 service sshd status #服务端的ssh状态 if ...

  3. MySQL 连接的使用

    MySQL 连接的使用 在前几章节中,我们已经学会了如果在一张表中读取数据,这是相对简单的,但是在真正的应用中经常需要从多个数据表中读取数据. 本章节我们将向大家介绍如何使用 MySQL 的 JOIN ...

  4. WebRTC 音频算法 附完整C代码

    WebRTC提供一套音频处理引擎, 包含以下算法: AGC自动增益控制(Automatic Gain Control) ANS噪音抑制(Automatic Noise Suppression) AEC ...

  5. How To determine DDIC Check Table, Domain and Get Table Field Text Data For Value?

     How To determineDDIC Check Table, Domain and Get Table Field Text Data For Value? 1.Get Table Fie ...

  6. 第一个Angular2的样例

    欢迎跟我一起学习Angular2 本文根据angular2官网手动敲码得来: 本文地址:http://blog.csdn.net/sushengmiyan 本文作者:苏生米沿 - 开发环境搭建 - 配 ...

  7. 20160212.CCPP体系详解(0022天)

    程序片段(01):01.二维数组.c 内容概要:二维数组 #include <stdio.h> #include <stdlib.h> //01.关于栈内存开辟数组: // 诀 ...

  8. 带有进度条的WebView

    带有进度条的WebView 本篇继于WebView的使用 效果图 自定义一个带有进度条的WebView package com.kongqw.kbox.view; import android.con ...

  9. IOS开发初体验

    IOS开发初体验 搭建开发环境 不多说什么了,开发环境的搭建太简单了,上App Store搜索XCode下载就行了,说多了都是眼泪 创建第一个IOS项目--HolleIOS 创建工程 选择工程创建位置 ...

  10. Ubuntu14下安装svn仓库,以及权限配置

    sudo apt-get update 接下来安装svn apt-get install subversionapt-get install libapache2-svn 检查svn是否安装成功了: ...