今天咋们看的这个项目源码是一个微博客户端,和服务端通讯用socket写的,项目名称:口袋微博,和前面那个项目不同,这个项目略难一点,不过没关系,让我们一起来学习学习吧。
按照使用流程,首先是注册页面,因此我们来写注册页面,先把注册页面的布局写了:
- <?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:paddingTop="25px"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- > <!-- 声明一个线性布局 -->
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- > <!-- 声明一个显示昵称的线性布局 -->
- <TextView
- android:text="@string/tvName"
- android:layout_width="100px"
- style="@style/text"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- />
- <EditText
- android:id="@+id/etName"
- android:singleLine="true"
- android:layout_width="160px"
- android:layout_height="wrap_content"
- />
- </LinearLayout>
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- > <!-- 声明显示密码的线性布局 -->
- <TextView
- android:text="@string/tvPwd"
- style="@style/text"
- android:layout_width="100px"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- />
- <EditText
- android:id="@+id/etPwd1"
- android:singleLine="true"
- android:password="true"
- android:layout_width="160px"
- android:layout_height="wrap_content"
- />
- </LinearLayout>
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- > <!-- 声明显示确认密码的线性布局 -->
- <TextView
- android:text="@string/tvPwd2"
- style="@style/text"
- android:layout_width="100px"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- /> <!-- 声明TextView控件 -->
- <EditText
- android:id="@+id/etPwd2"
- android:singleLine="true"
- android:password="true"
- android:layout_width="160px"
- android:layout_height="wrap_content"
- /> <!-- 声明输入确认密码EditText控件 -->
- </LinearLayout>
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- > <!-- 声明包含Email输入的线性布局 -->
- <TextView
- android:text="@string/tvEmail"
- style="@style/text"
- android:layout_width="100px"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- />
- <EditText
- android:id="@+id/etEmail"
- android:singleLine="true"
- android:layout_width="160px"
- android:layout_height="wrap_content"
- />
- </LinearLayout>
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- > <!-- 声明包含心情输入的线性布局 -->
- <TextView
- android:text="@string/tvStatus"
- style="@style/text"
- android:layout_width="100px"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- />
- <EditText
- android:id="@+id/etStatus"
- android:singleLine="true"
- android:text="@string/etStatus"
- android:layout_width="160px"
- android:layout_height="wrap_content"
- />
- </LinearLayout>
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- >
- <Button
- android:id="@+id/btnReg"
- style="@style/button"
- android:layout_width="120px"
- android:layout_height="wrap_content"
- android:text="@string/btnReg"
- />
- <Button
- android:id="@+id/btnBack"
- style="@style/button"
- android:layout_width="120px"
- android:layout_height="wrap_content"
- android:text="@string/btnBack"
- />
- </LinearLayout>
- <LinearLayout
- android:orientation="vertical"
- android:visibility="visible"
- android:id="@+id/regResult"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- >
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- >
- <TextView
- android:text="@string/regSuccess"
- style="@style/text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- <EditText
- android:id="@+id/etUno"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:cursorVisible="false"
- />
- </LinearLayout>
- <Button
- android:id="@+id/btnEnter"
- style="@style/text"
- android:layout_gravity="right"
- android:text="@string/btnEnter"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- </LinearLayout>
- </LinearLayout>
布局写好以后,我们需要给这些布局添加点击事件(主要是注册),通过自定义一个MyConnection指定地址和端口号连接socket得到DataOutputStream和一个DataInputStream,把要发送的注册信息封装好(这里封装用的是消息头+分隔符的形式)把消息发给服务端,然后通过DataInputStream读取服务端返回的结果,如果服务端返回结果的请求头为<#REG_SUCCESS#>代表注册成功,同时用一个变量记住返回的结果,否则就是注册失败。
注册代码如下:
- /**
- * 方法:连接服务器,进行注册
- */
- public void register(){
- new Thread(){
- public void run(){
- Looper.prepare();
- //1.获得用户输入的数据并进行验证
- EditText etName = (EditText)findViewById(R.id.etName); //获得昵称EditText对象
- EditText etPwd1 = (EditText)findViewById(R.id.etPwd1); //获得密码EditText对象
- EditText etPwd2 = (EditText)findViewById(R.id.etPwd2); //获得确认密码EditText对象
- EditText etEmail = (EditText)findViewById(R.id.etEmail); //获得邮箱EditText对象
- EditText etStatus = (EditText)findViewById(R.id.etStatus); //获得心情EditText对象
- String name = etName.getEditableText().toString().trim(); //获得昵称
- String pwd1 = etPwd1.getEditableText().toString().trim(); //获得密码
- String pwd2 = etPwd2.getEditableText().toString().trim(); //获得确认密码
- String email = etEmail.getEditableText().toString().trim(); //获得邮箱
- String status = etStatus.getEditableText().toString().trim(); //获得状态
- if(name.equals("") || pwd1.equals("") || pwd2.equals("") || email.equals("") || status.equals("")){
- Toast.makeText(RegActivity.this, "请将注册信息填写完整", Toast.LENGTH_LONG).show();
- return;
- }
- if(!pwd1.equals(pwd2)){ //判断两次输入的密码是否一致
- Toast.makeText(RegActivity.this, "两次输入的密码不一致!", Toast.LENGTH_LONG).show();
- return;
- }
- //2.连接服务器开始传数据
- try{
- mc = new MyConnector(SERVER_ADDRESS, SERVER_PORT);
- String regInfo = "<#REGISTER#>"+name+"|"+pwd1+"|"+email+"|"+status;
- mc.dout.writeUTF(regInfo);
- String result = mc.din.readUTF();
- pd.dismiss();
- if(result.startsWith("<#REG_SUCCESS#>")){ //返回信息为注册成功
- result= result.substring(15); //去掉信息头
- uno = result; //记录用户的ID
- myHandler.sendEmptyMessage(0); //发出Handler消息
- Toast.makeText(RegActivity.this, "注册成功!", Toast.LENGTH_LONG).show();
- }
- else{ //注册失败
- Toast.makeText(RegActivity.this, "注册失败!请重试!", Toast.LENGTH_LONG).show();
- }
- }
- catch(Exception e){
- e.printStackTrace();
- }
- }
- }.start();
- }
接下来我们来写服务端的代码,因为服务端用的socket,因此需要一个入口函数,这个入口在Server.java文件中,通过它开启一个服务线程:
- public class Server{
- public static void main(String args[]){
- try{
- ServerSocket ss = new ServerSocket(8888);
- ServerThread st = new ServerThread(ss);
- st.start();
- System.out.println("Listening...");
- }catch(Exception e){
- e.printStackTrace();
- }
- }
- }
我们转到这个线程类里面,发现实现是通过调用serverSocket的accept()方法,监听客户端请求。对于请求过来的数据,专门写一个代理类ServerAgent.java来处理。
- package wpf;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.net.SocketException;
- public class ServerThread extends Thread{
- public ServerSocket ss; //声明ServerSocket对象
- public boolean flag = false;
- public ServerThread(ServerSocket ss){ //构造器
- this.ss = ss;
- flag = true;
- }
- public void run(){
- while(flag){
- try{
- Socket socket = ss.accept();
- ServerAgent sa = new ServerAgent(socket);
- sa.start();
- }
- catch(SocketException se){
- try{
- ss.close();
- ss = null;
- System.out.println("ServerSocket closed");
- }catch(Exception ee){
- ee.printStackTrace();
- }
- }
- catch(Exception e){
- e.printStackTrace();
- }
- }
- }
- }
我们来看ServerAgent是怎么实现的。
代理线程通过构造函数拿到数据输入和输出流,然后在run方法里面处理用户请求。接下来就是对请求头的判断,我们这里先只讲注册请求,收到的是注册请求,解析用户发过来的数据,然后通过写一个数据库工具类DBUtil类处理用户发送过来的数据。并将处理结果返回给客户端,然后客户端通过判断从服务器获取的请求结果更新ui界面。两个主要的方法:
- public ServerAgent(Socket socket){
- this.socket = socket;
- try {
- this.din = new DataInputStream(socket.getInputStream());
- this.dout = new DataOutputStream(socket.getOutputStream());
- flag =true;
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- public void run(){
- while(flag){
- try {
- String msg = din.readUTF(); //接收客户端发来的消息
- // System.out.println("收到的消息是:"+msg);
- if(msg.startsWith("<#REGISTER#>")){ //消息为用户注册
- msg = msg.substring(12); //获得字符串值
- String [] sa = msg.split("\|"); //切割字符串
- String regResult = DBUtil.registerUser(sa[0], sa[1], sa[2], sa[3], "1");
- if(regResult.equals(REGISTER_FAIL)){ //注册失败
- dout.writeUTF("<#REG_FAIL#>");
- }
- else{
- dout.writeUTF("<#REG_SUCCESS#>"+regResult);
- }
- }
- }
于是,我们转到DbUtil类里面,看书怎么保存到数据库中的。我们发现存储使用一般存储方式,只是里面有设置存储编码方式,还用到了prepareStatement。处理结束返回处理结果,如果连接为null返回连接失败,如果保存到数据库成功就返回注册完的用户id,否则返回注册失败。完了之后不要忘了释放资源。
//方法:注册用户
- public static String registerUser(String u_name,String u_pwd,String u_email,String u_state,String h_id){
- String result=null;
- Connection con = null; //声明数据库连接对象
- PreparedStatement ps = null; //声明语句对象
- try{
- con = getConnection();
- if(con == null){ //判断是否成功获取连接
- result = CONNECTION_OUT;
- return result; //返回方法的执行
- }
- ps = con.prepareStatement("insert into user(u_no,u_name,u_pwd,u_email,u_state,h_id)" +
- "values(?,?,?,?,?,?)"); //构建SQL语句
- String u_no = String.valueOf(getMax(USER)); //获得分配给用户的帐号
- u_name = new String(u_name.getBytes(),"ISO-8859-1"); //转成ISO-8859-1以插入数据库
- u_state = new String(u_state.getBytes(),"ISO-8859-1"); //转成ISO-8859-1以插入数据库
- int no = Integer.valueOf(u_no);
- int hid = Integer.valueOf(h_id);
- ps.setInt(1, no); //设置PreparedStatement的参数
- ps.setString(2, u_name);
- ps.setString(3, u_pwd);
- ps.setString(4, u_email);
- ps.setString(5, u_state);
- ps.setInt(6,hid);
- int count = ps.executeUpdate(); //执行插入
- if(count == 1){ //如果插入成功
- result = u_no; //获得玩家的帐号
- }
- else{ //如果没有插入数据
- result = REGISTER_FAIL; //获得出错信息
- }
- }catch(Exception e){
- e.printStackTrace();
- }
- finally{
- 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;
- }
接下来便是客户端处理服务端返回数据:
客户端处理完服务端发过来的数据,就通过handler更新界面,让进入个人中心这个按钮可见,同时拿到用户id转到个人中心页面。这样,一个用户注册的模块写好了。
- Handler myHandler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what){
- case 0:
- View linearLayout = (LinearLayout)findViewById(R.id.regResult); //获得线性布局
- linearLayout.setVisibility(View.VISIBLE); //设置可见性
- EditText etUno = (EditText)findViewById(R.id.etUno);
- etUno.setText(uno);
- break;
- }
- super.handleMessage(msg);
- }
- };
下一篇介绍用户登录时如何实现的。