zoukankan      html  css  js  c++  java
  • 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();
                }
            });
        }
  • 相关阅读:
    requests.session()发送请求 和使用requests直接发送请求的区别
    axios请求接口如何将data转换formdata?
    yaml简单使用
    Appium-send_keys 无法写入?
    Appium-滑动操作
    Appium -appium desktop工具使用
    Appium学习笔记(2)adb常用命令
    Appium学习笔记(1)appium配置-起步
    Django学习笔记(20)celery_tasks 异步任务初识
    Django学习笔记(19)HttpResponse/JsonResponse /render /redirect/Http404
  • 原文地址:https://www.cnblogs.com/gezi1007/p/13048943.html
Copyright © 2011-2022 走看看