Android 的OkHttp 網(wǎng)絡(luò)請求框架的封裝學(xué)習(xí)

以前寫過一些東西盐固,但是寫寫不看不用就容易忘掉看疗,今天寫寫東西記下來,有空的時(shí)候多溫習(xí)溫習(xí)
OkHttp 分為同步和異步請求燥筷;請求方式常用的有 get和post兩種方式,封裝請求的大致步驟為:
1院崇、首先 創(chuàng)建 一個(gè)mOkHttpClient = new OkHttpClient()對象肆氓;
2、構(gòu)建Request請求對象(根據(jù)get和post不同的請求方式分別創(chuàng)建)底瓣;
3谢揪、如果是 post請求還需要 構(gòu)建 請求參數(shù) Params,RequestBody requestBody = buildFormData(params); builder.post(requestBody).build;捐凭;
4拨扶、進(jìn)行網(wǎng)絡(luò)異步請求 mOkHttpClient.newCall(request).enqueue(new Callback() {} ),如果是同步請求茁肠,則改為 Response response = mOkHttpClient.newCall(request).execute()進(jìn)行 患民;
具體實(shí)現(xiàn)就不細(xì)說了,直接上代碼如下:

public class OkHttpManager {
 
    private static OkHttpManager mOkHttpManager;
 
    private OkHttpClient mOkHttpClient;
 
    private Gson mGson;
 
    private Handler handler;
 
    private OkHttpManager() {
        mOkHttpClient = new OkHttpClient();
        mOkHttpClient.newBuilder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS);
        mGson = new Gson();
        handler = new Handler(Looper.getMainLooper());
    }
 
    //創(chuàng)建 單例模式(OkHttp官方建議如此操作)
    public static OkHttpManager getInstance() {
        if (mOkHttpManager == null) {
            mOkHttpManager = new OkHttpManager();
        }
        return mOkHttpManager;
    }
 
    /***********************
     * 對外公布的可調(diào)方法
     ************************/
 
    public void getRequest(String url, final BaseCallBack callBack) {
        Request request = buildRequest(url, null, HttpMethodType.GET);
        doRequest(request, callBack);
    }
 
    public void postRequest(String url, final BaseCallBack callBack, Map<String, String> params) {
        Request request = buildRequest(url, params, HttpMethodType.POST);
        doRequest(request, callBack);
    }
 
    public void postUploadSingleImage(String url, final BaseCallBack callback, File file, String fileKey, Map<String, String> params) {
        Param[] paramsArr = fromMapToParams(params);
 
        try {
            postAsyn(url, callback, file, fileKey, paramsArr);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
    }
 
    public void postUploadMoreImages(String url, final BaseCallBack callback, File[] files, String[] fileKeys, Map<String, String> params) {
        Param[] paramsArr = fromMapToParams(params);
 
        try {
            postAsyn(url, callback, files, fileKeys, paramsArr);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
    }
 
    /***********************
     * 對內(nèi)方法
     ************************/
    //單個(gè)文件上傳請求  不帶參數(shù)
    private void postAsyn(String url, BaseCallBack callback, File file, String fileKey) throws IOException {
        Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, null);
        doRequest(request, callback);
    }
 
    //單個(gè)文件上傳請求 帶參數(shù)
    private void postAsyn(String url, BaseCallBack callback, File file, String fileKey, Param... params) throws IOException {
        Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, params);
        doRequest(request, callback);
    }
 
    //多個(gè)文件上傳請求 帶參數(shù)
    private void postAsyn(String url, BaseCallBack callback, File[] files, String[] fileKeys, Param... params) throws IOException {
        Request request = buildMultipartFormRequest(url, files, fileKeys, params);
        doRequest(request, callback);
    }
 
    //異步下載文件
    public void asynDownloadFile(final String url, final String destFileDir, final BaseCallBack callBack) {
        final Request request = buildRequest(url, null, HttpMethodType.GET);
        callBack.OnRequestBefore(request);  //提示加載框
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callBack.onFailure(call, e);
            }
 
            @Override
            public void onResponse(Call call, Response response) throws IOException {
//                callBack.onResponse(response);
 
                InputStream is = null;
                byte[] buf = new byte[1024*2];
                final long fileLength = response.body().contentLength();
                int len = 0;
                long readLength = 0;
                FileOutputStream fos = null;
                try {
                    is = response.body().byteStream();
                    File file = new File(destFileDir, getFileName(url));
                    fos = new FileOutputStream(file);
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                        readLength += len;
                        int curProgress = (int) (((float) readLength / fileLength) * 100);
                        Log.e("lgz", "onResponse: >>>>>>>>>>>>>" + curProgress + ", readLength = " + readLength + ", fileLength = " + fileLength);
                        callBack.inProgress(curProgress, fileLength, 0);
                    }
                    fos.flush();
                    //如果下載文件成功垦梆,第一個(gè)參數(shù)為文件的絕對路徑
                    callBackSuccess(callBack, call, response, file.getAbsolutePath());
                } catch (IOException e) {
                    callBackError(callBack, call, response.code());
                } finally {
                    try {
                        if (is != null)
                            is.close();
                    } catch (IOException e) {
                    }
                    try {
                        if (fos != null)
                            fos.close();
                    } catch (IOException e) {
                    }
                }
            }
        });
 
 
    }
 
    //構(gòu)造上傳圖片 Request
    private Request buildMultipartFormRequest(String url, File[] files, String[] fileKeys, Param[] params) {
        params = validateParam(params);
        MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        for (Param param : params) {
            builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + param.key + "\""),
                    RequestBody.create(MediaType.parse("image/png"), param.value));
        }
        if (files != null) {
            RequestBody fileBody = null;
            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                String fileName = file.getName();
                fileBody = RequestBody.create(MediaType.parse(guessMimeType(fileName)), file);
                //TODO 根據(jù)文件名設(shè)置contentType
                builder.addPart(Headers.of("Content-Disposition",
                        "form-data; name=\"" + fileKeys[i] + "\"; filename=\"" + fileName + "\""),
                        fileBody);
            }
        }
        RequestBody requestBody = builder.build();
        return new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
    }
    //Activity頁面所有的請求以Activity對象作為tag匹颤,可以在onDestory()里面統(tǒng)一取消,this
    public void cancelTag(Object tag) {
        for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
        for (Call call : mOkHttpClient.dispatcher().runningCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
    }
    private String guessMimeType(String path) {
        FileNameMap fileNameMap = URLConnection.getFileNameMap();
        String contentTypeFor = fileNameMap.getContentTypeFor(path);
        if (contentTypeFor == null) {
            contentTypeFor = "application/octet-stream";
        }
        return contentTypeFor;
    }
    private String getFileName(String path) {
        int separatorIndex = path.lastIndexOf("/");
        return (separatorIndex < 0) ? path : path.substring(separatorIndex + 1, path.length());
    }
    private Param[] fromMapToParams(Map<String, String> params) {
        if (params == null)
            return new Param[0];
        int size = params.size();
        Param[] res = new Param[size];
        Set<Map.Entry<String, String>> entries = params.entrySet();
        int i = 0;
        for (Map.Entry<String, String> entry : entries) {
            res[i++] = new Param(entry.getKey(), entry.getValue());
        }
        return res;
    }
 
    //去進(jìn)行網(wǎng)絡(luò) 異步 請求
    private void doRequest(Request request, final BaseCallBack callBack) {
        callBack.OnRequestBefore(request);
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                callBack.onFailure(call, e);
            }
 
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                callBack.onResponse(response);
                String result = response.body().string();
                if (response.isSuccessful()) {
 
                    if (callBack.mType == String.class) {
//                        callBack.onSuccess(call, response, result);
                        callBackSuccess(callBack, call, response, result);
                    } else {
                        try {
                            Object object = mGson.fromJson(result, callBack.mType);//自動(dòng)轉(zhuǎn)化為 泛型對象
//                            callBack.onSuccess(call, response, object);
                            callBackSuccess(callBack, call, response, object);
                        } catch (JsonParseException e) {
                            //json解析錯(cuò)誤時(shí)調(diào)用
                            callBack.onEror(call, response.code(), e);
                        }
 
                    }
                } else {
                    callBack.onEror(call, response.code(), null);
                }
 
            }
 
        });
 
 
    }
 
    //創(chuàng)建 Request對象
    private Request buildRequest(String url, Map<String, String> params, HttpMethodType methodType) {
 
        Request.Builder builder = new Request.Builder();
        builder.url(url);
        if (methodType == HttpMethodType.GET) {
            builder.get();
        } else if (methodType == HttpMethodType.POST) {
            RequestBody requestBody = buildFormData(params);
            builder.post(requestBody);
        }
        return builder.build();
    }
 
    //構(gòu)建請求所需的參數(shù)表單
    private RequestBody buildFormData(Map<String, String> params) {
        FormBody.Builder builder = new FormBody.Builder();
        builder.add("platform", "android");
        builder.add("version", "1.0");
        builder.add("key", "123456");
        if (params != null) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                builder.add(entry.getKey(), entry.getValue());
            }
        }
        return builder.build();
    }
 
    private void callBackSuccess(final BaseCallBack callBack, final Call call, final Response response, final Object object) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                callBack.onSuccess(call, response, object);
            }
        });
 
    }
 
    private void callBackError(final BaseCallBack callBack, final Call call, final int code) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                callBack.onEror(call, code, null);
            }
        });
 
    }
 
    private Param[] validateParam(Param[] params) {
        if (params == null)
            return new Param[0];
        else
            return params;
    }
 
    public static class Param {
        public Param() {
        }
 
        public Param(String key, String value) {
            this.key = key;
            this.value = value;
        }
 
        String key;
        String value;
    }
 
    enum HttpMethodType {
        GET, POST
    }
 
 
}

其中的 BaseCallBack回調(diào)機(jī)制封裝如下:

public abstract class BaseCallBack<T> {
    public Type mType;
 
    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]);
    }
 
 
    public BaseCallBack() {
        mType = getSuperclassTypeParameter(getClass());
    }
 
    protected abstract void OnRequestBefore(Request request);
 
    protected abstract void onFailure(Call call, IOException e);
 
    protected abstract void onSuccess(Call call, Response response, T t);
 
    protected abstract void onResponse(Response response);
 
    protected abstract void onEror(Call call, int statusCode, Exception e);
 
    protected abstract void inProgress(int progress, long total , int id);
}

上面這個(gè)類OkHttpManager 是我根據(jù)網(wǎng)絡(luò)上各家資源學(xué)習(xí)封裝好的,copy進(jìn)代碼可以直接使用托猩,并且根據(jù)okhttp3.0以后的版本惋嚎,對之前的一下請求參數(shù)設(shè)置進(jìn)行了最新的修改,具體如下:

1站刑、設(shè)置請求超時(shí)參數(shù);

okhttp3.0以前的版本是這樣設(shè)置的

new OkHttpClient();

mHttpClient.setConnectTimeout(10, TimeUnit.SECONDS);

mHttpClient.setReadTimeout(10,TimeUnit.SECONDS);

mHttpClient.setWriteTimeout(30,TimeUnit.SECONDS);

之后的版本是這樣設(shè)置的:

new OkHttpClient.Builder()
.readTimeout(READ_TIMEOUT,TimeUnit.SECONDS)//設(shè)置讀取超時(shí)時(shí)間
.writeTimeout(WRITE_TIMEOUT,TimeUnit.SECONDS)//設(shè)置寫的超時(shí)時(shí)間
.connectTimeout(CONNECT_TIMEOUT,TimeUnit.SECONDS)//設(shè)置連接超時(shí)時(shí)間

2鼻百、post方式請求時(shí)绞旅,構(gòu)建表單對象參數(shù);

okhttp3.0以前的版本是這樣構(gòu)建的:new FormEncodingBuilder()對象温艇,然后向里面add (key,value)參數(shù)因悲;

之后的版本更改為:FormBody body = new FormBody.Builder(),.add(key, value)勺爱;即是FormEncodingBuilder已被FormBody取代晃琳;

至于BaseCallBack類,根據(jù)請求數(shù)據(jù)的功能的不同琐鲁,還需要對此進(jìn)行封裝卫旱,集成自己需要的方法實(shí)現(xiàn);

一围段、進(jìn)行一般的數(shù)據(jù)加載請求顾翼,可直接調(diào)用如下:

模擬用戶登錄:

Map<String, String> params = new HashMap<String, String>();
                    params.put("Mobile", username.getText().toString());
                    params.put("PassWord", password.getText().toString());
 
                    OkHttpManager.getInstance().postRequest(Constants.LOGIN_URL, new LoadCallBack<String>(getActivity()) {
                                @Override
                                protected void onSuccess(Call call, Response response, String s) {
                                    Log.e("lgz", "onSuccess = " + s);
                                    Toast.makeText(getActivity(), "登錄成功!", Toast.LENGTH_LONG).show();
                                }
 
                                @Override
                                protected void onEror(Call call, int statusCode, Exception e) {
                                    Log.e("lgz", "Exception = " + e.toString());
                                }
                            }
                            , params);

上面登錄請求中 就用到了自己根據(jù)需要再次封裝的Callback類的繼承實(shí)現(xiàn)奈泪,LoadCallBack<T>類:

//添加對請求時(shí)對話框的處理
public abstract class LoadCallBack<T> extends BaseCallBack<T> {
    private Context context;
    private SpotsDialog spotsDialog;
 
    public LoadCallBack(Context context) {
        this.context = context;
        spotsDialog = new SpotsDialog(context);
    }
 
    private void showDialog() {
        spotsDialog.show();
    }
 
    private void hideDialog() {
        if (spotsDialog != null) {
            spotsDialog.dismiss();
        }
    }
 
    public void setMsg(String str) {
        spotsDialog.setMessage(str);
    }
 
    public void setMsg(int  resId) {
        spotsDialog.setMessage(context.getString(resId));
    }
 
 
    @Override
    protected void OnRequestBefore(Request request) {
        showDialog();
 
    }
 
    @Override
    protected void onFailure(Call call, IOException e) {
        hideDialog();
    }
 
    @Override
    protected void onResponse(Response response) {
        hideDialog();
    }
 
    @Override
    protected void inProgress(int progress, long total, int id) {
 
    }

其實(shí)這個(gè)類就是對BaseCallBack再次繼承實(shí)現(xiàn)适贸;
二灸芳、下載文件,并顯示進(jìn)度條對話框的請求操作:

下載一張圖片:


OkHttpManager.getInstance().asynDownloadFile("http://www.7mlzg.com/uploads/bwf_1477419976.jpg", FILE_PATH, new FileCallBack<String>(getActivity()) {
                    @Override
                    protected void onResponse(Response response) {
 
                    }
 
                    @Override
                    protected void onSuccess(Call call, Response response, String s) {
                        super.onSuccess(call, response, s);
                        Log.e("lgz", "status = : " + s);
                        Toast.makeText(getActivity(), "下載成功", Toast.LENGTH_LONG).show();
                        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                        Uri uri = Uri.fromFile(new File(s));//廣播通知系統(tǒng)圖集更新
                        intent.setData(uri);
                        getActivity().sendBroadcast(intent);
                    }
                });

上面下載圖片中拜姿,就用到了自己根據(jù)需要再次繼承Callback類封裝得到的FileCallBack<T>類如下:

public abstract class FileCallBack<T> extends BaseCallBack<T> {
 
    private Context mContext;
 
    private ProgressDialog mProgressDialog;
 
    public FileCallBack(Context context) {
        mContext = context;
        initDialog();
    }
 
    private void initDialog(){
        mProgressDialog = new ProgressDialog(mContext);
        mProgressDialog.setTitle("下載中...");
        mProgressDialog.setCanceledOnTouchOutside(true);
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setMax(100);
    }
 
    private void hideDialog() {
        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
        }
    }
 
    @Override
    protected void OnRequestBefore(Request request) {
        mProgressDialog.show();
    }
 
    @Override
    protected void onFailure(Call call, IOException e) {
       hideDialog();
    }
 
    @Override
    protected void onSuccess(Call call, Response response, T t) {
        Log.e("lgz", "onSuccess: >>>>>>>>>>>>>");
        hideDialog();
    }
 
    @Override
    protected void onEror(Call call, int statusCode, Exception e) {
        hideDialog();
    }
 
    @Override
    protected void inProgress(int progress, long total, int id) {
        Log.e("lgz", "inProgress: >>>>>>>>>>>>>"+progress);
        mProgressDialog.setProgress(progress);
 
    }
}

三烙样、最后說一下使用okhttp的配置:
在Android Studio 中,直接在build.gradle文件里配置 :compile 'com.squareup.okhttp3:okhttp:3.4.1'

在Eclipse里需要導(dǎo)入jar包使用蕊肥,下載最新jar谒获;

當(dāng)然了,這里只是對Okhttp常用的一些功能進(jìn)行了封裝處理晴埂,使用的都是異步請求方式究反,至于同步操作,我個(gè)人覺得不是很常用儒洛,使用時(shí)需要開啟一個(gè)線程精耐,不然會(huì)阻塞UI線程的;最后琅锻,這只是一個(gè)簡單的學(xué)習(xí)卦停,其中要是有不足和錯(cuò)誤,還希望大家留言批評(píng)指正恼蓬,謝謝惊完!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市处硬,隨后出現(xiàn)的幾起案子小槐,更是在濱河造成了極大的恐慌,老刑警劉巖荷辕,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凿跳,死亡現(xiàn)場離奇詭異,居然都是意外死亡疮方,警方通過查閱死者的電腦和手機(jī)控嗜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骡显,“玉大人疆栏,你說我怎么就攤上這事巫员∷Ь颍” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵诺核,是天一觀的道長溜歪。 經(jīng)常有香客問我博助,道長,這世上最難降的妖魔是什么痹愚? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任富岳,我火速辦了婚禮蛔糯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘窖式。我一直安慰自己蚁飒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布萝喘。 她就那樣靜靜地躺著淮逻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪阁簸。 梳的紋絲不亂的頭發(fā)上爬早,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音启妹,去河邊找鬼筛严。 笑死,一個(gè)胖子當(dāng)著我的面吹牛饶米,可吹牛的內(nèi)容都是我干的桨啃。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼檬输,長吁一口氣:“原來是場噩夢啊……” “哼照瘾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起丧慈,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤析命,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后逃默,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹃愤,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年笑旺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片馍资。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筒主,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鸟蟹,到底是詐尸還是另有隱情乌妙,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布建钥,位于F島的核電站藤韵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏熊经。R本人自食惡果不足惜泽艘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一欲险、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧匹涮,春花似錦天试、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至雳攘,卻和暖如春带兜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吨灭。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國打工刚照, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沃于。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓涩咖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親繁莹。 傳聞我的和親對象是個(gè)殘疾皇子檩互,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容