FanChat学习笔记(三)——注册页

因为注册页和上一篇的FanChat学习笔记(二)——登录页框架很类似,几乎没有多少新知识,所以这篇文章就不像上文那样剖析了,只讲我看到的觉得可以单独记录下来的地方,如果对MVP不熟悉的话,可以先看看学习笔记一学习笔记二。今天我们需要在学习代码之前,先看看作者需要实现的业务逻辑,原文是这样介绍的:

  1. 实际项目中,注册会将用户名和密码注册到APP的服务器,然后APP的服务器再通过REST API方式注册到环信服务器。
  2. 由于本项目没有APP服务器,会将用户数据注册到第三方云数据库Bmob,注册成功后,再在客户端发送请求注册到环信服务器。

这里写图片描述

明白了上面的逻辑后,接下来看看java代码的具体实现,代码如下:

package com.itheima.leon.qqdemo.presenter.impl;

import com.hyphenate.chat.EMClient;
import com.hyphenate.exceptions.HyphenateException;
import com.itheima.leon.qqdemo.app.Constant;
import com.itheima.leon.qqdemo.model.User;
import com.itheima.leon.qqdemo.presenter.RegisterPresenter;
import com.itheima.leon.qqdemo.utils.StringUtils;
import com.itheima.leon.qqdemo.utils.ThreadUtils;
import com.itheima.leon.qqdemo.view.RegisterView;

import cn.bmob.v3.exception.BmobException;
import cn.bmob.v3.listener.SaveListener;

/**
 * 创建者:   Leon
 * 创建时间:  2016/10/16 22:21
 * 描述:    注册
 */
public class RegisterPresenterImpl implements RegisterPresenter {
    public static final String TAG = "RegisterPresenterImpl";

    public RegisterView mRegisterView;

    public RegisterPresenterImpl(RegisterView registerView) {
        mRegisterView = registerView;
    }

    @Override
    public void register(String userName, String pwd, String pwdConfirm) {
    //检查用户名是否符合规范
        if (StringUtils.checkUserName(userName)) {
        //检查密码是否符合规范
            if (StringUtils.checkPassword(pwd)) {
            //检查确认密码与输入密码是否一致
                if (pwd.equals(pwdConfirm)) {
                //UI展示开始登录
                    mRegisterView.onStartRegister();
                    registerBmob(userName, pwd);
                } else {
                //二次输入密码错误
                    mRegisterView.onConfirmPasswordError();
                }
            } else {
            //密码不符合规范
                mRegisterView.onPasswordError();
            }
        } else {
        //用户名不符合规范
            mRegisterView.onUserNameError();
        }
    }

    /**
     * 注册用户到Bmob
     * @param userName
     * @param pwd
     */
    private void registerBmob(final String userName, final String pwd) {
        User user = new User(userName, pwd);
        user.signUp(new SaveListener<User>() {
            @Override
            public void done(User user, BmobException e) {
                if (e == null) {
                    registerEaseMob(userName, pwd);
                } else {
                    notifyRegisterFailed(e);
                }
            }
        });
    }

    /**
     * 注册到环信
     * @param userName
     * @param pwd
     */
    private void registerEaseMob(final String userName, final String pwd) {
        ThreadUtils.runOnBackgroundThread(new Runnable() {
            @Override
            public void run() {
                try {
                    EMClient.getInstance().createAccount(userName, pwd);
                    ThreadUtils.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mRegisterView.onRegisterSuccess();
                        }
                    });
                } catch (HyphenateException e) {
                    e.printStackTrace();
                    ThreadUtils.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mRegisterView.onRegisterError();
                        }
                    });
                }
            }
        });
    }

    /**
     * 注册页失败异常甄别
     * @param e
     */
    private void notifyRegisterFailed(BmobException e) {
        if (e.getErrorCode() == Constant.ErrorCode.USER_ALREADY_EXIST) {
            mRegisterView.onResisterUserExist();
        } else {
            mRegisterView.onRegisterError();
        }
    }
}

既然是用户,那么还是先看看Ueer这个实体是怎么来定义的?代码如下:

package com.itheima.leon.qqdemo.model;

import cn.bmob.v3.BmobUser;

/**
 * 创建者:   Leon
 * 创建时间:  2016/10/16 23:31
 * 描述:    用户实体
 */
public class User extends BmobUser {

    public User(String userName, String password) {
        setUsername(userName);
        setPassword(password);
    }

}

对于云数据库Bmob,我们可以把它理解为一个服务器。那么我们继承了服务器中的用户,然后对用户的用户名和密码进行了赋值。至于用户的其它诸如手机号码、邮箱地址等可选字段,大家可以查看官方文档,这里不作过多的介绍。其它文章中用到的话,我们及时查看官方文档即可。

接下来我还需要看看它ThreadUtils类的线程处理方法:

runOnUiThread(Runnable runnable)
runOnBackgroundThread(Runnable runnable)

在查看方法的源码之前,我觉得这个项目中的命名都非常规范,有一种让人一眼看上去能见名知意的感觉,比如“runOnUiThread”我知道是需要在主线程执行的,“runOnBackgroundThread”我知道是后台子线程执行的,所以这种命名应该值得我们学习。话不多说,接下来看看方法的具体实现,我们找到源码:

package com.itheima.leon.qqdemo.utils;

import android.os.Handler;
import android.os.Looper;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * 创建者:   Leon
 * 创建时间:  2016/10/16 23:43
 * 描述:    线程工具类
 */
public class ThreadUtils {
    public static final String TAG = "ThreadUtils";
    //SingleThreadExecutor使用单线程执行任务
    //SingleThreadExecutor保证了任务执行的顺序,不会存在多线程活动。
    private static Executor sExecutor = Executors.newSingleThreadExecutor();

    private static Handler sHandler = new Handler(Looper.getMainLooper());
    //将Runnable作为callback属性然后生产一个新的Message对象,通过Handler发送出去
    //重点是没有产生新的线程
    public  static void runOnUiThread(Runnable runnable) {
        sHandler.post(runnable);
    }
    //开子线程执行任务
    public static void runOnBackgroundThread(Runnable runnable) {
        sExecutor.execute(runnable);
    }
}

这个工具类代码量很少,但是却非常有意义。Executor我第一次接触是在弘神的自定义ImageLoader项目看见的,当时的理解就是一个线程池的管理工具,但是我现在才知道,它原来也有很多的构造方法,除了上文中提到的单线程,还有固定线程数的线程池(第一次接触就是这种类型),无界线程池,构造方法如下:


 1. public static ExecutorService newFixedThreadPool(int nThreads);
    public static ExecutorService newFixedThreadPool(int nThreads,
    ThreadFactory threadFactory);
    使用固定线程数的线程池,满足了资源管理的需求,可以限制当前线程数量。适用于负载较重的服务器环境。

 2. public static ExecutorService newCachedThreadPool(); public static
    ExecutorService newCachedThreadPool(ThreadFactory threadFactory);
    无界线程池,适用于执行很多短期异步任务的小程序,适用于负载较轻的服务器。

除了Executor接口,文中还使用到了Handler来传递Runnable,但是这里不是开启线程,而是将Runnable作为属性赋值Message的Callback,然后通过handler来发送消息到主线程执行Runnable中的方法,源码如下:


    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
   private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

最后,需要处理的就是对密码的处理,因为我们不应该将密码明文的传入服务器,这是极大的不安全。所以我们需要对密码进行加密后再传入服务器。加密方法目前比较常用的有MD5加密和SHA加密,还有Base64进行加密的,其中MD5的密文是32位,SHA的密文是40位,Base64的密文最高长度根据传入的密码长度决定,但是因为Base64算法的计算方式和码表都是公开的,违反了柯克霍夫原则,比较容易被破解。所以剩下的加密方案只能在SHA和MD5中决定,虽然SHA在同样的硬件下,运行速度稍慢与MD5(我的电脑测试,SHA运行13毫秒,MD5运行1毫秒不到),但是SHA的安全性相对较高。假设使用强行技术攻击,产生任何一个报文使其摘要等于给定报摘要的难度对MD5是2^128数量级的操作,而对SHA-1则是2^160数量级的操作。这样,SHA-1对强行攻击有更大的强度。

所以给出工具类,加密解密,代码如下:

  private static MessageDigest sha = null;

    /**
     * SHA加密
     * @param inStr
     * @return
     * @throws Exception
     */
    public static String shaEncode(String inStr) {

        try {
            if(sha==null)
                sha = MessageDigest.getInstance("SHA");
        byte[] byteArray = inStr.getBytes("UTF-8");
        byte[] md5Bytes = sha.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16) {
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString().substring(10, 30);
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            return inStr;
        }

    }

    /**
     * SHA解密
     * @param newPassWord
     * @param oldPassWord
     * @return
     */
    public static boolean shaDecode(String newPassWord,String oldPassWord){

            return shaEncode(newPassWord).equals(shaEncode(oldPassWord));

    }

使用方式——注册:


 @Override
    public void register(String userName, String pwd, String pwdConfirm) {
        if (StringUtils.checkUserName(userName)) {
            if (StringUtils.checkPassword(pwd)) {
                if (pwd.equals(pwdConfirm)) {
                    mRegisterView.onStartRegister();
                    registerBmob(userName, StringUtils.shaEncode(pwd));
                } else {
                    mRegisterView.onConfirmPasswordError();
                }
            } else {
                mRegisterView.onPasswordError();
            }
        } else {
            mRegisterView.onUserNameError();
        }
    }

使用方式——登录:

 @Override
    public void login(String userName, String pwd) {
        if (StringUtils.checkUserName(userName)) {
            if (StringUtils.checkPassword(pwd)) {
                mLoginView.onStartLogin();
                startLogin(userName, StringUtils.shaEncode(pwd));
            } else {
                mLoginView.onPasswordError();
            }
        } else {
            mLoginView.onUserNameError();
        }
    }

OK,本篇文章就写到这里,有空继续补充!!

学习的项目地址:

github:https://github.com/uncleleonfan/FanChat

参考文章:

http://www.cnblogs.com/micrari/p/5634447.html
http://blog.csdn.net/lmj623565791/article/details/38377229/
http://docs.bmob.cn/data/Android/b_developdoc/doc/index.html#%E7%94%A8%E6%88%B7%E7%AE%A1%E7%90%86
http://www.blogjava.net/amigoxie/archive/2014/06/01/414299.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值