Login项目学习笔记【Android】

之前一直是单客户端开发,在学习了JavaEE和几个主流框架以后尝试想给课题项目的数据库上个服务器,但是并不知道客户端和服务器的数据交互流程和web有啥异同,所以看博主的demo学习一下。

1.单客户端登录https://blog.csdn.net/midnight_time/article/details/80792255

安卓开发个各位小伙伴,或多或少的都会用到数据库框架。为了帮助支持各位开发者,google推出了自己的数据库框架Room。

官方介绍:The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.
翻译过来就是,Room持久型类库在SQLite的基础上提供了一个抽象层,方便大家流利的访问数据库。并且,利用了SQlite的全部强力功能。

如果用SQLite数据库的话:https://www.jianshu.com/p/72c8efc3ad87

2.前后端分离https://blog.csdn.net/midnight_time/article/details/91203973

 打开成功了。

1.夜神模拟器访问本地tomcat配置的项目需要在android端使用本地电脑ip(WIFI路由为电脑分配的ip)

2.数据库表名和列名得和mapper里的表名列名对应。

后端就是秒杀项目基础2,3章没什么区别,前端就是web换成客户端的区别。客户端内部重要的其实就和html上要写的一样,要有连接服务器ip的网络操作去访问controller里面的方法和拿到服务器响应的数据。

---------------------------------------------------------------------------------------------

重点学习Android端中:

1.okhttp异步发送POST请求

这次大动干戈的将前后端进行分离,主要就是依靠像okhttp这样的框架来实现的。

因为请求URL属于耗时操作,所以开一个线程,避免耗时操作在子线程中进行

okhttp异步POST请求,总共5步,如下代码所示:

private void asyncValidate(final String telphone, final String password){
        new Thread( new Runnable() {
            @Override
            public void run() {
                // 1、初始化okhttpClient对象
                OkHttpClient okHttpClient = new OkHttpClient();
                // 2、构建请求体requestBody
                RequestBody requestBody = new FormBody.Builder()
                        .add("telphone", telphone)
                        .add("password", password)
                        .build();
                // 3、发送请求,因为要传密码,所以用POST方式
                Request request = new Request.Builder()
                        .url(loginURL)
                        .post(requestBody)
                        .build();
                // 4、使用okhttpClient对象获取请求的回调方法,enqueue()方法代表异步执行
                okHttpClient.newCall(request).enqueue( new Callback() {
                    // 5、重写两个回调方法
                    @Override
                    public void onFailure(Call call, IOException e) {//刚刚卡这里了因为数据库表中列名写错 所以回调了失败显示errMsg是未知错误
                        // ...
                    }

                    @Override
                    public void onResponse(Call call, Response response)//请求成功的话会有响应回来的数据需要解析 因为服务器写的统一json返回类型 
                        // ...
                    }
                });

            }
        }).start();
    }

这里,我用的这个版本okhttp要求API level 21+ 但夜神模拟器API是19

2.Gson解析okhttp回调响应的JSON数据

后端Controller层的方法上都加了@ResponseBody的注解,功能就是把返回值封装成JSON格式字符串(@ResponseBody的作用其实是将java对象转为json格式的数据。)但实际网络传输时还是传输json的字符串。

所以,okhttp回调接收的是一个JSON格式的字符串。这个字符串被封装在response对象的body里。

我们要做的就三步:

// 1、获取response对象的body里的字符串
String responseBodyStr = response.body().string();

// 2、将其解析为JSON对象
JsonObject responseBodyJSONObject = (JsonObject) new JsonParser().parse(responseBodyStr);

// 3、使用JSON对象获取具体值
String status = responseBodyJSONObject.get("status").getAsString();

这样就能解析形如下面的JSON数据(是响应信息response包下CommonReturnType设置的通用统一返回json数据类型,只要是@RequestMapping()括号里的方法就都是这个返回类型,包括异常也是这个返回类型),这里是/login方法不需要返回data

{
    "status" : "success",
    "data" : null
}

3.具体使用案例:

1.login.Avtivity

在点击登录按钮后调用该异步验证登录的方法,把输入框中输入的帐户密码当做请求参数发送了,然后回调响应信息:

    /*
      okhttp异步POST请求 要求API level 21+
      account 本来想的是可以是 telphone或者username
      但目前只实现了telphone
     */
    private void asyncValidate(final String account, final String password) {
        /*
         发送请求属于耗时操作,所以开辟子线程执行
         上面的参数都加上了final,否则无法传递到子线程中
        */
        new Thread(new Runnable() {
            @Override
            public void run() {
                // okhttp异步POST请求; 总共5步
                // 1、初始化okhttpClient对象
                OkHttpClient okHttpClient = new OkHttpClient();
                // 2、构建请求体requestBody
                final String telphone = account;  // 为了让键和值名字相同,把account改成了telphone,没其他意思
                RequestBody requestBody = new FormBody.Builder()
                        .add("telphone", telphone)
                        .add("password", password)
                        .build();
                // 3、发送请求,因为要传密码,所以用POST方式
                Request request = new Request.Builder()
                        .url(NetConstant.getLoginURL())
                        .post(requestBody)
                        .build();
                // 4、使用okhttpClient对象获取请求的回调方法,enqueue()方法代表异步执行
                okHttpClient.newCall(request).enqueue(new Callback() {
                    // 5、重写两个回调方法
                    @Override
                    public void onFailure(Call call, IOException e) {//客户端网络失败,直接连不到服务器
                        Log.d(TAG, "请求URL失败: " + e.getMessage());
                        showToastInThread(LoginActivity.this, "请求URL失败, 请重试!");
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {//连上服务器收到响应了
                        // 先判断一下服务器是否异常
                        String responseStr = response.toString();
                        if (responseStr.contains("200")) {
                             /*
                            注意这里,同一个方法内
                            response.body().string()只能调用一次,多次调用会报错
                             */
                            /* 使用Gson解析response的JSON数据的第一步 */获取response对象body里面的字符串
                            String responseBodyStr = response.body().string();
                            /* 使用Gson解析response的JSON数据的第二步 */将其解析为JSON对象
                            JsonObject responseBodyJSONObject = (JsonObject) new JsonParser().parse(responseBodyStr);
                            // 如果返回的status为success,则getStatus返回true(这个第三步在get方法里,使用json对象获取具体值,登录验证通过
                            if (getStatus(responseBodyJSONObject).equals("success")) {
                         
                                    Intent it_login_to_main = new Intent(LoginActivity.this, MainActivity.class);
                                    startActivity(it_login_to_main);
                                    // 登录成功后,登录界面就没必要占据资源了
                                    finish();
                            } else {//status不是success的话 异常信息也是解析出json返回
                                getResponseErrMsg(LoginActivity.this, responseBodyJSONObject);
                                Log.d(TAG, "账号或密码验证失败");
                            }
                        } else {//若responseStr里面不含200
                            Log.d(TAG, "服务器异常");
                            showToastInThread(LoginActivity.this, responseStr);
                        }
                    }
}); }
}).start(); }
/* 使用Gson解析response的JSON数据 本来总共是有三步的,一、二步在方法调用之前执行了 */ private String getStatus(JsonObject responseBodyJSONObject) {//这是Gson解析响应信息的第三步通过键拿值 /* 使用Gson解析response的JSON数据的第三步 通过JSON对象获取对应的属性值 */ String status = responseBodyJSONObject.get("status").getAsString(); // 登录成功返回的json为{ "status":"success", "data":null } // 只获取status即可,data为null return status; } /* 使用Gson解析response返回异常信息的JSON中的data对象 这也属于第三步,一、二步在方法调用之前执行了 */ private void getResponseErrMsg(Context context, JsonObject responseBodyJSONObject) {
//不是success是fail,则也需要解析异常信息,也是统一返回类型status和data。status是fail,data是错误对象包含errcode和errMsg两个属性 JsonObject dataObject
= responseBodyJSONObject.get("data").getAsJsonObject(); String errorCode = dataObject.get("errorCode").getAsString(); String errorMsg = dataObject.get("errorMsg").getAsString(); Log.d(TAG, "errorCode: " + errorCode + " errorMsg: " + errorMsg); // 在子线程中显示Toast showToastInThread(context, errorMsg); } // 实现在子线程中显示Toast private void showToastInThread(Context context, String msg) { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(context, msg, Toast.LENGTH_LONG).show(); } }); }

 2.register.Activity

有两个用上网络与服务器通信,一是获取验证码,二是提交注册

// okhttp异步请求验证码
    private void asyncGetOtpCode(final String telphone) {
        if (TextUtils.isEmpty(telphone)) {
            Toast.makeText(this, "请输入手机号", Toast.LENGTH_SHORT).show();
        }
        // 发送请求属于耗时操作,开辟子线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                // okhttp的使用,POST,异步; 总共5步
                // 1、初始化okhttpClient对象
                OkHttpClient okHttpClient = new OkHttpClient.Builder()
                        .connectTimeout(10, TimeUnit.SECONDS)
                        .readTimeout(20, TimeUnit.SECONDS)
                        .writeTimeout(30, TimeUnit.SECONDS)
                        .build();
                // 2、构建请求体
                RequestBody requestBody = new FormBody.Builder()
                        .add("telphone", telphone)
                        .build();
                // 3、发送请求,特别强调这里是POST方式
                Request request = new Request.Builder()
                        .url(NetConstant.getGetOtpCodeURL())
                        .post(requestBody)
                        .build();
                // 4、使用okhttpClient对象获取请求的回调方法,enqueue()方法代表异步执行
                okHttpClient.newCall(request).enqueue(new Callback() {
                    // 5、重写两个回调方法
                    // onFailure有可能是请求连接超时导致的
                    @Override
                    public void onFailure(Call call, IOException e) {
                        Log.d(TAG, "onFailure: " + e.getMessage());
                        e.printStackTrace();
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        // 先判断一下服务器是否异常
                        String responseStr = response.toString();
                        if (responseStr.contains("200")) {
                            // response.body().string()只能调用一次,多次调用会报错
                            String responseData = response.body().string();
                            JsonObject responseBodyJSONObject = (JsonObject) new JsonParser().parse(responseData);
                            // 如果返回的status为success,代表获取验证码成功
                            if (getResponseStatus(responseBodyJSONObject).equals("successGetOtpCode")) {
                                JsonObject dataObject = responseBodyJSONObject.get("data").getAsJsonObject();
                                if (!dataObject.isJsonNull()) {
                                    String telphone = dataObject.get("telphone").getAsString();
                                    String otpCode = dataObject.get("otpCode").getAsString();
                                    // 自动填充验证码
                                    setTextInThread(et_otpCode, otpCode);
                                    // 在子线程中显示Toast
                                    showToastInThread(RegisterActivity.this, "验证码:" + otpCode);
                                    Log.d(TAG, "telphone: " + telphone + " otpCode: " + otpCode);
                                }
                                Log.d(TAG, "验证码已发送,注意查收!");
                            } else {
                                getResponseErrMsg(RegisterActivity.this, responseBodyJSONObject);
                            }
                        } else {
                            Log.d(TAG, "服务器异常");
                            showToastInThread(RegisterActivity.this, responseStr);
                        }
                    }
                });

            }
        }).start();
    }


    // okhttp异步请求进行注册
    // 参数统一传递字符串
    // 传递到后端再进行类型转换以适配数据库
    private void asyncRegister(final String telphone, final String otpCode,
                               final String username, final String gender,
                               final String age, final String password1, final String password2) {

        if (TextUtils.isEmpty(telphone) || TextUtils.isEmpty(otpCode) || TextUtils.isEmpty(username)
                || TextUtils.isEmpty(gender) || TextUtils.isEmpty(age)
                || TextUtils.isEmpty(password1) || TextUtils.isEmpty(password2)) {
            Toast.makeText(RegisterActivity.this, "存在输入为空,注册失败", Toast.LENGTH_SHORT).show();
        } else if (password1.equals(password2)) {

            // 发送请求属于耗时操作,开辟子线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // okhttp的使用,POST,异步; 总共5步
                    // 1、初始化okhttpClient对象
                    OkHttpClient okHttpClient = new OkHttpClient();
                    // 2、构建请求体
                    // 注意这里的name 要和后端接收的参数名一一对应,否则无法传递过去
                    RequestBody requestBody = new FormBody.Builder()
                            .add("telphone", telphone)
                            .add("otpCode", otpCode)
                            .add("name", username)
                            .add("gender", gender)
                            .add("age", age)
                            .add("password", password1)
                            .build();
                    // 3、发送请求,特别强调这里是POST方式
                    Request request = new Request.Builder()
                            .url(NetConstant.getRegisterURL())
                            .post(requestBody)
                            .build();
                    // 4、使用okhttpClient对象获取请求的回调方法,enqueue()方法代表异步执行
                    okHttpClient.newCall(request).enqueue(new Callback() {
                        // 5、重写两个回调方法
                        @Override
                        public void onFailure(Call call, IOException e) {
                            Log.d(TAG, "onFailure: " + e.getMessage());
                        }

                        @Override
                        public void onResponse(Call call, Response response) throws IOException {
                            // 先判断一下服务器是否异常
                            String responseStr = response.toString();
                            if (responseStr.contains("200")) {
                                // response.body().string()只能调用一次,多次调用会报错
                                String responseBodyStr = response.body().string();
                                JsonObject responseBodyJSONObject = (JsonObject) new JsonParser().parse(responseBodyStr);
                                // 如果返回的status为success,代表验证通过
                                if (getResponseStatus(responseBodyJSONObject).equals("success")) {
                                    // 注册成功,记录token
                                    sp = getSharedPreferences("login_info", MODE_PRIVATE);
                                    editor = sp.edit();
                                    editor.putString("token", "token_value");
                                    editor.putString("telphone", telphone);
                                    editor.putString("password", password1); // 注意这里是password1

                                    if (editor.commit()) {
                                        Intent it_register_to_main = new Intent(RegisterActivity.this, MainActivity.class);
                                        startActivity(it_register_to_main);
                                        // 注册成功后,注册界面就没必要占据资源了
                                        finish();
                                    }
                                } else {
                                    getResponseErrMsg(RegisterActivity.this, responseBodyJSONObject);
                                }
                            } else {
                                Log.d(TAG, "服务器异常");
                                showToastInThread(RegisterActivity.this, responseStr);
                            }
                        }
                    });

                }
            }).start();
        } else {
            Toast.makeText(RegisterActivity.this, "两次密码不一致", Toast.LENGTH_SHORT).show();
        }
    }

    // 使用Gson解析response的JSON数据中的status,返回布尔值
    private String getResponseStatus(JsonObject responseBodyJSONObject) {
        // Gson解析JSON,总共3步
        // 1、获取response对象的字符串序列化
        // String responseData = response.body().string();
        // 2、通过JSON解析器JsonParser()把字符串解析为JSON对象,
        //
        // *****前两步抽写方法外面了*****
        //
        // JsonObject jsonObject = (JsonObject) new JsonParser().parse(responseBodyStr);
        // 3、通过JSON对象获取对应的属性值
        String status = responseBodyJSONObject.get("status").getAsString();
        return status;
    }

    // 获取验证码响应data
    // 使用Gson解析response返回异常信息的JSON中的data对象
    private void getResponseErrMsg(Context context, JsonObject responseBodyJSONObject) {
        JsonObject dataObject = responseBodyJSONObject.get("data").getAsJsonObject();
        String errorCode = dataObject.get("errorCode").getAsString();
        String errorMsg = dataObject.get("errorMsg").getAsString();
        Log.d(TAG, "errorCode: " + errorCode + " errorMsg: " + errorMsg);
        // 在子线程中显示Toast
        showToastInThread(context, errorMsg);
    }

    /* 在子线程中更新UI ,实现自动填充验证码 */
    private void setTextInThread(EditText editText, String otpCode) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                editText.setText(otpCode);
            }
        });
    }

    /* 实现在子线程中显示Toast */
    private void showToastInThread(Context context, String msg) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
            }
        });
    }

 

posted @ 2020-06-06 00:20  xinxinpang  阅读(267)  评论(0编辑  收藏  举报