NoHttpConnecter——基于 NoHttp 的封装

基于 NoHttp 的封装,主要包括字符串、Bitmap、JsonArray 等的 GET 和 POST 请求、文件上传下载方法的简单封装,以及五种缓存模式的使用。

首先对 NoHttp 网络框架做一个简介

Nohttp 是一个 Android Http 标准框架,底层可动态切换 OkHttp、URLConnection,与 RxJava 完美结合,支持缓存数据到数据库或 SD 卡(缓存数据自动加密),支持请求 Restful 风格的接口,比 Retrofit 更简单易用。

Nohttp 框架特性

  • 动态配置底层框架为 OkHttp、HttpURLConnection
  • 支持异步请求、支持同步请求
  • 多文件上传,支持大文件上传,表单提交数据
  • 文件下载、上传下载、上传和下载的进度回调、错误回调
  • 支持 Json、xml、Map、List 的提交
  • 完美的 Http 缓存模式,可指定缓存到数据库、SD 卡,缓存数据已安全加密
  • 自定义 Request,直接请求 JsonObject、JavaBean 等
  • Cookie 的自动维持,App 重启、关开机后还持续维持
  • http 301 302 303 304 307 重定向,支持多层嵌套重定向
  • Https、自签名网站 Https 的访问、支持双向验证
  • 失败重试机制,支持请求优先级
  • GET、POST、PUT、PATCH、HEAD、DELETE、OPTIONS、TRACE 等请求协议
  • 用队列保存请求,平均分配多线程的资源,支持多个请求并发
  • 支持取消某个请求、取消指定多个请求、取消所有请求

这么多好用的功能,难道你不想试试?

NoHttp 开源框架地址:https://github.com/yanzhenjie/NoHttp

使用方法

1. Gradle添加依赖(推荐)

compile 'com.yanzhenjie.nohttp:okhttp:1.1.2' (可能非最新版)

2. 需要的权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

3. 初始化

初始化 NoHttp,并设置 NoHttp 底层采用那种网络框架去请求,建议把初始化方法放到 ApplicationonCreate 生命周期方法里面。还有别忘了在manifest.xml中注册Application

//初始化 NoHttp
NoHttp.initialize(this, new NoHttp.Config()
        .setConnectTimeout(30 * 1000)  // 设置全局连接超时时间,单位毫秒,默认10s。
        .setReadTimeout(30 * 1000)  // 设置全局服务器响应超时时间,单位毫秒,默认10s。
        // 配置缓存,默认保存数据库DBCacheStore,保存到SD卡使用DiskCacheStore。
        .setCacheStore(
                new DBCacheStore(this).setEnable(true) // 如果不使用缓存,设置setEnable(false)禁用。
        )
        // 配置Cookie,默认保存数据库DBCookieStore,开发者可以自己实现。
        .setCookieStore(
                new DBCookieStore(this).setEnable(true) // 如果不维护cookie,设置false禁用。
        )
        // 配置网络层,默认使用URLConnection,如果想用OkHttp:OkHttpNetworkExecutor。
        .setNetworkExecutor(new OkHttpNetworkExecutor())
);

4.接下来,你就可以愉快的进行网络请求了:

  • new 队列
RequestQueue requestQueue = NoHttp.newRequestQueue();
  • new 请求

比如这样,

Request<String> request = NoHttp.createStringRequest(url, RequestMethod.GET);

或者这样,

Request<JSONObject> objRequest = NoHttp.createJsonObjectRequest(url, RequestMethod.POST);

...等等(支持更多,如 JsonArray、Bitmap、byte[] 或自定义请求类型)。然后把需要的请求参数添加进来:

.add("name", "name") // String类型
...
  • 把请求添加到队列,完成请求
requestQueue.add(what, request, responseListener);
  • 回调对象,接受请求结果

处理成功、失败等方法的回调,实现当前界面的业务和逻辑。

  • 添加请求到队列时有一个what,这个what会在responseLisetener响应时回调回来,所以可以用一个responseLisetener接受多个请求的响应,用 what 来区分结果。
  • 强烈建议把生成队列写成懒汉单例模式,因为每新建队列就会 new 出相应个数的线程来,同时只有线程数固定了,队列的作用才会发挥到最大。
  • 取消请求

在组件销毁的时候(onDestroy())调用队列的按照 sign 取消的方法即可取消

这时我们发现有很多重复的操作,每个 Activity 和 Fragment 都这么写就显得有点麻烦了,再加上上面的两条重要提示,所以我们这里把队列进行单例模式封装,并把这些操作封装在 BaseActivityBaseFragment 中。

以下是 NoHttp 简单的封装,方便在项目中使用。

1. NoHttp 请求
public class HttpResponseListener<T> implements OnResponseListener<T> {

    private Activity mActivity;
    /**
     * Dialog
     */
    private WaitDialog mWaitDialog;
    /**
     * Request
     */
    private Request<?> mRequest;
    /**
     * 结果回调
     */
    private HttpListener<T> callback;

    /**
     * @param activity     context用来实例化dialog
     * @param request      请求对象
     * @param httpCallback 回调对象
     * @param canCancel    是否允许用户取消请求
     * @param isLoading    是否显示dialog
     */
    public HttpResponseListener(Activity activity, Request<?> request, HttpListener<T> httpCallback,
                                boolean canCancel, boolean isLoading) {
        this.mActivity = activity;
        this.mRequest = request;
        if (activity != null && isLoading) {
            mWaitDialog = new WaitDialog(activity);
            mWaitDialog.setCancelable(canCancel);
            mWaitDialog.setOnCancelListener(dialog -> mRequest.cancel());
        }
        this.callback = httpCallback;
    }

    /**
     * 开始请求, 这里显示一个dialog
     */
    @Override
    public void onStart(int what) {
        if (mWaitDialog != null && !mActivity.isFinishing() && !mWaitDialog.isShowing())
            mWaitDialog.show();
    }

    /**
     * 结束请求, 这里关闭dialog
     */
    @Override
    public void onFinish(int what) {
        if (mWaitDialog != null && mWaitDialog.isShowing())
            mWaitDialog.dismiss();
    }

    /**
     * 成功回调
     */
    @Override
    public void onSucceed(int what, Response<T> response) {
        if (callback != null) {
            // 这里判断一下http响应码,这个响应码问下你们的服务端你们的状态有几种,一般是200成功。
            // w3c标准http响应码:http://www.w3school.com.cn/tags/html_ref_httpmessages.asp

            callback.onSucceed(what, response);
        }
    }

    /**
     * 失败回调
     */
    @Override
    public void onFailed(int what, Response<T> response) {
        Exception exception = response.getException();
        if (exception instanceof NetworkError) {// 网络不好
            Toast.show(mActivity, R.string.error_please_check_network);
        } else if (exception instanceof TimeoutError) {// 请求超时
            Toast.show(mActivity, R.string.error_timeout);
        } else if (exception instanceof UnKnownHostError) {// 找不到服务器
            Toast.show(mActivity, R.string.error_not_found_server);
        } else if (exception instanceof URLError) {// URL是错的
            Toast.show(mActivity, R.string.error_url_error);
        } else if (exception instanceof NotFoundCacheError) {
            // 这个异常只会在仅仅查找缓存时没有找到缓存时返回
            Toast.show(mActivity, R.string.error_not_found_cache);
        } else {
            Toast.show(mActivity, R.string.error_unknow);
        }
        Logger.e("错误:" + exception.getMessage());
        if (callback != null)
            callback.onFailed(what, response);
    }

}
2. 接受回调结果
public interface HttpListener<T> {

    void onSucceed(int what, Response<T> response);

    void onFailed(int what, Response<T> response);

}
3. 队列的单例模式封装 -- 提供外部调用进行网络请求
public class CallServer {

    private static CallServer instance;

    /**
     * 请求队列
     */
    private RequestQueue requestQueue;

    private CallServer() {
        // 初始化请求队列,传入的参数是请求并发值
        requestQueue = NoHttp.newRequestQueue(3);
    }

    /**
     * 请求队列
     */
    public synchronized static CallServer getInstance() {
        if (instance == null)
            synchronized (CallServer.class) {
                if (instance == null)
                    instance = new CallServer();
            }
        return instance;
    }

    /**
     * 添加一个请求到请求队列
     *
     * @param activity     Context
     * @param what         用来标志请求, 当多个请求使用同一个Listener时, 在回调方法中会返回这个what。
     * @param request      请求对象
     * @param httpCallback 回调函数
     * @param canCancel    是否能被用户取消
     * @param isLoading    是否显示加载框
     * @param <T>
     */
    public <T> void add(Activity activity, int what, Request<T> request,
                        HttpListener<T> httpCallback, boolean canCancel, boolean isLoading) {
        requestQueue.add(what, request, new HttpResponseListener<>(activity, request, httpCallback,
                canCancel, isLoading));
    }

    /**
     * 取消这个sign标记的所有请求
     *
     * @param sign 请求的取消标志
     */
    public void cancelBySign(Object sign) {
        requestQueue.cancelBySign(sign);
    }

    /**
     * 取消队列中所有请求
     */
    public void cancelAll() {
        requestQueue.cancelAll();
    }

    /**
     * 停止队列
     */
    public void stop() {
        requestQueue.stop();
    }
}
4. 在 BaseActivity、BaseFragment 中发起请求,并设置标记
...
    /**
     * 用来标记取消
     */
    private Object object = new Object();

    /**
     * 发起请求
     *
     * @param what      what.
     * @param request   请求对象。
     * @param callback  回调函数。
     * @param canCancel 是否能被用户取消。
     * @param isLoading 实现显示加载框。
     * @param <T>       想请求到的数据类型。
     */
    public <T> void request(int what, Request<T> request, HttpListener<T> callback,
                            boolean canCancel, boolean isLoading) {
        // 这里设置一个sign给这个请求
        request.setCancelSign(object);
        CallServer.getInstance().add(context, what, request, callback, canCancel, isLoading);
    }

    @Override
    protected void onDestroy() {
        // 和声明周期绑定,退出时取消这个队列中的所有请求,当然可以在你想取消的时候取消也可以,不一定和声明周期绑定。
        CallServer.getInstance().cancelBySign(object);
        super.onDestroy();
    }
5. 其它界面继承 BaseActivity 或 BaseFragment,请求网络的方式跟上面的类似,多了几个参数而已。
...
Request<String> request = NoHttp.createStringRequest(url, RequestMethod.GET);
request.add("name", "name")
request(0, request, httpListener, true, true);
附 0. 封装中使用到的自定义控件及引用文字
// 加载框
public class WaitDialog extends ProgressDialog {

    public WaitDialog(Context context) {
        super(context);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setCanceledOnTouchOutside(false);
        setProgressStyle(STYLE_SPINNER);
        setMessage(context.getText(R.string.wait_dialog_title));
    }

}
//代码中的引用字符串
<string name="wait_dialog_title">正在请求,请稍候…</string>

<!-- error -->
<string name="error_please_check_network">请检查网络</string>
<string name="error_timeout">请求超时,网络不好或者服务器不稳定</string>
<string name="error_not_found_server">未发现指定服务器,请切换网络后重试</string>
<string name="error_url_error">URL错误</string>
<string name="error_not_found_cache">没有找到缓存</string>
<string name="error_unknow">未知错误</string>
<string name="error_response_code">服务器响应码%1$d</string>
<string name="error_response_code_dex">服务器响应码%1$d,如果你们服务器在这种情况下也返回数据你可以处理</string>

现已将此封装发布到 JitPack ,可以直接使用如下方式引入。

[图片上传失败...(image-8ba3f4-1520919194946)]

JitPack 引入方法

1. 在 Project 下的 build.gradle 添加
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
2. 在 Module 下的 build.gradle 添加
dependencies {
    compile 'com.github.lishide:NoHttpConnecter:v+latest version'
    //latest version 见上方 JitPack 图标所示,如:
    compile 'com.github.lishide:NoHttpConnecter:v1.0.2'
}

五大缓存模式

  • 1、Default 模式,实现 http304 重定向缓存
request.setCacheMode(CacheMode.DEFAULT);
  • 2、请求网络失败返回缓存
request.setCacheMode(CacheMode.REQUEST_NETWORK_FAILED_READ_CACHE);
  • 3、没有缓存才去请求网络
request.setCacheMode(CacheMode.NONE_CACHE_REQUEST_NETWORK);
  • 4、仅仅请求网络
request.setCacheMode(CacheMode.ONLY_REQUEST_NETWORK);
  • 5、仅仅读取缓存
request.setCacheMode(CacheMode.ONLY_READ_CACHE);

文件下载

  • 1、单个文件下载
  • 2、多个文件下载

文件下载也是队列,队列和开头所说的请求的队列是一样的。

  • 发起下载请求
mDownloadRequest = NoHttp.createDownloadRequest(url, path, filename, true, true);
downloadQueue.add(0, mDownloadRequest, downloadListener);
  • 暂停或者停止下载
mDownloadRequest.cancel();
  • 监听下载过程
private DownloadListener downloadListener = new DownloadListener() {

  // 下载开始
  @Override
  public void onStart(int what, boolean isResume, long beforeLength, Headers headers, long allCount) {

  }

  // 下载发生错误
  @Override
  public void onDownloadError(int what, Exception exception) {

  }

  // 更新下载进度和下载网速
  @Override
  public void onProgress(int what, int progress, long fileCount, long speed) {

  }

  // 下载完成
  @Override
  public void onFinish(int what, String filePath) {

  }

  // 下载被取消或者暂停
  @Override
  public void onCancel(int what) {

  }
};

关于文件下载,具体的请参考 Demo。

文件上传

  • 1、单个文件上传
Request<String> request = NoHttp.createStringRequest(url, RequestMethod.POST);
request.add("file", new FileBinary(file));
  • 2、多个文件上传(这里可以添加各种形式的文件,File、Bitmap、InputStream、ByteArray。)

    • 多个Key多个文件形式
    Request<String> request = ...
    request.add("file1", new FileBinary(File));
    request.add("file2", new FileBinary(File));
    request.add("file3", new InputStreamBinary(InputStream));
    request.add("file4", new ByteArrayBinary(byte[]));
    request.add("file5", new BitmapBinary(Bitmap));
    
    • 一个Key多个文件形式
    Request<String> request = ...
    fileList.add("image", new FileBinary(File));
    fileList.add("image", new InputStreamBinary(InputStream));
    fileList.add("image", new ByteArrayBinary(byte[]));
    fileList.add("image", new BitmapBinary(Bitmap));
    

    或者:

    Request<String> request = ...
    List<Binary> fileList = ...
    fileList.add(new FileBinary(File));
    fileList.add(new InputStreamBinary(InputStream));
    fileList.add(new ByteArrayBinary(byte[]));
    fileList.add(new BitmapStreamBinary(Bitmap));
    request.add("file_list", fileList);
    

......


本人仅是简单地对 NoHttp 网络请求框架进行轻量级的封装,后期还会进行持续维护,更多关于 NoHttp 的使用可直接查看原作。

NoHttp —— 一个有情怀的网络框架 ,让你的网络请求更简单。

像上面说的一样,NoHttp 真的很强大、很好用,嗯,没错。

Demo 源码请看 GitHub: NoHttpConnecter

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,657评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,133评论 25 707
  • 1. 功能介绍 1.1. Volley Volley 是 Google 推出的 Android 异步网络请求框架和...
    爱码士平头哥阅读 1,822评论 0 9
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,631评论 18 399
  • 并且好像迈出了错误的一步。 又是每三个月的临考前,终于有一科似乎可以handle的,但却因为一个定义式的概念纠结了...
    北方方阅读 133评论 0 0