接口回調,OkHttp基本使用與封裝

接口回調

在對象中,有些事件不確定什么時候能完成,此時可以通過預留監督機制,關注事件的變化,這個機制即回調。

舉個例子
模擬下載,實施監督下載的進度,當100%時,提醒下載完成

1.  建立一個下載進度的類
    需要監督加載的進度,因此方法內需要一個監督進度的形參接口
class Progress {

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


2.  建立監聽的回調接口,用來監聽進度
interface IProgressListener {
    void curProgress(int curProgress);
}


3.運行調用
  實際還是對多態的運用
  當Progress類調用loadProgress(IProgressListener progressListener)時
  傳入的實參是一個匿名內部類,也是IProgressListener的子類
  相當于 IProgressListener progressListener = new 子類();
  此時調用的是子類的實現。

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網絡請求的變遷
OkHttp的基本用法
前置工作
1.  添加依賴
OkHttp依賴:implementation 'com.squareup.okhttp3:okhttp:3.11.0'
Gson依賴:implementation 'com.google.code.gson:gson:2.8.5'

2.  Android在做網絡請求時要添加權限
<uses-permission android:name="android.permission.INTERNET" />
注意
Android P 限制了明文流量的網絡請求,非加密的流量請求都會被系統禁止。
如果 WebView 的 url 用 http 協議,同樣會出現加載失敗,https 不受影響。

如果當前應用的請求是 htttp 而非 https,系統會禁止當前應用進行該請求。
在 Android P 版本如果使用了明文流量,OkHttp3會拋出
CLEARTEXT communication to " + host + " not permitted by network security policy異常

解決方案
服務器和客戶端的請求最好都用https
OkHttp提供的API

Get請求
1.  首先創建OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();


2.  發送請求需要構建Request對象
Request request = new Request.Builder()
                .url(requestUrl())
                .build();


3.  okHttpClient 調用newCall()創建一個Call對象,并調用enqueue()
發送請求并獲取服務器返回的數據。
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請求
1. 需要構建一個RequestBody對象來存放提交的參數
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();
舉個例子
客戶端向服務器發送用戶輸入的用戶名,用戶編號
服務端返回用戶的信息


1.  服務端
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();
            }
        });
    }

    //網絡請求
    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封裝
思路
通過觀察OkHttp,不難發現所有的請求都是通過Request這個類構建
為了保證不浪費資源,將OkHttp設置為單例模式。

1.  用枚舉區分Get,Post,設置對外調用方法,獲取網絡請求
    傳遞參數
      1.請求的路徑
      2.post請求時傳遞的參數
      3.一個用于監測請求完成的回調類

2.  定義OkHttp的請求doRequest()方法,用于監聽請求成功失敗,請求前的狀態
    傳遞參數
      1.構建的Request類
      2.監聽回調類

3.  所有請求都用異步在子線程操作,修改UI需要Handler發送到主線程
這就需要根據成功失敗的回調,創建發送的Handler方法

4.  定義回調類
    1.請求成功
    2.請求失敗

5.  設置回調的包裝類,幫助簡化構建不必要的方法
1.  設置回調類

public abstract class IBaseCallBack<T> {

    public Type mType;

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

    /**
     * 將Type類型轉換成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]);
    }

    //請求成功前回調
    public abstract void onRequestBefore();

    //網絡請求成功后根據返回碼調用
    public abstract void onSuccess(Response response, T t);

    //請求失敗時
    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();

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

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

    //來區別請求的類型
    enum HttpMethodType {
        GET, POST
    }

    /**
     * 對外調用的Get,Post請求
     */
    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);
    }

    /**
     * 構建Get或Post請求的Request
     *
     * @param url        Url地址
     * @param parameter  Post請求所需要的參數
     * @param methodType 通過枚舉來判斷是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 {
            //構建參數獲取
            RequestBody body = buildFormData(parameter);
            builder.post(body);
        }
        return builder.build();
    }

    /**
     * 構建Post請求的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();
    }


    /**
     * 通過構建好的Get或Post請求的Request,做網絡訪問
     */
    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解析錯誤
                            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.  設置接口的包裝類,幫助簡化不必要的實現接口或者添加其他方法
注意:兩者同樣是抽象類,SpotsCallBack重寫的抽象方法,在其子類是不會主動提示覆蓋的

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();
    }
}
具體調用

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();
                    }
          });
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容