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

推荐阅读更多精彩内容