接口回調(diào)冒嫡,OkHttp基本使用與封裝

接口回調(diào)

在對(duì)象中,有些事件不確定什么時(shí)候能完成寨躁,此時(shí)可以通過(guò)預(yù)留監(jiān)督機(jī)制,關(guān)注事件的變化,這個(gè)機(jī)制即回調(diào)。

舉個(gè)例子
模擬下載器腋,實(shí)施監(jiān)督下載的進(jìn)度翎冲,當(dāng)100%時(shí)赏壹,提醒下載完成

1.  建立一個(gè)下載進(jìn)度的類
    需要監(jiān)督加載的進(jìn)度,因此方法內(nèi)需要一個(gè)監(jiān)督進(jìn)度的形參接口
class Progress {

    public void loadProgress(IProgressListener progressListener) {
        //模擬進(jìn)度
        for (int i = 1; i <= 100; i++) {
            //進(jìn)度每改變一次,即調(diào)用更新一次監(jiān)督的數(shù)據(jù)
            progressListener.curProgress(i);
        }
    }
}


2.  建立監(jiān)聽(tīng)的回調(diào)接口数苫,用來(lái)監(jiān)聽(tīng)進(jìn)度
interface IProgressListener {
    void curProgress(int curProgress);
}


3.運(yùn)行調(diào)用
  實(shí)際還是對(duì)多態(tài)的運(yùn)用
  當(dāng)Progress類調(diào)用loadProgress(IProgressListener progressListener)時(shí)
  傳入的實(shí)參是一個(gè)匿名內(nèi)部類谈山,也是IProgressListener的子類
  相當(dāng)于 IProgressListener progressListener = new 子類();
  此時(shí)調(diào)用的是子類的實(shí)現(xiàn)斜脂。

public class Main {

    public static void main(String[] args) {
        Progress progress = new Progress();

        progress.loadProgress(new IProgressListener() {
            @Override
            public void curProgress(int curProgress) {
                System.out.printf("curProgress:" + curProgress + "\r\n");
                if (curProgress == 100)
                    System.out.printf("下載完成");
            }
        });
    }
}

OkHttp
Android網(wǎng)絡(luò)請(qǐng)求的變遷
OkHttp的基本用法
前置工作
1.  添加依賴
OkHttp依賴:implementation 'com.squareup.okhttp3:okhttp:3.11.0'
Gson依賴:implementation 'com.google.code.gson:gson:2.8.5'

2.  Android在做網(wǎng)絡(luò)請(qǐng)求時(shí)要添加權(quán)限
<uses-permission android:name="android.permission.INTERNET" />
注意
Android P 限制了明文流量的網(wǎng)絡(luò)請(qǐng)求涝动,非加密的流量請(qǐng)求都會(huì)被系統(tǒng)禁止鼻吮。
如果 WebView 的 url 用 http 協(xié)議,同樣會(huì)出現(xiàn)加載失敗肥照,https 不受影響。

如果當(dāng)前應(yīng)用的請(qǐng)求是 htttp 而非 https梧税,系統(tǒng)會(huì)禁止當(dāng)前應(yīng)用進(jìn)行該請(qǐng)求缓醋。
在 Android P 版本如果使用了明文流量褪贵,OkHttp3會(huì)拋出
CLEARTEXT communication to " + host + " not permitted by network security policy異常

解決方案
服務(wù)器和客戶端的請(qǐng)求最好都用https
OkHttp提供的API

Get請(qǐng)求
1.  首先創(chuàng)建OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();


2.  發(fā)送請(qǐng)求需要構(gòu)建Request對(duì)象
Request request = new Request.Builder()
                .url(requestUrl())
                .build();


3.  okHttpClient 調(diào)用newCall()創(chuàng)建一個(gè)Call對(duì)象橄镜,并調(diào)用enqueue()
發(fā)送請(qǐng)求并獲取服務(wù)器返回的數(shù)據(jù)姊氓。
okHttpClient.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {

        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {

            if (response.isSuccessful()) {
                String resJson = response.body().string();
                Gson gson = new Gson();
                User user = gson.fromJson(resJson, User.class);
            }
        }
});


Post請(qǐng)求
1. 需要構(gòu)建一個(gè)RequestBody對(duì)象來(lái)存放提交的參數(shù)
RequestBody body = new FormBody.Builder()
                .add("userName", getUserName())
                .add("userCode", getUserCode())
                .build();


2.  將body 添加到Request.Builder中
Request request = new Request.Builder()
                .url(requestUrl())
                .post(body)
                .build();
舉個(gè)例子
客戶端向服務(wù)器發(fā)送用戶輸入的用戶名瘦锹,用戶編號(hào)
服務(wù)端返回用戶的信息


1.  服務(wù)端
Module類
public class User {
    String name;
    int code;
    String info;
}


@WebServlet(value = "/user")
public class UserServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");

        Gson gson = new Gson();
        String userName = req.getParameter("userName");
        String userCode = req.getParameter("userCode");

        if (userName.equals("admin") && userCode.equals("123")) {
            User user = new User("admin", 1, "演示信息");
            resp.getWriter().println(gson.toJson(user));
        }
    }
}
2.  客戶端

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    private EditText mEtUserName;
    private EditText mEtUserCode;
    private Button mBtnQuery;
    private TextView mTvShowUserInfo;

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    User user = (User) msg.obj;
                    showUserInfo(user);
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initEvent();
    }

    private void initEvent() {
        mBtnQuery.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getUserInfo();
            }
        });
    }

    //網(wǎng)絡(luò)請(qǐng)求
    private void getUserInfo() {
        OkHttpClient okHttpClient = new OkHttpClient();
        RequestBody body = new FormBody.Builder()
                .add("userName", getUserName())
                .add("userCode", getUserCode())
                .build();

        final Request request = new Request.Builder()
                .url(requestUrl())
                .post(body)
                .build();

        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

                if (response.isSuccessful()) {
                    String resJson = response.body().string();
                    Gson gson = new Gson();
                    User user = gson.fromJson(resJson, User.class);
                    Message message = Message.obtain();
                    message.what = 1;
                    message.obj = user;
                    mHandler.sendMessage(message);
                }
            }
        });
    }

    private void initView() {
        mEtUserName = findViewById(R.id.et_user_name);
        mEtUserCode = findViewById(R.id.et_user_code);
        mBtnQuery = findViewById(R.id.btn_query_data);
        mTvShowUserInfo = findViewById(R.id.tv_user_info_show);
    }

    private String getUserName() {
        return mEtUserName.getText().toString();
    }

    private String getUserCode() {
        return mEtUserCode.getText().toString();
    }

    private void showUserInfo(User user) {
        StringBuilder builder = new StringBuilder();
        builder.append("UserName:" + user.getName())
                .append("\r\n")
                .append("UserCode:" + user.getCode())
                .append("\r\n")
                .append("UserInfo:" + user.getInfo());
        mTvShowUserInfo.setText(builder.toString());
    }

    private String requestUrl() {
        return "http://192.168.1.104:8080/user";
    }
}
OkHttp封裝
思路
通過(guò)觀察OkHttp贴妻,不難發(fā)現(xiàn)所有的請(qǐng)求都是通過(guò)Request這個(gè)類構(gòu)建
為了保證不浪費(fèi)資源,將OkHttp設(shè)置為單例模式蝙斜。

1.  用枚舉區(qū)分Get名惩,Post,設(shè)置對(duì)外調(diào)用方法孕荠,獲取網(wǎng)絡(luò)請(qǐng)求
    傳遞參數(shù)
      1.請(qǐng)求的路徑
      2.post請(qǐng)求時(shí)傳遞的參數(shù)
      3.一個(gè)用于監(jiān)測(cè)請(qǐng)求完成的回調(diào)類

2.  定義OkHttp的請(qǐng)求doRequest()方法娩鹉,用于監(jiān)聽(tīng)請(qǐng)求成功失敗攻谁,請(qǐng)求前的狀態(tài)
    傳遞參數(shù)
      1.構(gòu)建的Request類
      2.監(jiān)聽(tīng)回調(diào)類

3.  所有請(qǐng)求都用異步在子線程操作,修改UI需要Handler發(fā)送到主線程
這就需要根據(jù)成功失敗的回調(diào)弯予,創(chuàng)建發(fā)送的Handler方法

4.  定義回調(diào)類
    1.請(qǐng)求成功
    2.請(qǐng)求失敗

5.  設(shè)置回調(diào)的包裝類戚宦,幫助簡(jiǎn)化構(gòu)建不必要的方法
1.  設(shè)置回調(diào)類

public abstract class IBaseCallBack<T> {

    public Type mType;

    public IBaseCallBack() {
        mType = getSuperclassTypeParameter(getClass());
    }

    /**
     * 將Type類型轉(zhuǎn)換成Gson,解析
     */
    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]);
    }

    //請(qǐng)求成功前回調(diào)
    public abstract void onRequestBefore();

    //網(wǎng)絡(luò)請(qǐng)求成功后根據(jù)返回碼調(diào)用
    public abstract void onSuccess(Response response, T t);

    //請(qǐng)求失敗時(shí)
    public abstract void onFailure(Call call, Exception e);
}
2.  將OkHttp封裝

public class OkHttpHelper {

    private static OkHttpHelper mOkHttpHelper;
    private static OkHttpClient mOkHttpClient;

    private static Gson mGson;
    private Handler mHandler;

    private OkHttpHelper() {
        mOkHttpClient = new OkHttpClient();
        mGson = new Gson();

        //每發(fā)現(xiàn)一條消息就推送到Handler
        mHandler = new Handler(Looper.getMainLooper());
    }

    public static OkHttpHelper getOkHttpHelperInstance() {
        if (mOkHttpHelper == null) {
            synchronized (OkHttpHelper.class) {
                if (mOkHttpHelper == null) {
                    mOkHttpHelper = new OkHttpHelper();
                }
            }
        }
        return mOkHttpHelper;
    }

    //來(lái)區(qū)別請(qǐng)求的類型
    enum HttpMethodType {
        GET, POST
    }

    /**
     * 對(duì)外調(diào)用的Get锈嫩,Post請(qǐng)求
     */
    public void get(String url, IBaseCallBack callBack) {
        Request request = buildRequest(
                url,
                null,
                HttpMethodType.GET);

        doRequest(request, callBack);
    }

    public void post(String url, Map<String, String> parameter,
                     IBaseCallBack callBack) {

        Request request = buildRequest(
                url,
                parameter,
                HttpMethodType.POST);

        doRequest(request, callBack);
    }

    /**
     * 構(gòu)建Get或Post請(qǐng)求的Request
     *
     * @param url        Url地址
     * @param parameter  Post請(qǐng)求所需要的參數(shù)
     * @param methodType 通過(guò)枚舉來(lái)判斷是Get還是Post
     * @return Request
     */
    private Request buildRequest(String url,
                                 Map<String, String> parameter,
                                 HttpMethodType methodType) {

        Request.Builder builder = new Request.Builder();
        builder.url(url);
        if (methodType == HttpMethodType.GET) {
            builder.get();
        } else {
            //構(gòu)建參數(shù)獲取
            RequestBody body = buildFormData(parameter);
            builder.post(body);
        }
        return builder.build();
    }

    /**
     * 構(gòu)建Post請(qǐng)求的body
     */
    private RequestBody buildFormData(Map<String, String> parameter) {
        FormBody.Builder body = new FormBody.Builder();

        if (null != parameter) {

            for (Map.Entry<String, String> entry : parameter.entrySet()) {

                body.add(entry.getKey(), entry.getValue());
            }
        }
        return body.build();
    }


    /**
     * 通過(guò)構(gòu)建好的Get或Post請(qǐng)求的Request受楼,做網(wǎng)絡(luò)訪問(wèn)
     */
    private void doRequest(final Request request, final IBaseCallBack callBack) {

        callBack.onRequestBefore();

        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                requestFailure(callBack, call, e);
            }

            @Override
            public void onResponse(Call call, Response response)
                    throws IOException {

                if (response.isSuccessful()) {
                    String resultStr = response.body().string();

                    if (callBack.mType == String.class) {

                        requestSuccess(callBack, response, request);

                    } else {

                        try {
                            //防止Json解析錯(cuò)誤
                            Object obj = mGson.fromJson(resultStr, callBack.mType);

                            requestSuccess(callBack, response, obj);

                        } catch (JsonParseException e) {
                            requestFailure(callBack, call, e);
                        }
                    }
                } else {
                    requestFailure(callBack, call, null);
                }
            }
        });
    }

    private void requestFailure(final IBaseCallBack callBack,
                                final Call call,
                                final Exception e) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callBack.onFailure(call, e);
            }
        });
    }

    private void requestSuccess(final IBaseCallBack callBack,
                                final Response response,
                                final Object o) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                callBack.onSuccess(response, o);
            }
        });
    }
}
3.  設(shè)置接口的包裝類,幫助簡(jiǎn)化不必要的實(shí)現(xiàn)接口或者添加其他方法
注意:兩者同樣是抽象類呼寸,SpotsCallBack重寫(xiě)的抽象方法艳汽,在其子類是不會(huì)主動(dòng)提示覆蓋的

public abstract class SpotsCallBack<T> extends IBaseCallBack<T> {

    ProgressDialog mDialog;

    public SpotsCallBack(Context context) {
        mDialog = new ProgressDialog(context);
        mDialog.setCancelable(false);
        mDialog.setCanceledOnTouchOutside(false);
    }

    public void showDialog() {
        mDialog.show();
    }

    public void dismissDialog() {
        mDialog.dismiss();
    }

    @Override
    public void onRequestBefore() {
        showDialog();
    }
}
具體調(diào)用

private void getUserInfo() {

        HashMap<String, String> paramester = new HashMap<>();
        paramester.put("userName", getUserName());
        paramester.put("userCode", getUserCode());

        OkHttpHelper.getOkHttpHelperInstance()
                .post(requestUrl(), paramester, new SpotsCallBack<User>(this) {
                    @Override
                    public void onSuccess(Response response, User user) {
                        dismissDialog();
                        showUserInfo(user);
                    }

                    @Override
                    public void onFailure(Call call, Exception e) {
                        dismissDialog();
                    }
          });
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市对雪,隨后出現(xiàn)的幾起案子河狐,更是在濱河造成了極大的恐慌,老刑警劉巖瑟捣,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件馋艺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蝶柿,警方通過(guò)查閱死者的電腦和手機(jī)丈钙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)交汤,“玉大人雏赦,你說(shuō)我怎么就攤上這事≤皆” “怎么了星岗?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)戒洼。 經(jīng)常有香客問(wèn)我俏橘,道長(zhǎng),這世上最難降的妖魔是什么圈浇? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任寥掐,我火速辦了婚禮,結(jié)果婚禮上磷蜀,老公的妹妹穿的比我還像新娘召耘。我一直安慰自己,他們只是感情好褐隆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布污它。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪衫贬。 梳的紋絲不亂的頭發(fā)上德澈,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音固惯,去河邊找鬼梆造。 笑死,一個(gè)胖子當(dāng)著我的面吹牛缝呕,可吹牛的內(nèi)容都是我干的澳窑。 我是一名探鬼主播斧散,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼供常,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鸡捐?” 一聲冷哼從身側(cè)響起栈暇,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎箍镜,沒(méi)想到半個(gè)月后源祈,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡色迂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年香缺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歇僧。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡图张,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诈悍,到底是詐尸還是另有隱情祸轮,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布侥钳,位于F島的核電站适袜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏舷夺。R本人自食惡果不足惜苦酱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望给猾。 院中可真熱鬧疫萤,春花似錦、人聲如沸耙册。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至帝际,卻和暖如春蔓同,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蹲诀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工斑粱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脯爪。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓则北,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親痕慢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尚揣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,515評(píng)論 25 707
  • 關(guān)于okhttp是一款優(yōu)秀的網(wǎng)絡(luò)請(qǐng)求框架,關(guān)于它的源碼分析文章有很多掖举,這里分享我在學(xué)習(xí)過(guò)程中讀到的感覺(jué)比較好的文章...
    蕉下孤客閱讀 3,596評(píng)論 2 38
  • Okhttp使用指南與源碼分析 標(biāo)簽(空格分隔): Android 使用指南篇# 為什么使用okhttp### A...
    背影殺手不太冷閱讀 8,143評(píng)論 2 119
  • 參考Android網(wǎng)絡(luò)請(qǐng)求心路歷程Android Http接地氣網(wǎng)絡(luò)請(qǐng)求(HttpURLConnection) 一...
    合肥黑閱讀 21,234評(píng)論 7 63
  • 1:感恩天地滋養(yǎng)萬(wàn)物快骗,感恩列祖列宗護(hù)佑,感恩父母給予生命塔次,感恩國(guó)泰民安方篮。感恩金錢(qián)富足,感恩時(shí)間自由励负。 2:感恩公公...
    吉秀wang閱讀 130評(píng)論 0 1