Android之Socket群组聊天
2016-04-29 11:07
344 查看
在这只做了一个简单的例子,没有用到数据库,思路就是客户端发送信息到服务器端,服务器端转发所有数据到客户端,校验服务器端发来消息是否是自己发出的,如果是自己发出的,则不显示自己的消息。
项目源码下载
贴一下Android客户端的源码 -
MainActivity.java
Msg.java 消息实体类
MyAdapter 数据适配器
ViewHoder 一个超实用的通用ViewHoder类
好了,客户端的源码贴完了,接下来服务器端的源码就比较简单了
Client.java 重写一个子线程
Server 服务器端,先运行起它来,再去部署客户端即可
项目源码下载
贴一下Android客户端的源码 -
MainActivity.java
package com.zml.chatproject; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.Toast; import com.google.gson.Gson; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.util.ArrayList; import java.util.List; /** * @author 郑明亮 @email 1072307340@qq.com * @Time:2016/4/20 14:25 * @version 1.0 * TODO */ public class MainActivity extends AppCompatActivity implements View.OnClickListener, TextWatcher { private static final String TAG = "MainActivity"; public static final int SENDMESSAGE = 0x004; public static final String name = System.currentTimeMillis()+""; List<Msg> list ; ListView mListView; EditText edit; Button bt_send; Socket socket; public static final int SHOWMSG = 0x003; private DataInputStream mDataInputStream = null; private DataOutputStream mDataOutputStream = null; private boolean Conneted = false; Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i(TAG,"执行到handle"); if (msg.what == SENDMESSAGE) { Msg xiaoxi = (Msg) msg.obj; Log.i(TAG,"handler:"+xiaoxi.toString()); list.add(xiaoxi); //设置适配器 mListView.setAdapter(new MyAdapter(MainActivity.this, list)); mListView.setSelection(mListView.getCount()-1); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mListView = (ListView) findViewById(R.id.listView); edit = (EditText) findViewById(R.id.et_edit); edit.addTextChangedListener(this); bt_send = (Button) findViewById(R.id.bt_send); bt_send.setOnClickListener(this); list = new ArrayList<>(); new AsyncTask<Void,Void,String>(){ @Override protected String doInBackground(Void... params) { try { socket = new Socket("172.18.40.182", 9999); // socket = new Socket("115.28.167.152", 9999); connect(); ClientThread thread = new ClientThread(); thread.run(); } catch (IOException e) { e.printStackTrace(); } return null; } }.execute(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (event.getAction()){ case KeyEvent.KEYCODE_HOME: Toast.makeText(MainActivity.this,"就是不让你退出,O(∩_∩)O哈哈哈~",Toast.LENGTH_LONG).show(); Log.i(TAG,"KeyEvent.KEYCODE_HOME"+KeyEvent.KEYCODE_HOME); break; case KeyEvent.KEYCODE_MOVE_HOME: Toast.makeText(MainActivity.this,"就是不让你退出,O(∩_∩)O哈哈哈~",Toast.LENGTH_LONG).show(); Log.i(TAG,"KeyEvent.KEYCODE_MOVE_HOME"+KeyEvent.KEYCODE_MOVE_HOME); break; } return true; } @Override public void onAttachedToWindow() { // TODO Auto-generated method stub this.getWindow().setType(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); super.onAttachedToWindow(); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.bt_send: String show = edit.getText().toString().trim(); if (TextUtils.isEmpty(show)){ }else { edit.setText(""); Msg msg = new Msg(); msg.setFlag(Msg.TO); msg.setMsg(show); msg.setUsername(name); list.add(msg); mListView.setAdapter(new MyAdapter(MainActivity.this,list)); mListView.setSelection(mListView.getCount()-1); try {if (mDataOutputStream==null){ mDataOutputStream = new DataOutputStream(socket.getOutputStream()); } Gson gson = new Gson(); show = gson.toJson(msg); mDataOutputStream.writeUTF(show); mDataOutputStream.flush(); Log.i(TAG,"发送成功:"+show); } catch (IOException e) { e.printStackTrace(); } } break; } } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { bt_send.setEnabled(false); } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (s.length()>0){ bt_send.setEnabled(true); } } @Override public void afterTextChanged(Editable s) { } /** * 开启线程,接收消息 */ private class ClientThread implements Runnable { @Override public void run() { while (Conneted) { try { String str = mDataInputStream.readUTF(); Log.i(TAG,"子线程得到数据:"+str); Gson gson = new Gson(); Msg msg = gson.fromJson(str,Msg.class); msg.setFlag(Msg.FROM); Message message = mHandler.obtainMessage(); message.what = SENDMESSAGE; message.obj = msg; mHandler.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } } /** * 打开连接 */ public void connect() { try { mDataInputStream = new DataInputStream(socket.getInputStream()); mDataOutputStream = new DataOutputStream(socket.getOutputStream()); if (socket.isConnected()){ Log.i(TAG, "连接上了"); }else { Log.i(TAG, "连接失败"); } Conneted = true; } catch (IOException e) { e.printStackTrace(); } } /** * 断开与服务器的连接 */ public void disconnect() { try { mDataInputStream.close(); mDataOutputStream.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } @Override protected void onDestroy() { super.onDestroy(); disconnect(); } }
Msg.java 消息实体类
package com.zml.chatproject; /** * Created by bri on 2016/4/16. */ /** * 消息实体类 */ public class Msg { public static final int FROM = 0x001; public static final int TO = 0x002; /** * 发送聊天消息 */ private String msg; /** * 标识符,表示是发送方还是接收方 */ private int flag; /** * 用户名 */ private String username; @Override public String toString() { return "Msg{" + "msg='" + msg + '\\'' + ", flag=" + flag + ", username='" + username + '\\'' + '}'; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public int getFlag() { return flag; } public void setFlag(int flag) { this.flag = flag; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
MyAdapter 数据适配器
package com.zml.chatproject; import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListAdapter; import android.widget.TextView; import java.util.List; /** * Created by bri on 2016/4/17. */ public class MyAdapter extends BaseAdapter implements ListAdapter { private static final String TAG = "MyAdapter"; Context context; List<Msg> message; public MyAdapter(Context context, List<Msg> message) { this.context = context; this.message = message; } @Override public int getCount() { return message.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null){ convertView = LayoutInflater.from(context).inflate(R.layout.activity_main_item,null); } TextView tv_from = ViewHolder.get(convertView,R.id.tv_chatting_from); TextView tv_to = ViewHolder.get(convertView,R.id.tv_chatting_to); // tv_from.setText(message.getMsg()); Log.i(TAG,"接收成功"+message.get(position).getMsg()); if (message.get(position).getFlag()==(Msg.FROM)){ if (message.get(position).getUsername().equals(MainActivity.name)){ tv_from.setVisibility(View.GONE); tv_to.setVisibility(View.GONE);} else { Log.i(TAG,"接收成功FROM"+message.get(position).getMsg()); tv_from.setText(message.get(position).getMsg()); tv_from.setVisibility(View.VISIBLE); tv_to.setVisibility(View.GONE);} // Toast.makeText(context,"from:"+message.get(position).getMsg(),Toast.LENGTH_LONG).show(); }if (message.get(position).getFlag()==(Msg.TO)){ // Toast.makeText(context,"to:"+message.get(position).getMsg(),Toast.LENGTH_LONG).show(); Log.i(TAG,"接收成功TO"+message.get(position).getMsg()); tv_to.setText(message.get(position).getMsg()); tv_from.setVisibility(View.GONE); tv_to.setVisibility(View.VISIBLE); } return convertView; } }
ViewHoder 一个超实用的通用ViewHoder类
package com.zml.chatproject; import android.util.SparseArray; import android.view.View; /** * Created by bri on 2016/4/17. */ public class ViewHolder { // I added a generic return type to reduce the casting noise in client code @SuppressWarnings("unchecked") public static <T extends View> T get(View view, int id) { SparseArray<View> viewHolder = (SparseArray<View>) view.getTag(); if (viewHolder == null) { viewHolder = new SparseArray<View>(); view.setTag(viewHolder); } View childView = viewHolder.get(id); if (childView == null) { childView = view.findViewById(id); viewHolder.put(id, childView); } return (T) childView; } }
好了,客户端的源码贴完了,接下来服务器端的源码就比较简单了
Client.java 重写一个子线程
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * @author 郑明亮 * @Time:2016年4月16日 下午9:01:53 * @version 1.0 */ public class Client implements Runnable { Socket socket; List<Client> clients ; public static final int SHOWMSG = 0x003; private DataInputStream mDataInputStream = null; private DataOutputStream mDataOutputStream = null; private boolean Conneted = false; public Client(Socket socket){ this.socket = socket; try { mDataInputStream = new DataInputStream(socket.getInputStream()); mDataOutputStream = new DataOutputStream(socket.getOutputStream()); Conneted = true; clients =new ArrayList<Client>(); } catch (IOException e) { e.printStackTrace(); } }; /** * 发送消息 */ public void send(String string){ try { mDataOutputStream.writeUTF(string);//向输入流中写入数据 System.out.println("向输入流中写入数据"); } catch (IOException e) { e.printStackTrace(); // Server.clients.remove(this);//出错时,客户端可能已断线,移除当前客户端 System.out.println("出错时,客户端可能已断线,移除当前客户端"); } } @Override public void run() { while(Conneted){ try { System.out.println("连接成功!"); //读取数据 String str = mDataInputStream.readUTF(); clients = Server.clients; synchronized (clients) { Iterator<Client> iterator =clients.iterator(); while (iterator.hasNext()) { Client client = iterator.next(); //将读取的数据发送回去 System.out.println("将读取的数据发送回去"+str); client.send(str); } // Conneted = false; } } catch (IOException e) { e.printStackTrace(); Server.clients.remove(this); System.out.println("线程出现异常"); }finally{ // try { //// mDataOutputStream.close(); //// mDataInputStream.close(); // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } } } }
Server 服务器端,先运行起它来,再去部署客户端即可
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author 郑明亮 * @Time:2016年4月16日 下午9:01:43 * @version 1.0 */ public class Server { static boolean started = false; public static List<Client> clients = new ArrayList<>(); public static void main(String a[]){ try { ServerSocket serverSocket = new ServerSocket(9999); System.out.println("开启服务,等待监听"); started = true; while (started) { System.out.println("开始监听"); Socket socket = serverSocket.accept(); Client client = new Client(socket); new Thread(client).start(); clients.add(client); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
相关文章推荐
- 自定义view之柱状图
- android学习笔记—AIDL的使用步骤
- Android-实时将下载图片加入相册中
- Android之Sensor 简介
- Android自定义View之圆形ImageView--RoundImageView
- Android Fragment的三种应用方式
- hjr-Android:关于Log
- Android四大布局之表格布局行列位置控制
- Android中service
- android:padding和android:margin的区别
- Android 常用抓包工具介绍之Charles
- android基础学习之popupwindow
- Android TextView边框和背景,以及圆形背景
- Android 常用抓包工具介绍之Charles
- Android Studio安装过程问题汇总
- Android Studio安装过程问题汇总
- Android Studio安装过程问题汇总
- Android Studio安装过程问题汇总
- Android Studio安装过程问题汇总
- Android实现在子线程中更新Activity中UI的方法