Android 群聊天室
来源:互联网 发布:常州科教城淘宝装修 编辑:程序博客网 时间:2024/05/29 09:26
THREAD_SORCKET DAY05
- 解析XML生成Point对象
- 实现群聊天室
1 解析XML生成Point对象
1.1 问题
要求解析如下xml文件,并且生成Point对象。
- <?xml version="1.0" encoding="UTF-8"?>
- <beans>
- <bean name="point" class="day23.Point">
- <param name="x" value="10"/>
- <param name="y" value="20"/>
- </bean>
- </beans>。
1.2 方案
利用dom4j解析xml首先要把dom4j-1.6.1.jar和jaxen-1.1-beta-6.jar这2个jar包拷贝到项目的lib目录下,并配置build path。把xml文件也拷贝到项目中,根据xml的格式创建相对应的Point实体类,然后编写BeanFactory类去解析xml文件。
1.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:编写Point实体类
创建一个java项目,创建包,包中创建一个类Point,根据xml的格式编写类的属性,并复写toString方法。代码如下所示:
- package tarena.gsd;
- //所有的类都一个类对象,其类型为Class类型
- public class Point {//Point.class
- private int x;//也是对象,类型为Field类型
- private int y;
- public double distance(Point point){
- int a=x-point.x;
- int b=y-point.y;
- return Math.sqrt(a*a+b*b);
- }
- @Override
- public String toString() {
- return "Point [x=" + x + ", y=" + y + "]";
- }
- }
步骤二:编写BeanFactory类
编写BeanFactory类,用于解析xml文件,在类中定义2个静态的属性,类型是Map,用于存放解析元素的key和value值。代码如下:
- package tarena.gsd;
- import java.io.FileNotFoundException;
- import java.lang.reflect.Field;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import org.dom4j.Document;
- import org.dom4j.DocumentException;
- import org.dom4j.Element;
- import org.dom4j.Node;
- import org.dom4j.io.SAXReader;
- public class BeanFactory {// 工厂
- /**
- * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
- * value:class属性对应的值
- */
- private static Map<String, String> beanMap = new HashMap<String, String>();
- // key:bean的name属性值
- // value:bean中的参数
- private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
- // 当类中有多个静态属性(类变量)或静态代码块时
- }
步骤三:编写newInstance方法
在BeanFactory类中编写一个newInstance方法,返回值类型是Object,参数是1个String型变量,方法中根据传入的值,用反射创建对象,并用map的值构建对象属性,最后把对象返回。代码如下:
- package tarena.gsd;
- import java.io.FileNotFoundException;
- import java.lang.reflect.Field;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import org.dom4j.Document;
- import org.dom4j.DocumentException;
- import org.dom4j.Element;
- import org.dom4j.Node;
- import org.dom4j.io.SAXReader;
- public class BeanFactory {// 工厂
- /**
- * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
- * value:class属性对应的值
- */
- private static Map<String, String> beanMap = new HashMap<String, String>();
- // key:bean的name属性值
- // value:bean中的参数
- private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
- public static Object newInstance(String key) throws Exception {
- // 通过反射构建类的对象
- Class<?> c = Class.forName(beanMap.get(key));// 类对象
- Object obj = c.newInstance();// 构建类的对象
- // 通过反射给类的属性赋值
- Map<String, String> params = paramMap.get(key);
- // 遍历Map(首先应将map转换为set)
- Set<Entry<String, String>> es = params.entrySet();
- // 遍历set
- for (Entry<String, String> e : es) {
- // 通过类对象获得属性对象
- Field f = c.getDeclaredField(e.getKey());
- // 设置属性对象可访问
- f.setAccessible(true);// 设置属性可访问
- // 给obj对象的f属性赋值
- f.set(obj, Integer.valueOf(e.getValue()));
- }
- return obj;
- }
- }
步骤四:编写parse方法
在BeanFactory类中编写一个parse方法,返回值类型是Document,参数是1个String型变量,方法中根据传入的值,把传入的文件转换为Document对象并返回。代码如下:
- package tarena.gsd;
- import java.io.FileNotFoundException;
- import java.lang.reflect.Field;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import org.dom4j.Document;
- import org.dom4j.DocumentException;
- import org.dom4j.Element;
- import org.dom4j.Node;
- import org.dom4j.io.SAXReader;
- public class BeanFactory {// 工厂
- /**
- * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
- * value:class属性对应的值
- */
- private static Map<String, String> beanMap = new HashMap<String, String>();
- // key:bean的name属性值
- // value:bean中的参数
- private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
- // alt+shift+m (快速提取代码块为方法)
- private static Document parse(String fileName) throws DocumentException,
- FileNotFoundException {
- // 1.构建解析器对象
- SAXReader r = new SAXReader();
- // 2.解析文件
- // return r.read(
- // new FileInputStream(fileName));
- return r.read(BeanFactory.class.// 获得类对象
- getClassLoader(). // 类加载器对象(负责将类读到内存的对象)
- getResourceAsStream(fileName));// 从类路径读取
- }
- public static Object newInstance(String key) throws Exception {
- // 通过反射构建类的对象
- Class<?> c = Class.forName(beanMap.get(key));// 类对象
- Object obj = c.newInstance();// 构建类的对象
- // 通过反射给类的属性赋值
- Map<String, String> params = paramMap.get(key);
- // 遍历Map(首先应将map转换为set)
- Set<Entry<String, String>> es = params.entrySet();
- // 遍历set
- for (Entry<String, String> e : es) {
- // 通过类对象获得属性对象
- Field f = c.getDeclaredField(e.getKey());
- // 设置属性对象可访问
- f.setAccessible(true);// 设置属性可访问
- // 给obj对象的f属性赋值
- f.set(obj, Integer.valueOf(e.getValue()));
- }
- return obj;
- }
- }
步骤五:编写processParams方法
在BeanFactory类中编写一个processParams方法。该方法无返回值类型,参数是1个Node类型变量,1个是 String型变量,方法中把传入的Node节点解析出来,根据String值放入对象的map中。代码如下:
- package tarena.gsd;
- import java.io.FileNotFoundException;
- import java.lang.reflect.Field;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import org.dom4j.Document;
- import org.dom4j.DocumentException;
- import org.dom4j.Element;
- import org.dom4j.Node;
- import org.dom4j.io.SAXReader;
- public class BeanFactory {// 工厂
- /**
- * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
- * value:class属性对应的值
- */
- private static Map<String, String> beanMap = new HashMap<String, String>();
- // key:bean的name属性值
- // value:bean中的参数
- private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
- private static void processParams(Node n, String key) {
- Element e = (Element) n;
- @SuppressWarnings("unchecked")
- Iterator<Element> it = e.elementIterator("param");
- Map<String, String> map1 = new HashMap<String, String>();
- while (it.hasNext()) {
- Element e1 = it.next();
- String k = e1.attributeValue("name");
- String v = e1.attributeValue("value");
- map1.put(k, v);
- }
- paramMap.put(key, map1);
- }
- // alt+shift+m (快速提取代码块为方法)
- private static Document parse(String fileName) throws DocumentException,
- FileNotFoundException {
- // 1.构建解析器对象
- SAXReader r = new SAXReader();
- // 2.解析文件
- // return r.read(
- // new FileInputStream(fileName));
- return r.read(BeanFactory.class.// 获得类对象
- getClassLoader(). // 类加载器对象(负责将类读到内存的对象)
- getResourceAsStream(fileName));// 从类路径读取
- }
- public static Object newInstance(String key) throws Exception {
- // 通过反射构建类的对象
- Class<?> c = Class.forName(beanMap.get(key));// 类对象
- Object obj = c.newInstance();// 构建类的对象
- // 通过反射给类的属性赋值
- Map<String, String> params = paramMap.get(key);
- // 遍历Map(首先应将map转换为set)
- Set<Entry<String, String>> es = params.entrySet();
- // 遍历set
- for (Entry<String, String> e : es) {
- // 通过类对象获得属性对象
- Field f = c.getDeclaredField(e.getKey());
- // 设置属性对象可访问
- f.setAccessible(true);// 设置属性可访问
- // 给obj对象的f属性赋值
- f.set(obj, Integer.valueOf(e.getValue()));
- }
- return obj;
- }
- }
步骤六:编写processData方法
在BeanFactory类中编写一个processData方法,方法无返回值类型,参数是1个Document类型变量,在方法中把传入的Document对象解析出来,放入相应的map中。代码如下:
- package tarena.gsd;
- import java.io.FileNotFoundException;
- import java.lang.reflect.Field;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import org.dom4j.Document;
- import org.dom4j.DocumentException;
- import org.dom4j.Element;
- import org.dom4j.Node;
- import org.dom4j.io.SAXReader;
- public class BeanFactory {// 工厂
- /**
- * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
- * value:class属性对应的值
- */
- private static Map<String, String> beanMap = new HashMap<String, String>();
- // key:bean的name属性值
- // value:bean中的参数
- private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
- // 当类中有多个静态属性(类变量)或静态代码块时
- // 其执行顺序是从上到下
- private static void processData(Document doc) {
- // 3.处理数据
- // 3.1获得所有的Bean节点
- @SuppressWarnings("unchecked")
- List<Node> nodes = doc.selectNodes("/beans/bean");// xpath
- // 3.2遍历Bean节点,取出每个节点的内容封装到Map
- for (Node n : nodes) {
- String key = n.valueOf("@name");
- String value = n.valueOf("@class");
- beanMap.put(key, value);
- processParams(n, key);
- }
- }
- private static void processParams(Node n, String key) {
- Element e = (Element) n;
- @SuppressWarnings("unchecked")
- Iterator<Element> it = e.elementIterator("param");
- Map<String, String> map1 = new HashMap<String, String>();
- while (it.hasNext()) {
- Element e1 = it.next();
- String k = e1.attributeValue("name");
- String v = e1.attributeValue("value");
- map1.put(k, v);
- }
- paramMap.put(key, map1);
- }
- // alt+shift+m (快速提取代码块为方法)
- private static Document parse(String fileName) throws DocumentException,
- FileNotFoundException {
- // 1.构建解析器对象
- SAXReader r = new SAXReader();
- // 2.解析文件
- // return r.read(
- // new FileInputStream(fileName));
- return r.read(BeanFactory.class.// 获得类对象
- getClassLoader(). // 类加载器对象(负责将类读到内存的对象)
- getResourceAsStream(fileName));// 从类路径读取
- }
- public static Object newInstance(String key) throws Exception {
- // 通过反射构建类的对象
- Class<?> c = Class.forName(beanMap.get(key));// 类对象
- Object obj = c.newInstance();// 构建类的对象
- // 通过反射给类的属性赋值
- Map<String, String> params = paramMap.get(key);
- // 遍历Map(首先应将map转换为set)
- Set<Entry<String, String>> es = params.entrySet();
- // 遍历set
- for (Entry<String, String> e : es) {
- // 通过类对象获得属性对象
- Field f = c.getDeclaredField(e.getKey());
- // 设置属性对象可访问
- f.setAccessible(true);// 设置属性可访问
- // 给obj对象的f属性赋值
- f.set(obj, Integer.valueOf(e.getValue()));
- }
- return obj;
- }
- public static void main(String[] args) throws Exception {
- // System.out.println(map);
- // System.out.println(paramMap);
- Point p = (Point) BeanFactory.newInstance("point");
- System.out.println(p);
- }
- }
步骤七:编写loadBeans方法
在BeanFactory类中编写一个loadBeans方法,方法无返回值类型,参数是1个String类型变量,在方法中调用parse方法把传入的String变量传入方法,获取Document对象,再调用processData把Document对象放入。代码如下:
- package tarena.gsd;
- import java.io.FileNotFoundException;
- import java.lang.reflect.Field;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import org.dom4j.Document;
- import org.dom4j.DocumentException;
- import org.dom4j.Element;
- import org.dom4j.Node;
- import org.dom4j.io.SAXReader;
- public class BeanFactory {// 工厂
- /**
- * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
- * value:class属性对应的值
- */
- private static Map<String, String> beanMap = new HashMap<String, String>();
- // key:bean的name属性值
- // value:bean中的参数
- private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
- // 此方法将读取的内容封装到map
- private static void loadBeans(String fileName) throws Exception {
- // 解析文件
- Document doc = parse(fileName);
- // 处理数据
- processData(doc);
- }
- private static void processData(Document doc) {
- // 3.处理数据
- // 3.1获得所有的Bean节点
- @SuppressWarnings("unchecked")
- List<Node> nodes = doc.selectNodes("/beans/bean");// xpath
- // 3.2遍历Bean节点,取出每个节点的内容封装到Map
- for (Node n : nodes) {
- String key = n.valueOf("@name");
- String value = n.valueOf("@class");
- beanMap.put(key, value);
- processParams(n, key);
- }
- }
- private static void processParams(Node n, String key) {
- Element e = (Element) n;
- @SuppressWarnings("unchecked")
- Iterator<Element> it = e.elementIterator("param");
- Map<String, String> map1 = new HashMap<String, String>();
- while (it.hasNext()) {
- Element e1 = it.next();
- String k = e1.attributeValue("name");
- String v = e1.attributeValue("value");
- map1.put(k, v);
- }
- paramMap.put(key, map1);
- }
- // alt+shift+m (快速提取代码块为方法)
- private static Document parse(String fileName) throws DocumentException,
- FileNotFoundException {
- // 1.构建解析器对象
- SAXReader r = new SAXReader();
- // 2.解析文件
- // return r.read(
- // new FileInputStream(fileName));
- return r.read(BeanFactory.class.// 获得类对象
- getClassLoader(). // 类加载器对象(负责将类读到内存的对象)
- getResourceAsStream(fileName));// 从类路径读取
- }
- public static Object newInstance(String key) throws Exception {
- // 通过反射构建类的对象
- Class<?> c = Class.forName(beanMap.get(key));// 类对象
- Object obj = c.newInstance();// 构建类的对象
- // 通过反射给类的属性赋值
- Map<String, String> params = paramMap.get(key);
- // 遍历Map(首先应将map转换为set)
- Set<Entry<String, String>> es = params.entrySet();
- // 遍历set
- for (Entry<String, String> e : es) {
- // 通过类对象获得属性对象
- Field f = c.getDeclaredField(e.getKey());
- // 设置属性对象可访问
- f.setAccessible(true);// 设置属性可访问
- // 给obj对象的f属性赋值
- f.set(obj, Integer.valueOf(e.getValue()));
- }
- return obj;
- }
- public static void main(String[] args) throws Exception {
- // System.out.println(map);
- // System.out.println(paramMap);
- Point p = (Point) BeanFactory.newInstance("point");
- System.out.println(p);
- }
- }
步骤八:静态加载
在BeanFactory类中编写一个静态代码块,代码块中调用loadBeans方法,去加载xml文件。代码如下:
- package tarena.gsd;
- import java.io.FileNotFoundException;
- import java.lang.reflect.Field;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import org.dom4j.Document;
- import org.dom4j.DocumentException;
- import org.dom4j.Element;
- import org.dom4j.Node;
- import org.dom4j.io.SAXReader;
- public class BeanFactory {// 工厂
- /**
- * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
- * value:class属性对应的值
- */
- private static Map<String, String> beanMap = new HashMap<String, String>();
- // key:bean的name属性值
- // value:bean中的参数
- private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
- // 当类中有多个静态属性(类变量)或静态代码块时
- // 其执行顺序是从上到下
- static {
- try {
- loadBeans("beans.xml");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- // 此方法将读取的内容封装到map
- private static void loadBeans(String fileName) throws Exception {
- // 解析文件
- Document doc = parse(fileName);
- // 处理数据
- processData(doc);
- }
- private static void processData(Document doc) {
- // 3.处理数据
- // 3.1获得所有的Bean节点
- @SuppressWarnings("unchecked")
- List<Node> nodes = doc.selectNodes("/beans/bean");// xpath
- // 3.2遍历Bean节点,取出每个节点的内容封装到Map
- for (Node n : nodes) {
- String key = n.valueOf("@name");
- String value = n.valueOf("@class");
- beanMap.put(key, value);
- processParams(n, key);
- }
- }
- private static void processParams(Node n, String key) {
- Element e = (Element) n;
- @SuppressWarnings("unchecked")
- Iterator<Element> it = e.elementIterator("param");
- Map<String, String> map1 = new HashMap<String, String>();
- while (it.hasNext()) {
- Element e1 = it.next();
- String k = e1.attributeValue("name");
- String v = e1.attributeValue("value");
- map1.put(k, v);
- }
- paramMap.put(key, map1);
- }
- // alt+shift+m (快速提取代码块为方法)
- private static Document parse(String fileName) throws DocumentException,
- FileNotFoundException {
- // 1.构建解析器对象
- SAXReader r = new SAXReader();
- // 2.解析文件
- // return r.read(
- // new FileInputStream(fileName));
- return r.read(BeanFactory.class.// 获得类对象
- getClassLoader(). // 类加载器对象(负责将类读到内存的对象)
- getResourceAsStream(fileName));// 从类路径读取
- }
- public static Object newInstance(String key) throws Exception {
- // 通过反射构建类的对象
- Class<?> c = Class.forName(beanMap.get(key));// 类对象
- Object obj = c.newInstance();// 构建类的对象
- // 通过反射给类的属性赋值
- Map<String, String> params = paramMap.get(key);
- // 遍历Map(首先应将map转换为set)
- Set<Entry<String, String>> es = params.entrySet();
- // 遍历set
- for (Entry<String, String> e : es) {
- // 通过类对象获得属性对象
- Field f = c.getDeclaredField(e.getKey());
- // 设置属性对象可访问
- f.setAccessible(true);// 设置属性可访问
- // 给obj对象的f属性赋值
- f.set(obj, Integer.valueOf(e.getValue()));
- }
- return obj;
- }
- public static void main(String[] args) throws Exception {
- // System.out.println(map);
- // System.out.println(paramMap);
- Point p = (Point) BeanFactory.newInstance("point");
- System.out.println(p);
- }
- }
1.4 完整代码
本案例Point类的完整代码如下所示:
代码
本案例客户端的完整代码如下所示:
代码
2 实现群聊天室
2.1 问题
要求编写一个Android程序,界面如图-1所示:
图-1
点击连接按钮后可以建立与服务器连接,连接后点击发送可以把发送的文本显示在所有连接客户端的ListView中,实现类似群聊的功能。
2.2 方案
先编写服务器程序,使用Vector 对象来存储所有连接上的客户端,使用ServerSocket来监听客户端连接,连接成功后存放入Vector中,在创建一个线程用于读取客户发送消息,并遍历Vector,把消息发送给所有客户端。
再编写Android客户端,按照界面要求编写界面,再编写Activity,在点击连接按钮后开启新线程,再使用Socket与服务端建立连接。在点击发送按钮后开启新线程,把输入框内容发送给服务端。在主程序中再开启一个新线程用于接收服务端发送过来的消息,并显示在ListView中。
2.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:编写布局文件
创建Android项目,相对布局编写布局文件,布局文件中放入2个EditText,2个按钮和1个ListView。代码如下所示:
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".MainActivity" >
- <LinearLayout
- android:id="@+id/linearLayout1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true" >
- <EditText
- android:id="@+id/nickname"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:ems="10"
- android:enabled="false">
- <requestFocus />
- </EditText>
- <Button
- android:id="@+id/connect"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:onClick="doClick"
- android:text="建立连接" />
- </LinearLayout>
- <ListView
- android:id="@+id/listView"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_above="@+id/LinearLayout01"
- android:layout_alignParentLeft="true"
- android:layout_below="@+id/linearLayout1" >
- </ListView>
- <LinearLayout
- android:id="@+id/LinearLayout01"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true" >
- <EditText
- android:id="@+id/text"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:ems="10"
- android:hint="请输入发送文本" />
- <Button
- android:id="@+id/send"
- android:layout_width="wrap_content"
- android:onClick="doClick"
- android:layout_height="wrap_content"
- android:text="发送" />
- </LinearLayout>
- </RelativeLayout>
步骤二:编写MainActivity类
编写MainActivity类,类中编写一个init方法,方法中获取界面的所有控件,并赋值给成员变量。在onCreate方法中调用init。在定义一个Handler类型的成员变量,实例化这个变量,并复写handleMessage方法,在方法中使用switch语句处理不同的消息。代码如下所示:
- package com.tarena.thread_socket_day05_cookbook_02_client;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.net.Socket;
- import java.net.SocketAddress;
- import java.util.ArrayList;
- import java.util.List;
- import android.os.Bundle;
- import android.os.Handler;
- import android.app.Activity;
- import android.view.Menu;
- import android.view.View;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.ListView;
- import android.widget.Toast;
- public class MainActivity extends Activity {
- private EditText etNickname;
- private EditText etText;
- private Button connect;
- private boolean isConnected;
- private ListView lv;
- private List<String> messages=new ArrayList<String>();
- private ArrayAdapter<String> adapter;
- private Socket socket;
- private DataInputStream is;
- private DataOutputStream os;
- private Handler handler=new Handler(){
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case HANDLER_CONNECT_SUCCESS:
- connect.setText("连接已建立");
- etNickname.setText(socket.getInetAddress().toString());
- etNickname.setEnabled(false);
- connect.setEnabled(false);
- isConnected = true;
- //把messages中的数据呈现在listView中
- adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, messages);
- lv.setAdapter(adapter);
- // 启动读取服务端返回聊天记录的线程
- new Reader().start();
- break;
- case HANDLER_RECEIVE_MESSAGE:
- adapter.notifyDataSetChanged();
- break;
- case HANDLER_CONNECT_TIMEOUT:
- Toast.makeText(MainActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
- break;
- }
- }
- };
- public static final int HANDLER_CONNECT_SUCCESS = 0;
- public static final int HANDLER_RECEIVE_MESSAGE = 1;
- public static final int HANDLER_CONNECT_TIMEOUT = 2;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- init();
- }
- private void init() {
- etNickname = (EditText) findViewById(R.id.nickname);
- etText = (EditText) findViewById(R.id.text);
- connect = (Button) findViewById(R.id.connect);
- lv = (ListView) findViewById(R.id.listView);
- }
- }
步骤三:编写客户端读取线程类
编写一个内部类Reader继承自Thread,复写run方法,在方法中使用无限循环,不断读取服务器发来的信息,接收后发消息通知界面去更新。代码如下所示:
- package com.tarena.thread_socket_day05_cookbook_02_client;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.net.Socket;
- import java.net.SocketAddress;
- import java.util.ArrayList;
- import java.util.List;
- import android.os.Bundle;
- import android.os.Handler;
- import android.app.Activity;
- import android.view.Menu;
- import android.view.View;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.ListView;
- import android.widget.Toast;
- public class MainActivity extends Activity {
- private EditText etNickname;
- private EditText etText;
- private Button connect;
- private boolean isConnected;
- private ListView lv;
- private List<String> messages=new ArrayList<String>();
- private ArrayAdapter<String> adapter;
- private Socket socket;
- private DataInputStream is;
- private DataOutputStream os;
- private Handler handler=new Handler(){
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case HANDLER_CONNECT_SUCCESS:
- connect.setText("连接已建立");
- etNickname.setText(socket.getInetAddress().toString());
- etNickname.setEnabled(false);
- connect.setEnabled(false);
- isConnected = true;
- //把messages中的数据呈现在listView中
- adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, messages);
- lv.setAdapter(adapter);
- // 启动读取服务端返回聊天记录的线程
- new Reader().start();
- break;
- case HANDLER_RECEIVE_MESSAGE:
- adapter.notifyDataSetChanged();
- break;
- case HANDLER_CONNECT_TIMEOUT:
- Toast.makeText(MainActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
- break;
- }
- }
- };
- public static final int HANDLER_CONNECT_SUCCESS = 0;
- public static final int HANDLER_RECEIVE_MESSAGE = 1;
- public static final int HANDLER_CONNECT_TIMEOUT = 2;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- init();
- }
- private void init() {
- etNickname = (EditText) findViewById(R.id.nickname);
- etText = (EditText) findViewById(R.id.text);
- connect = (Button) findViewById(R.id.connect);
- lv = (ListView) findViewById(R.id.listView);
- }
- class Reader extends Thread {
- public Reader() {
- }
- // 接收服务端输入进客户端的信息 并且显示在ListView中
- public void run() {
- try {
- while (isConnected) {
- String data = is.readUTF();
- //更新listView的数据源
- messages.add(data);
- //发消息给Handler 更新listview
- handler.sendEmptyMessage(HANDLER_RECEIVE_MESSAGE);
- }
- is.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
步骤四:编写send方法
编写MainActivity中编写send方法,在方法中获取输入框内容,开启新线程把内容发送给服务端。代码如下所示:
- package com.tarena.thread_socket_day05_cookbook_02_client;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.net.Socket;
- import java.net.SocketAddress;
- import java.util.ArrayList;
- import java.util.List;
- import android.os.Bundle;
- import android.os.Handler;
- import android.app.Activity;
- import android.view.Menu;
- import android.view.View;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.ListView;
- import android.widget.Toast;
- public class MainActivity extends Activity {
- private EditText etNickname;
- private EditText etText;
- private Button connect;
- private boolean isConnected;
- private ListView lv;
- private List<String> messages=new ArrayList<String>();
- private ArrayAdapter<String> adapter;
- private Socket socket;
- private DataInputStream is;
- private DataOutputStream os;
- private Handler handler=new Handler(){
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case HANDLER_CONNECT_SUCCESS:
- connect.setText("连接已建立");
- etNickname.setText(socket.getInetAddress().toString());
- etNickname.setEnabled(false);
- connect.setEnabled(false);
- isConnected = true;
- //把messages中的数据呈现在listView中
- adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, messages);
- lv.setAdapter(adapter);
- // 启动读取服务端返回聊天记录的线程
- new Reader().start();
- break;
- case HANDLER_RECEIVE_MESSAGE:
- adapter.notifyDataSetChanged();
- break;
- case HANDLER_CONNECT_TIMEOUT:
- Toast.makeText(MainActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
- break;
- }
- }
- };
- public static final int HANDLER_CONNECT_SUCCESS = 0;
- public static final int HANDLER_RECEIVE_MESSAGE = 1;
- public static final int HANDLER_CONNECT_TIMEOUT = 2;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- init();
- }
- private void init() {
- etNickname = (EditText) findViewById(R.id.nickname);
- etText = (EditText) findViewById(R.id.text);
- connect = (Button) findViewById(R.id.connect);
- lv = (ListView) findViewById(R.id.listView);
- }
- //发送消息
- private void send() {
- if(!isConnected){
- Toast.makeText(this, "请先建立连接", Toast.LENGTH_SHORT).show();
- return;
- }
- //发送消息
- //获取输出流输出 网络访问需要在工作线程中完成
- new Thread(){
- public void run() {
- try {
- String text=etText.getText().toString();
- os.writeUTF(text);
- os.flush();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }.start();
- }
- class Reader extends Thread {
- public Reader() {
- }
- // 接收服务端输入进客户端的信息 并且显示在ListView中
- public void run() {
- try {
- while (isConnected) {
- String data = is.readUTF();
- //更新listView的数据源
- messages.add(data);
- //发消息给Handler 更新listview
- handler.sendEmptyMessage(HANDLER_RECEIVE_MESSAGE);
- }
- is.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
步骤五:编写connect方法
编写MainActivity中编写connect方法,在方法中开启新线程,使用Socket对象建立与服务端连接。代码如下所示:
- package com.tarena.thread_socket_day05_cookbook_02_client;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.net.Socket;
- import java.net.SocketAddress;
- import java.util.ArrayList;
- import java.util.List;
- import android.os.Bundle;
- import android.os.Handler;
- import android.app.Activity;
- import android.view.Menu;
- import android.view.View;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.ListView;
- import android.widget.Toast;
- public class MainActivity extends Activity {
- private EditText etNickname;
- private EditText etText;
- private Button connect;
- private boolean isConnected;
- private ListView lv;
- private List<String> messages=new ArrayList<String>();
- private ArrayAdapter<String> adapter;
- private Socket socket;
- private DataInputStream is;
- private DataOutputStream os;
- private Handler handler=new Handler(){
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case HANDLER_CONNECT_SUCCESS:
- connect.setText("连接已建立");
- etNickname.setText(socket.getInetAddress().toString());
- etNickname.setEnabled(false);
- connect.setEnabled(false);
- isConnected = true;
- //把messages中的数据呈现在listView中
- adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, messages);
- lv.setAdapter(adapter);
- // 启动读取服务端返回聊天记录的线程
- new Reader().start();
- break;
- case HANDLER_RECEIVE_MESSAGE:
- adapter.notifyDataSetChanged();
- break;
- case HANDLER_CONNECT_TIMEOUT:
- Toast.makeText(MainActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
- break;
- }
- }
- };
- public static final int HANDLER_CONNECT_SUCCESS = 0;
- public static final int HANDLER_RECEIVE_MESSAGE = 1;
- public static final int HANDLER_CONNECT_TIMEOUT = 2;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- init();
- }
- private void init() {
- etNickname = (EditText) findViewById(R.id.nickname);
- etText = (EditText) findViewById(R.id.text);
- connect = (Button) findViewById(R.id.connect);
- lv = (ListView) findViewById(R.id.listView);
- }
- //发送消息
- private void send() {
- if(!isConnected){
- Toast.makeText(this, "请先建立连接", Toast.LENGTH_SHORT).show();
- return;
- }
- //发送消息
- //获取输出流输出 网络访问需要在工作线程中完成
- new Thread(){
- public void run() {
- try {
- String text=etText.getText().toString();
- os.writeUTF(text);
- os.flush();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }.start();
- }
- // 建立连接
- private void connect(){
- //访问网络 须在工作线程
- new Thread(){
- public void run() {
- try {
- socket = new Socket();
- SocketAddress address=new InetSocketAddress("192.168.1.107", 9999);
- //3秒没有连接 将会抛出TimeOut异常
- socket.connect(address, 3000);
- is=new DataInputStream(socket.getInputStream());
- os=new DataOutputStream(socket.getOutputStream());
- //不出异常 连接建立成功发消息给handler
- handler.sendEmptyMessage(HANDLER_CONNECT_SUCCESS);
- } catch (IOException e) {
- e.printStackTrace();
- handler.sendEmptyMessage(HANDLER_CONNECT_TIMEOUT);
- }
- }
- }.start();
- }
- class Reader extends Thread {
- public Reader() {
- }
- // 接收服务端输入进客户端的信息 并且显示在ListView中
- public void run() {
- try {
- while (isConnected) {
- String data = is.readUTF();
- //更新listView的数据源
- messages.add(data);
- //发消息给Handler 更新listview
- handler.sendEmptyMessage(HANDLER_RECEIVE_MESSAGE);
- }
- is.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
步骤六:编写doClick方法
编写MainActivity中编写doClick方法,方法为按钮的点击处理方法,根据不同的按钮控件id,分别调用connect方法和send方法。代码如下所示:
- package com.tarena.thread_socket_day05_cookbook_02_client;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.net.Socket;
- import java.net.SocketAddress;
- import java.util.ArrayList;
- import java.util.List;
- import android.os.Bundle;
- import android.os.Handler;
- import android.app.Activity;
- import android.view.Menu;
- import android.view.View;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.ListView;
- import android.widget.Toast;
- public class MainActivity extends Activity {
- private EditText etNickname;
- private EditText etText;
- private Button connect;
- private boolean isConnected;
- private ListView lv;
- private List<String> messages=new ArrayList<String>();
- private ArrayAdapter<String> adapter;
- private Socket socket;
- private DataInputStream is;
- private DataOutputStream os;
- private Handler handler=new Handler(){
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case HANDLER_CONNECT_SUCCESS:
- connect.setText("连接已建立");
- etNickname.setText(socket.getInetAddress().toString());
- etNickname.setEnabled(false);
- connect.setEnabled(false);
- isConnected = true;
- //把messages中的数据呈现在listView中
- adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, messages);
- lv.setAdapter(adapter);
- // 启动读取服务端返回聊天记录的线程
- new Reader().start();
- break;
- case HANDLER_RECEIVE_MESSAGE:
- adapter.notifyDataSetChanged();
- break;
- case HANDLER_CONNECT_TIMEOUT:
- Toast.makeText(MainActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
- break;
- }
- }
- };
- public static final int HANDLER_CONNECT_SUCCESS = 0;
- public static final int HANDLER_RECEIVE_MESSAGE = 1;
- public static final int HANDLER_CONNECT_TIMEOUT = 2;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- init();
- }
- private void init() {
- etNickname = (EditText) findViewById(R.id.nickname);
- etText = (EditText) findViewById(R.id.text);
- connect = (Button) findViewById(R.id.connect);
- lv = (ListView) findViewById(R.id.listView);
- }
- public void doClick(View view) {
- switch (view.getId()) {
- case R.id.connect:
- connect();
- break;
- case R.id.send:
- send();
- break;
- }
- }
- //发送消息
- private void send() {
- if(!isConnected){
- Toast.makeText(this, "请先建立连接", Toast.LENGTH_SHORT).show();
- return;
- }
- //发送消息
- //获取输出流输出 网络访问需要在工作线程中完成
- new Thread(){
- public void run() {
- try {
- String text=etText.getText().toString();
- os.writeUTF(text);
- os.flush();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }.start();
- }
- // 建立连接
- private void connect(){
- //访问网络 须在工作线程
- new Thread(){
- public void run() {
- try {
- socket = new Socket();
- SocketAddress address=new InetSocketAddress("192.168.1.107", 9999);
- //3秒没有连接 将会抛出TimeOut异常
- socket.connect(address, 3000);
- is=new DataInputStream(socket.getInputStream());
- os=new DataOutputStream(socket.getOutputStream());
- //不出异常 连接建立成功发消息给handler
- handler.sendEmptyMessage(HANDLER_CONNECT_SUCCESS);
- } catch (IOException e) {
- e.printStackTrace();
- handler.sendEmptyMessage(HANDLER_CONNECT_TIMEOUT);
- }
- }
- }.start();
- }
- class Reader extends Thread {
- public Reader() {
- }
- // 接收服务端输入进客户端的信息 并且显示在ListView中
- public void run() {
- try {
- while (isConnected) {
- String data = is.readUTF();
- //更新listView的数据源
- messages.add(data);
- //发消息给Handler 更新listview
- handler.sendEmptyMessage(HANDLER_RECEIVE_MESSAGE);
- }
- is.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
步骤七:服务端程序
创建一个java项目作为服务端程序,创建包,包中创建类Server,类中使用定义一个Vector 对象来存储所有连接上的客户端,再使用ServerSocket来监听客户端连接,连接成功后存放入Vector中。再编写一个线程内部类,用于读取客户发送消息,并遍历Vector,把消息发送给所有客户端。再main方法中开始连接监听。代码如下所示:
- package tarena.gsd;
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.Vector;
- public class Server {
- //使用Vector保存客户端的所有连接 使用方式同ArrayList 优点:线程安全
- private Vector<Client> clients=new Vector<Client>();
- public void startServer() throws IOException {
- ServerSocket ss=new ServerSocket(9999);
- //接收客户端请求
- while(true){
- Socket socket=ss.accept();
- Client c=new Client(socket);
- clients.add(c);
- c.start();
- }
- }
- public static void main(String[] args) throws IOException {
- Server server=new Server();
- server.startServer();
- }
- class Client extends Thread{
- Socket socket;
- DataInputStream is;
- DataOutputStream os;
- public Client(Socket socket) {
- this.socket=socket;
- try {
- this.is=new DataInputStream(socket.getInputStream());
- this.os=new DataOutputStream(socket.getOutputStream());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- //start后 接收客户端传进的数据 并且把这些数据输出给每一个客户端
- public void run() {
- boolean isLoop=true;
- while(isLoop){
- //接收客户端传来的数据
- try {
- String msg=is.readUTF();
- System.out.println(msg);
- //若用户输入的exit 则退出
- if(msg.equals("exit")){
- isLoop=false; //退出循环
- socket.close();
- clients.remove(this);
- return;
- }else{
- for(int i=0; i<clients.size(); i++){
- clients.get(i).os.writeUTF(socket.getInetAddress()+"\n"+msg);
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- break;
- }
- }
- }
- }
- }
2.4 完整代码
本案例布局文件的完整代码如下所示:
代码
本案例MainActivity的完整代码如下所示:
代码
本案例服务端Server类的完整代码如下所示:
代码
- Android 群聊天室
- android聊天室
- Android聊天室
- 【聊天室】android 简单的聊天室
- android实现简单聊天室
- android 开放聊天室
- Android开发之聊天室
- android 聊天室窗口
- Android 聊天室的开发
- Android Socket 聊天室
- Android socket聊天室
- Android Socket之聊天室
- 蓝牙聊天室(Android版)
- Android 基于Socket的聊天室
- android开发一个聊天室client
- android 基于socket的聊天室
- Android 基于Socket的聊天室
- Android 基于Socket的聊天室
- xml的创建 c++哦
- hdu2544 最短路 模板题
- 常用sql语句之删除或查询单个字段重复数据的记录
- iOS-UIWebview比例缩放
- Hello,world!是如何运行到我的手机的呢
- Android 群聊天室
- Hibernate(十三)Session管理
- 理解钩子Hook以及在Thinkphp下利用钩子使用行为扩展
- 图片自动父元素小大
- Linux的网络协议中的网卡缓冲区
- 直接拿来用!十大Material Design开源项目
- android自动连接到指定wifi
- python 不是内部或外部命令
- hdu 5289 Assignment(ST表+双指针)