MyOkHttp

Project Url: tsy12321/MyOkHttp
Introduction: 对 Okhttp3 进行二次封装,对外提供了 POST 请求、GET 请求、PATCH 请求、PUT 请求、DELETE 请求、上传文件、下载文件、取消请求、Raw/Json/Gson 返回、后台下载管理等功能
More: Author   ReportBugs   
Tags:
Android-okhttp-下载管理-网络请求-

License Download

对 Okhttp3 进行二次封装,对外提供了 POST 请求、GET 请求、PATCH 请求、PUT 请求、DELETE 请求、上传文件、下载文件、取消请求、Raw/Json/Gson 返回、后台下载管理等功能.

版本更新记录

版本更新记录

参考文献

对于 Okhttp3 的封装参考了:

  1. https://github.com/hongyangAndroid/okhttputils
  2. https://github.com/jeasonlzy/okhttp-OkGo
  3. https://github.com/ZhaoKaiQiang/OkHttpPlus

cookie 本地持久化使用了 PersistentCookieJar:

  1. https://github.com/franmontiel/PersistentCookieJar

如何添加

在 app 目录下的 build.gradle 中添加依赖

    compile 'com.tsy:myokhttp:1.1.4'

1 总体简介

在项目入口创建唯一 MyOkhttp 实例

MyOkHttp mMyOkhttp = new MyOkHttp();

也可以自行配置 OkhttpClient.

OkHttpClient okHttpClient = new OkHttpClient.Builder()
                  .connectTimeout(10000L, TimeUnit.MILLISECONDS)
                  .readTimeout(10000L, TimeUnit.MILLISECONDS)
                  //其他配置
                 .build();

MyOkHttp mMyOkhttp = new MyOkHttp(okHttpClient);

设置 cookie.

//设置开启 cookie
ClearableCookieJar cookieJar =
        new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(getApplicationContext()));
OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .cookieJar(cookieJar)
        .build();
mMyOkHttp = new MyOkHttp(okHttpClient);

1.1 调用方式

整个调用采用链式调用的方式. 方便以后扩展.

1.2 请求类型

现在有 Get, Post, Patch, Put, Delete, Upload, Download 这些请求方式

1.3 添加参数方式

添加参数可以使用 addParam 一个个添加, 也可以使用 params 一次性添加

1.4 添加 Header 方式

添加参数可以使用 addHeader 一个个添加, 也可以使用 headers 一次性添加

1.5 回调格式

现在回调格式有以下几种:

  1. Raw 原生数据 RawResponseHandler
  2. Json JsonResponseHandler
  3. Gson GsonResponseHandler

2 调用示例

2.1 POST 请求 + Json 回调示例

kv 参数

String url = "http://192.168.2.135/myokhttp/post.php";

Map<String, String> params = new HashMap<>();
params.put("name", "tsy");
params.put("age", "24");

mMyOkhttp.post()
        .url(url)
        .params(params)
        .tag(this)
        .enqueue(new JsonResponseHandler() {
            @Override
            public void onSuccess(int statusCode, JSONObject response) {
                Log.d(TAG, "doPost onSuccess JSONObject:" + response);
            }

            @Override
            public void onSuccess(int statusCode, JSONArray response) {
                Log.d(TAG, "doPost onSuccess JSONArray:" + response);
            }

            @Override
            public void onFailure(int statusCode, String error_msg) {
                Log.d(TAG, "doPost onFailure:" + error_msg);
            }
        });

json 参数

String url = "http://192.168.2.135/myokhttp/post_json.php";

JSONObject jsonObject = new JSONObject();

try {
    jsonObject.put("name", "tsy");
    jsonObject.put("age", 24);
    jsonObject.put("type", "json");
} catch (JSONException e) {
    e.printStackTrace();
}

mMyOkhttp.post()
        .url(url)
        .jsonParams(jsonObject.toString())          //与 params 不共存 以 jsonParams 优先
        .tag(this)
        .enqueue(new JsonResponseHandler() {
            @Override
            public void onSuccess(int statusCode, JSONObject response) {
                Log.d(TAG, "doPostJSON onSuccess JSONObject:" + response);
            }

            @Override
            public void onSuccess(int statusCode, JSONArray response) {
                Log.d(TAG, "doPostJSON onSuccess JSONArray:" + response);
            }

            @Override
            public void onFailure(int statusCode, String error_msg) {
                Log.d(TAG, "doPostJSON onFailure:" + error_msg);
            }
        });

2.2 GET 请求 + Raw 回调示例

String url = "http://192.168.2.135/myokhttp/get.php";

mMyOkhttp.get()
        .url(url)
        .addParam("name", "tsy")
        .addParam("id", "5")
        .tag(this)
        .enqueue(new RawResponseHandler() {
            @Override
            public void onSuccess(int statusCode, String response) {
                Log.d(TAG, "doGet onSuccess:" + response);
            }

            @Override
            public void onFailure(int statusCode, String error_msg) {
                Log.d(TAG, "doGet onFailure:" + error_msg);
            }
        });

2.3 Patch 请求 + Json 回调示例

String url = "http://192.168.2.135/myokhttp/patch.php/id/5/name/tsy";

mMyOkhttp.patch()
        .url(url)
        .tag(this)
        .enqueue(new JsonResponseHandler() {
            @Override
            public void onSuccess(int statusCode, JSONObject response) {
                Log.d(TAG, "doPatch onSuccess:" + response);
            }

            @Override
            public void onFailure(int statusCode, String error_msg) {
                Log.d(TAG, "doPatch onFailure:" + error_msg);
            }
        });

2.4 Put 请求 + Json 回调示例

String url = "http://192.168.2.135/myokhttp/put.php/id/5/name/tsy/age/15";

mMyOkhttp.put()
        .url(url)
        .tag(this)
        .enqueue(new JsonResponseHandler() {
            @Override
            public void onSuccess(int statusCode, JSONObject response) {
                Log.d(TAG, "doPut onSuccess:" + response);
            }

            @Override
            public void onFailure(int statusCode, String error_msg) {
                Log.d(TAG, "doPut onFailure:" + error_msg);
            }
        });

2.5 Delete 请求 + Json 回调示例

String url = "http://192.168.2.135/myokhttp/delete.php/id/5";

mMyOkhttp.delete()
        .url(url)
        .tag(this)
        .enqueue(new JsonResponseHandler() {
            @Override
            public void onSuccess(int statusCode, JSONObject response) {
                Log.d(TAG, "doDelete onSuccess:" + response);
            }

            @Override
            public void onFailure(int statusCode, String error_msg) {
                Log.d(TAG, "doDelete onFailure:" + error_msg);
            }
        });

2.6 上传文件 + Gson 回调示例

String url = "http://192.168.2.135/myokhttp/upload.php";

mMyOkhttp.upload()
        .url(url)
        .addParam("name", "tsy")
        .addFile("avatar",
                new File(Environment.getExternalStorageDirectory()
                        + "/ahome/sasas.jpg"))        //上传已经存在的 File
//                .addFile("avatar2", "asdsda.png", byteContents)    //直接上传 File bytes
        .tag(this)
        .enqueue(new GsonResponseHandler<UploadModel>() {
            @Override
            public void onFailure(int statusCode, String error_msg) {
                Log.d(TAG, "doUpload onFailure:" + error_msg);
            }

            @Override
            public void onProgress(long currentBytes, long totalBytes) {
                Log.d(TAG, "doUpload onProgress:" + currentBytes + "/" + totalBytes);
            }

            @Override
            public void onSuccess(int statusCode, UploadModel response) {
                Log.d(TAG, "doUpload onSuccess:" + response.ret + " " + response.msg);
            }
        });

2.7 下载文件

String url = "http://192.168.2.135/myokhttp/head.jpg";

mMyOkhttp.download()
        .url(url)
        .filePath(Environment.getExternalStorageDirectory() + "/ahome/a.jpg")
        .tag(this)
        .enqueue(new DownloadResponseHandler() {
            @Override
            public void onStart(long totalBytes) {
                Log.d(TAG, "doDownload onStart");
            }

            @Override
            public void onFinish(File downloadFile) {
                Log.d(TAG, "doDownload onFinish:");
            }

            @Override
            public void onProgress(long currentBytes, long totalBytes) {
                Log.d(TAG, "doDownload onProgress:" + currentBytes + "/" + totalBytes);
            }

            @Override
            public void onFailure(String error_msg) {
                Log.d(TAG, "doDownload onFailure:" + error_msg);
            }
        });

3 取消请求(建议放在 BaseActivity,BaseFragment 的 onDestroy 中)

mMyOkhttp.cancel(this);     //tag 即之前请求时传入的 tag 建议直接将页面作为 object 传入

4 下载管理

实现了下载管理的核心逻辑,包括添加下载任务、开始任务、暂停任务、删除任务、任务下载等待、下载进度和状态监听等功能。依赖于 MyOkhttp

4.1 实现 AbstractDownloadMgr

项目中创建 DownloadMgr 继承 AbstractDownloadMgr,并实现下载任务的状态,进度的本地持久化保存。(AbstractDownloadMgr 只负责在内存中存储所有任务, 需要项目自行实现本地持续化存储,重新进入 App 后的任务恢复等)

示例:

/**
 * 实现下载管理 AbstractDownloadMgr
 * Created by tsy on 2016/11/24. */

public class DownloadMgr extends AbstractDownloadMgr {

    private DownloadMgr(Builder builder) {
        super(builder);
    }

    /**
     * 初始进入 app 恢复所有未完成的任务
     */
    @Override
    public void resumeTasks() {
        if(DEBUG) {
            Log.i(TAG, "start resumeTasks");
        }

        //获取所有未完成的任务(已完成的不需要添加)
        DownloadContract.Interactor downloadInteractor = new DownloadInteractor();
        ArrayList<Task> tasks = downloadInteractor.parseTask(downloadInteractor.getAllUnfinishedDownloadTasks());

        //将任务加到下载管理队列中
        if(tasks != null && tasks.size() > 0) {
            for(int i = 0; i < tasks.size(); i ++) {
                Task task = tasks.get(i);
                task.setDefaultStatus(DEFAULT_TASK_STATUS_PAUSE);       //所有任务初始设置为暂停
                addTask(tasks.get(i));
            }

            downloadInteractor.pauseAllTask();
        }
    }

    /**
     * 保存进度
     * @param taskId taskId
     * @param currentBytes 已经下载的 bytes
     * @param totalBytes 总共 bytes
     */
    @Override
    protected void saveProgress(String taskId, long currentBytes, long totalBytes) {
        DownloadContract.Interactor downloadInteractor = new DownloadInteractor();
        downloadInteractor.updateProgress(taskId, currentBytes, totalBytes);
    }

    /**
     * 下载任务开始
     * @param taskId task id
     */
    @Override
    protected void onTaskStart(String taskId) {
        DownloadContract.Interactor downloadInteractor = new DownloadInteractor();
        downloadInteractor.startTask(taskId);
    }

    /**
     * 下载任务暂停
     * @param taskId task id
     */
    @Override
    protected void onTaskPause(String taskId) {
        DownloadContract.Interactor downloadInteractor = new DownloadInteractor();
        downloadInteractor.pauseTask(taskId);
    }

    /**
     * 下载任务完成
     * @param taskId task id
     */
    @Override
    protected void onTaskFinish(String taskId) {
        DownloadContract.Interactor downloadInteractor = new DownloadInteractor();
        downloadInteractor.finishTask(taskId);
    }

    /**
     * 下载任务失败
     * @param taskId task id
     */
    @Override
    protected void onTaskFail(String taskId) {
        //失败设置为暂停 允许用户再次尝试开始
        DownloadContract.Interactor downloadInteractor = new DownloadInteractor();
        downloadInteractor.pauseTask(taskId);
    }

    //实现 Builder
    public static class Builder extends AbstractDownloadMgr.Builder {

        @Override
        public AbstractDownloadMgr build() {
            return new DownloadMgr(this);
        }
    }
}

4.2 使用后台下载管理

4.2.1 项目开始时初始化唯一实例 DownloadMgr 并自定义配置

mDownloadMgr = (DownloadMgr) new DownloadMgr.Builder()
                .myOkHttp(mMyOkhttp)
                .maxDownloadIngNum(5)       //设置最大同时下载数量(不设置默认 5)
                .saveProgressBytes(50 * 1024)  //设置每 50kb 触发一次 saveProgress 保存进度 (不能在 onProgress 每次都保存 过于频繁) 不设置默认 50kb
                .build();

4.2.2 恢复本地存储的所有未完成的任务

mDownloadMgr.resumeTasks();
`

4.2.3 创建一个下载任务

DownloadMgr.Task task = new DownloadMgr.Task();
task.setTaskId(mDownloadMgr.genTaskId());       //生成一个 taskId
task.setUrl("xxxxx");   //下载地址
task.setFilePath("xxxxxxx");    //下载后文件保存位置
task.setCompleteBytes(1234L);       //设置已完成的 bytes (用于恢复任务时添加,如果新添加的任务不需要设置该字段)
task.setDefaultStatus(DownloadMgr.DEFAULT_TASK_STATUS_START);       //任务添加后开始状态 如果不设置 默认任务添加后就自动开始

mDownloadMgr.addTask(task);

4.2.4 开始 暂停任务

mDownloadMgr.startTask("taskId");

mDownloadMgr.pauseTask("taskId");

4.2.5 页面添加下载监听

PS:不要使用匿名类,会造成内存泄露。在 onStart 或者 onResume 时初始化监听,在 onStop 或者 onPause 时销毁监听

@Override
public void onResume() {
    super.onResume();

    //每次显示页面的时候加上监听
    mDownloadTaskListener = new DownloadTaskListener() {
        @Override
        public void onStart(String taskId, long completeBytes, long totalBytes) {
            //开始下载
        }

        @Override
        public void onProgress(String taskId, long currentBytes, long totalBytes) {
            //下载进度

            //建议使用 handler 延迟刷新下载进度 不然过于频繁刷新
            if(!mMyHandler.hasMessages(MSG_DELAY_NOTIFICAION)) {
                Message message = new Message();
                message.what = MSG_DELAY_NOTIFICAION;
                mMyHandler.sendMessageDelayed(message, 300);
            }
        }

        @Override
        public void onPause(String taskId, long currentBytes, long totalBytes) {
            //下载暂停
        }

        @Override
        public void onFinish(String taskId, File file) {
            //下载完成
        }

        @Override
        public void onFailure(String taskId, String error_msg) {
            //下载失败
        }
    };

    //加入监听
    mDownloadMgr.addListener(mDownloadTaskListener);
}

@Override
public void onPause() {

    //释放监听 防止内存泄露
    mDownloadMgr.removeListener(mDownloadTaskListener);
    mDownloadTaskListener = null;

    super.onPause();
}

4.3 API

1. 进入应用后恢复所有本地任务

/**
 * 初始进入 app 恢复所有未完成的任务
 */
void resumeTasks()

2. 添加下载任务

/**
 * 添加下载任务
 * @param task Task
 */
DownloadTask addTask(Task task)

3. 开始任务

/**
 * 开始任务
 * @param taskId task id
 */
void startTask(String taskId)

4. 开始所有任务

/**
 * 开始所有任务
 */
void startAllTask()

5. 暂停任务

/**
 * 暂停任务
 * @param taskId task id
 */
void pauseTask(String taskId)

6. 暂停所有任务

/**
 * 暂停所有任务
 */
void pauseAllTask()

7. 删除任务

/**
 * 删除任务
 * @param taskId 任务 id
 */
void deleteTask(String taskId)

8. 添加下载监听

/**
 * 添加下载监听
 * @param downloadTaskListener
 */
void addListener(DownloadTaskListener downloadTaskListener)

9. 移除下载监听

/**
 * 移除下载监听
 * @param downloadTaskListener
 */
void removeListener(DownloadTaskListener downloadTaskListener)

10. 生成 taskId

/**
 * 生成 taskId yyyyMMddHHmmss+3 位随机数字
 * @return
 */
String genTaskId()

11. 获取当前任务的下载任务信息

/**
 * 获取当前任务的下载任务信息
 * @param taskId 任务 id
 * @return
 */
DownloadTask getDownloadTask(String taskId)

4.4 下载任务状态说明

public class DownloadStatus {
    public static final int STATUS_DEFAULT = -1;        //初始状态
    public static final int STATUS_WAIT = 0;            //队列等待中
    public static final int STATUS_PAUSE = 1;           //暂停
    public static final int STATUS_DOWNLOADING = 2;     //下载中
    public static final int STATUS_FINISH = 3;          //下载完成
    public static final int STATUS_FAIL = 4;            //下载失败
}

4.5 下载任务监听说明

public interface DownloadTaskListener {
    /**
     * 任务开始
     * @param taskId task id
     * @param completeBytes 断点续传 已经完成的 bytes
     * @param totalBytes total bytes
     */
    void onStart(String taskId, long completeBytes, long totalBytes);

    /**
     * 任务下载中
     * @param taskId task id
     * @param currentBytes 当前已经下载的 bytes
     * @param totalBytes total bytes
     */
    void onProgress(String taskId, long currentBytes, long totalBytes);

    /**
     * 任务暂停
     * @param taskId task id
     * @param currentBytes 当前已经下载的 bytes
     * @param totalBytes total bytes
     */
    void onPause(String taskId, long currentBytes, long totalBytes);

    /**
     * 任务完成
     * @param taskId task id
     * @param file 下载完成后的 file
     */
    void onFinish(String taskId, File file);

    /**
     * 任务失败
     * @param taskId task id
     * @param error_msg error_msg
     */
    void onFailure(String taskId, String error_msg);
}

5 混淆

#myokhttp
-dontwarn com.tsy.sdk.myokhttp.**
-keep class com.tsy.sdk.myokhttp.**{*;}

#okhttp
-dontwarn okhttp3.**
-keep class okhttp3.**{*;}

#okio
-dontwarn okio.**
-keep class okio.**{*;}

#persistentcookiejar
-dontwarn com.franmontiel.persistentcookiejar.**
-keep class com.franmontiel.persistentcookiejar.**{*;}

#gson
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

## 还有所有定义的实体类

About Me

简书地址:http://www.jianshu.com/users/21716b19302d/latest_articles

微信公众号

我的公众号

License

Copyright 2017 SY.Tang

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools