一口气看完口袋微博源码(二)之用户登录
2014-12-19 09:01
232 查看
这一篇讲用户登录,步入正题,用户登录首先要写好安卓的界面部分,既然是登录,无外乎用户名,密码,是否记住,然后就是登录按钮,ok直接上代码:
Xml代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="center_horizontal"
android:background="@drawable/back"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
> <!-- 声明垂直分布的线性布局 -->
<LinearLayout
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:paddingTop="25px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<TextView
android:text="@string/tvUid"
android:layout_width="100px"
style="@style/text"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
/>
<EditText
android:id="@+id/etUid"
android:singleLine="true"
android:layout_width="150px"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<TextView
android:text="@string/tvPwd"
android:layout_width="100px"
style="@style/text"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
/>
<EditText
android:id="@+id/etPwd"
android:singleLine="true"
android:password="true"
android:layout_width="150px"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<CheckBox
android:id="@+id/cbRemember"
android:text="@string/cbRemember"
android:layout_gravity="center_horizontal"
android:checked="false"
android:textColor="@color/character"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<ImageButton
android:id="@+id/ibExit"
android:layout_width="60px"
android:layout_height="60px"
android:src="@drawable/exit" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
> <!-- 声明于显示按钮的线性布局 -->
<Button
android:id="@+id/btnLogin"
style="@style/button"
android:layout_width="120px"
android:layout_height="wrap_content"
android:text="@string/btnLogin"
/>
<Button
android:id="@+id/btnReg"
style="@style/button"
android:layout_width="120px"
android:layout_height="wrap_content"
android:text="@string/btnReg"
/>
</LinearLayout>
</LinearLayout>
接下来就是写对应布局的activity了,这里就只着重讲登录的实现流程了,因为相信大家对获取文本框数据和checkBox配合sharedPreference很了解了就不再赘述,当点击登录按钮的时候进入登录,登录的客户端的流程和前面讲的注册的流程是一样的,客户端把登录数据装到字符串里面,数据格式为请求头+用户名+”|”+密码,这个|是分割线。然后通过上文降到的MyConnection获取到与服务器的socket连接,然后通过这个连接的数据输出流向服务器发送数据,然后通过服务端返回的数据判断是否登录成功:
Java代码
//方法:连接服务器进行登录
public void login(){
new Thread(){
public void run(){
Looper.prepare();
try{
if(mc == null){
mc = new MyConnector(SERVER_ADDRESS, SERVER_PORT);
}
EditText etUid = (EditText)findViewById(R.id.etUid); //获得帐号EditText
EditText etPwd = (EditText)findViewById(R.id.etPwd); //获得密码EditText
String uid = etUid.getEditableText().toString().trim(); //获得输入的帐号
String pwd = etPwd.getEditableText().toString().trim(); //获得输入的密码
if(uid.equals("") || pwd.equals("")){ //判断输入是否为空
Toast.makeText(LoginActivity.this, "请输入帐号或密码!", Toast.LENGTH_SHORT).show();//输出提示消息
return;
}
String msg = "<#LOGIN#>"+uid+"|"+pwd; //组织要返回的字符串
mc.dout.writeUTF(msg); //发出消息
String receivedMsg = mc.din.readUTF(); //读取服务器发来的消息
pd.dismiss();
if(receivedMsg.startsWith("<#LOGIN_SUCCESS#>")){ //收到的消息为登录成功消息
receivedMsg = receivedMsg.substring(17);
String [] sa = receivedMsg.split("\\|");
CheckBox cb = (CheckBox)findViewById(R.id.cbRemember); //获得CheckBox对象
if(cb.isChecked()){
rememberMe(uid,pwd);
}
//转到功能面板
Intent intent = new Intent(LoginActivity.this,FunctionTabActivity.class);
intent.putExtra("uno", sa[0]);
startActivity(intent); //启动功能Activity
finish();
}
else if(msg.startsWith("<#LOGIN_FAIL#>")){ //收到的消息为登录失败
Toast.makeText(LoginActivity.this, msg.substring(14), Toast.LENGTH_LONG).show();
Looper.loop();
Looper.myLooper().quit();
}
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
}
接下来我们转到服务端,看服务端执行情况,当服务器收到客户端<#LOGIN#>的请求头后,通过解析客户端发来的数据拿到用户名和密码,然后调用DBUtil.checkLogin()方法,校验用户,并返回校验结果,校验结果是ArrayList<String>的形式,其实很好理解,因为用户信息包括用户名,id,email,state等信息,我们来看看这个过程是怎么处理的:
Java代码
//方法:检查用户名和密码是否正确
public static ArrayList<String> checkLogin(String u_no,String u_pwd){
ArrayList<String> result = new ArrayList<String>();
Connection con = null; //声明获取数据库连接
PreparedStatement ps = null; //声明Statement对象
ResultSet rs = null; //声明ResultSet对象
try{
con = getConnection(); //获取数据库连接
if(con == null){ //判断数据库连接对象是否
result.add(CONNECTION_OUT); //
return result;
}
ps = con.prepareStatement("select u_no,u_name,u_email,u_state,h_id from user where u_no=? and u_pwd=?");
ps.setString(1, u_no); //设置预编译语句的参数
ps.setString(2, u_pwd); //设置预编译语句的参数
rs = ps.executeQuery();
if(rs.next()){ //判断结果集是否为空
for(int i=1;i<=5;i++){
result.add(rs.getString(i)); //将结果集中数据存放到ArrayList中
}
}
else{ //如果数据库查无此人
result.add(LOGIN_FAIL); //返回登录出错信息
}
}catch(Exception e){
e.printStackTrace();
}
finally{
try{
if(rs != null){
rs.close();
rs = null;
}
}catch(Exception e){
e.printStackTrace();
}
try{
if(ps != null){
ps.close();
ps = null;
}
}catch(Exception e){
e.printStackTrace();
}
try{
if(con != null){
con.close();
con = null;
}
}catch(Exception e){
e.printStackTrace();
}
}
return result;
}
哦,原来是查询数据库,然后把数据装在一个list集合返回去,然后serverAgent把这些用户数据封装成一条格式化后的字符串,然后写给客户端:
Java代码
if(msg.startsWith("<#LOGIN#>")){ //消息为登录
String content = msg.substring(9); //获得消息内容
String [] sa = content.split("\\|");
ArrayList<String> result = DBUtil.checkLogin(sa[0], sa[1]);
if(result.size()>1){ //登录成功
StringBuilder sb = new StringBuilder();
sb.append("<#LOGIN_SUCCESS#>");
for(String s:result){
sb.append(s);
sb.append("|");
}
String loginInfo = sb.substring(0,sb.length()-1);
dout.writeUTF(loginInfo); //返回用户的基本信息
}
数据发给客户端了,所以我们又转回到客户端,看看客户端是怎么处理这些数据的。
客户端判断服务端发过来的数据的消息头是不是<#LOGIN_SUCCESS#>,是的话获取后的数据然后跳转到FunctionTabActivity.java,同时用intent把这些数据带过去。如果消息头是<#LOGIN_FAIL#>则给用户一个友好提示,在处理失败消息的时候我们发现用到了一个不太熟悉的东西Looper:
Java代码
Looper.loop();
Looper.myLooper().quit();
那这个Looper是个啥玩意呢,现在来科普一下:
1. Looper类用来为一个线程开启一个消息循环。
默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。)
Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。
2. 通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理方法。
默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,Handler在主线程中定义,那么它是与主线程的Looper绑定。
mainHandler = new Handler() 等价于new Handler(Looper.myLooper()).
Looper.myLooper():获取当前进程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。
3. 在非主线程中直接new Handler() 会报如下的错误:
AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception
AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。
4. Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。
注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
5. 基于以上知识,可实现主线程给子线程(非主线程)发送消息。
到现在,用户登录一个模块我们已经学习完了,需要详细请看代码。
Xml代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="center_horizontal"
android:background="@drawable/back"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
> <!-- 声明垂直分布的线性布局 -->
<LinearLayout
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:paddingTop="25px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<TextView
android:text="@string/tvUid"
android:layout_width="100px"
style="@style/text"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
/>
<EditText
android:id="@+id/etUid"
android:singleLine="true"
android:layout_width="150px"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<TextView
android:text="@string/tvPwd"
android:layout_width="100px"
style="@style/text"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
/>
<EditText
android:id="@+id/etPwd"
android:singleLine="true"
android:password="true"
android:layout_width="150px"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<CheckBox
android:id="@+id/cbRemember"
android:text="@string/cbRemember"
android:layout_gravity="center_horizontal"
android:checked="false"
android:textColor="@color/character"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<ImageButton
android:id="@+id/ibExit"
android:layout_width="60px"
android:layout_height="60px"
android:src="@drawable/exit" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
> <!-- 声明于显示按钮的线性布局 -->
<Button
android:id="@+id/btnLogin"
style="@style/button"
android:layout_width="120px"
android:layout_height="wrap_content"
android:text="@string/btnLogin"
/>
<Button
android:id="@+id/btnReg"
style="@style/button"
android:layout_width="120px"
android:layout_height="wrap_content"
android:text="@string/btnReg"
/>
</LinearLayout>
</LinearLayout>
接下来就是写对应布局的activity了,这里就只着重讲登录的实现流程了,因为相信大家对获取文本框数据和checkBox配合sharedPreference很了解了就不再赘述,当点击登录按钮的时候进入登录,登录的客户端的流程和前面讲的注册的流程是一样的,客户端把登录数据装到字符串里面,数据格式为请求头+用户名+”|”+密码,这个|是分割线。然后通过上文降到的MyConnection获取到与服务器的socket连接,然后通过这个连接的数据输出流向服务器发送数据,然后通过服务端返回的数据判断是否登录成功:
Java代码
//方法:连接服务器进行登录
public void login(){
new Thread(){
public void run(){
Looper.prepare();
try{
if(mc == null){
mc = new MyConnector(SERVER_ADDRESS, SERVER_PORT);
}
EditText etUid = (EditText)findViewById(R.id.etUid); //获得帐号EditText
EditText etPwd = (EditText)findViewById(R.id.etPwd); //获得密码EditText
String uid = etUid.getEditableText().toString().trim(); //获得输入的帐号
String pwd = etPwd.getEditableText().toString().trim(); //获得输入的密码
if(uid.equals("") || pwd.equals("")){ //判断输入是否为空
Toast.makeText(LoginActivity.this, "请输入帐号或密码!", Toast.LENGTH_SHORT).show();//输出提示消息
return;
}
String msg = "<#LOGIN#>"+uid+"|"+pwd; //组织要返回的字符串
mc.dout.writeUTF(msg); //发出消息
String receivedMsg = mc.din.readUTF(); //读取服务器发来的消息
pd.dismiss();
if(receivedMsg.startsWith("<#LOGIN_SUCCESS#>")){ //收到的消息为登录成功消息
receivedMsg = receivedMsg.substring(17);
String [] sa = receivedMsg.split("\\|");
CheckBox cb = (CheckBox)findViewById(R.id.cbRemember); //获得CheckBox对象
if(cb.isChecked()){
rememberMe(uid,pwd);
}
//转到功能面板
Intent intent = new Intent(LoginActivity.this,FunctionTabActivity.class);
intent.putExtra("uno", sa[0]);
startActivity(intent); //启动功能Activity
finish();
}
else if(msg.startsWith("<#LOGIN_FAIL#>")){ //收到的消息为登录失败
Toast.makeText(LoginActivity.this, msg.substring(14), Toast.LENGTH_LONG).show();
Looper.loop();
Looper.myLooper().quit();
}
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
}
接下来我们转到服务端,看服务端执行情况,当服务器收到客户端<#LOGIN#>的请求头后,通过解析客户端发来的数据拿到用户名和密码,然后调用DBUtil.checkLogin()方法,校验用户,并返回校验结果,校验结果是ArrayList<String>的形式,其实很好理解,因为用户信息包括用户名,id,email,state等信息,我们来看看这个过程是怎么处理的:
Java代码
//方法:检查用户名和密码是否正确
public static ArrayList<String> checkLogin(String u_no,String u_pwd){
ArrayList<String> result = new ArrayList<String>();
Connection con = null; //声明获取数据库连接
PreparedStatement ps = null; //声明Statement对象
ResultSet rs = null; //声明ResultSet对象
try{
con = getConnection(); //获取数据库连接
if(con == null){ //判断数据库连接对象是否
result.add(CONNECTION_OUT); //
return result;
}
ps = con.prepareStatement("select u_no,u_name,u_email,u_state,h_id from user where u_no=? and u_pwd=?");
ps.setString(1, u_no); //设置预编译语句的参数
ps.setString(2, u_pwd); //设置预编译语句的参数
rs = ps.executeQuery();
if(rs.next()){ //判断结果集是否为空
for(int i=1;i<=5;i++){
result.add(rs.getString(i)); //将结果集中数据存放到ArrayList中
}
}
else{ //如果数据库查无此人
result.add(LOGIN_FAIL); //返回登录出错信息
}
}catch(Exception e){
e.printStackTrace();
}
finally{
try{
if(rs != null){
rs.close();
rs = null;
}
}catch(Exception e){
e.printStackTrace();
}
try{
if(ps != null){
ps.close();
ps = null;
}
}catch(Exception e){
e.printStackTrace();
}
try{
if(con != null){
con.close();
con = null;
}
}catch(Exception e){
e.printStackTrace();
}
}
return result;
}
哦,原来是查询数据库,然后把数据装在一个list集合返回去,然后serverAgent把这些用户数据封装成一条格式化后的字符串,然后写给客户端:
Java代码
if(msg.startsWith("<#LOGIN#>")){ //消息为登录
String content = msg.substring(9); //获得消息内容
String [] sa = content.split("\\|");
ArrayList<String> result = DBUtil.checkLogin(sa[0], sa[1]);
if(result.size()>1){ //登录成功
StringBuilder sb = new StringBuilder();
sb.append("<#LOGIN_SUCCESS#>");
for(String s:result){
sb.append(s);
sb.append("|");
}
String loginInfo = sb.substring(0,sb.length()-1);
dout.writeUTF(loginInfo); //返回用户的基本信息
}
数据发给客户端了,所以我们又转回到客户端,看看客户端是怎么处理这些数据的。
客户端判断服务端发过来的数据的消息头是不是<#LOGIN_SUCCESS#>,是的话获取后的数据然后跳转到FunctionTabActivity.java,同时用intent把这些数据带过去。如果消息头是<#LOGIN_FAIL#>则给用户一个友好提示,在处理失败消息的时候我们发现用到了一个不太熟悉的东西Looper:
Java代码
Looper.loop();
Looper.myLooper().quit();
那这个Looper是个啥玩意呢,现在来科普一下:
1. Looper类用来为一个线程开启一个消息循环。
默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。)
Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。
2. 通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理方法。
默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,Handler在主线程中定义,那么它是与主线程的Looper绑定。
mainHandler = new Handler() 等价于new Handler(Looper.myLooper()).
Looper.myLooper():获取当前进程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。
3. 在非主线程中直接new Handler() 会报如下的错误:
AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception
AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。
4. Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。
注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
5. 基于以上知识,可实现主线程给子线程(非主线程)发送消息。
到现在,用户登录一个模块我们已经学习完了,需要详细请看代码。
相关文章推荐
- 一口气看完一个项目源码(一)之用户注册
- C#自动模拟用户登录发布或发布数据源码?--AliasNet
- J2EE 下各登录用户共享对象的实现 源码下载
- Struts2+Spring+Ibatis用户注册、登录、管理入门学习实例源码下载
- 用户登录:Spring Security3源码分析-UsernamePasswordAuthenticationFilter分析
- Horizon 源码阅读(三)—— Horizon 用户登录流程
- Asp.Net使用加密cookie代替session验证用户登录状态 源码分享 欢迎拍砖
- (转)OpenFire源码学习之五:用户登录
- 【原创】基于Memcached 实现用户登录的Demo(附源码)
- [Tools]获取域环境内所有用户登录信息(附源码及程序)
- python基础练习:用户登录实验源码
- Ecshop微博登录方式与用户账号的绑定
- Asp.Net使用加密cookie代替session验证用户登录状态 源码分享
- linux id.c源码之显示当前登录用户信息
- jsp基于XML实现用户登录与注册的实例解析(附源码)
- OpenFire源码学习之五:用户登录
- J2EE 应用服务器下各登录用户共享对象的实现 源码下载
- chatofpomelo源码分析(一)——用户登录
- 在为应用集成微博第三方登录获取用户信息的时候出现异常com.weibo.sdk.android.WeiboException: {"error":"User does not exists!","er
- iOS微博授权登录及获取用户数据的方法