利用socket实现聊天-Android端核心代码
实体类与服务端报错一致,包括包名
package loaderman.im.bean; /** * 一个聊天消息的JavaBean * * */ public class ChatMsgEntity { private String name;// 消息来自 private String date;// 消息日期 private String message;// 消息内容 private String img; //头像 private boolean msgType = true;// 是否为收到的消息 public ChatMsgEntity() { } public ChatMsgEntity(String name, String date, String text, String img, boolean msgType) { super(); this.name = name; this.date = date; this.message = text; this.img = img; this.msgType = msgType; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public boolean getMsgType() { return msgType; } public void setMsgType(boolean isComMsg) { msgType = isComMsg; } public String getImg() { return img; } public void setImg(String img) { this.img = img; } }
package loaderman.im.bean; import java.io.Serializable; public class IMMessage implements Serializable { private String userId; private String userName; private String userHeadPhoto; private int messageType; private String lastMsgContent; private String time; private int noReadCount; public IMMessage(String userID, String userName, String userHeadPhoto, int messageType, String lastMsgContent, String time) { this.userId = userID; this.userName = userName; this.userHeadPhoto = userHeadPhoto; this.messageType = messageType; this.lastMsgContent = lastMsgContent; this.time = time; } public IMMessage(String userId, String userName, String userHeadPhoto, int messageType, String lastMsgContent, String time, int noReadCount) { this.userId = userId; this.userName = userName; this.userHeadPhoto = userHeadPhoto; this.messageType = messageType; this.lastMsgContent = lastMsgContent; this.time = time; this.noReadCount = noReadCount; } public int getNoReadCount() { return noReadCount; } public void setNoReadCount(int noReadCount) { this.noReadCount = noReadCount; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserHeadPhoto() { return userHeadPhoto; } public void setUserHeadPhoto(String userHeadPhoto) { this.userHeadPhoto = userHeadPhoto; } public int getMessageType() { return messageType; } public void setMessageType(int messageType) { this.messageType = messageType; } public String getLastMsgContent() { return lastMsgContent; } public void setLastMsgContent(String lastMsgContent) { this.lastMsgContent = lastMsgContent; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } }
package loaderman.im.bean; import java.io.Serializable; public class OffLineMessage implements Serializable { private String fromUser; private String toUser; private String textMsg; private int msgType; private String time; public OffLineMessage() { } public OffLineMessage(String fromUser, String toUser, String textMsg, int msgType, String time) { this.fromUser = fromUser; this.toUser = toUser; this.textMsg = textMsg; this.msgType = msgType; this.time = time; } public String getFromUser() { return fromUser; } public void setFromUser(String fromUser) { this.fromUser = fromUser; } public String getToUser() { return toUser; } public void setToUser(String toUser) { this.toUser = toUser; } public String getTextMsg() { return textMsg; } public void setTextMsg(String textMsg) { this.textMsg = textMsg; } public int getMsgType() { return msgType; } public void setMsgType(int msgType) { this.msgType = msgType; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } }
package loaderman.im.bean; import java.io.Serializable; /** * 文本消息 * */ public class TextMessage implements Serializable { /** * */ private static final long serialVersionUID = 1L; private String message; public TextMessage() { } public TextMessage(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
package loaderman.im.bean; import java.io.Serializable; public class User implements Serializable { private String userId; private String userName; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } private String userHeadPhoto; private String password; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserHeadPhoto() { return userHeadPhoto; } public void setUserHeadPhoto(String userHeadPhoto) { this.userHeadPhoto = userHeadPhoto; } @Override public String toString() { return "User{" + "userId='" + userId + '\'' + ", userName='" + userName + '\'' + ", userHeadPhoto='" + userHeadPhoto + '\'' + ", password='" + password + '\'' + '}'; } }
数据库设计
package loaderman.im.dao; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import loaderman.im.Constants; import loaderman.util.LoggerUtil; /** * Created by Administrator on 2018/12/7 0007. */ public class IMChatOpenHelper extends SQLiteOpenHelper { public IMChatOpenHelper(Context context) { super(context, Constants.DBNAME, null, 1); } @Override public void onCreate(SQLiteDatabase db) { LoggerUtil.systemOut("建表"); db.execSQL("create table user(id integer primary key autoincrement, userId TEXT, userName TEXT, userHeadPhoto TEXT)"); db.execSQL("create table imchat(id integer primary key autoincrement, userId TEXT, userName TEXT, userHeadPhoto TEXT, messageType integer, lastMsgContent TEXT, time TEXT, noReadCount integer)"); } //UserId 用户ID UserName 用户名 UserHeadPhoto 用户头像 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
package loaderman.im.dao; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import java.util.ArrayList; import java.util.List; import loaderman.im.Constants; import loaderman.im.bean.ChatMsgEntity; public class ChatDB { private SQLiteDatabase db; public ChatDB(Context context) { db = context.openOrCreateDatabase(Constants.DBNAME, Context.MODE_PRIVATE, null); } public void saveMsg(String userID, ChatMsgEntity entity) { db.execSQL("CREATE table IF NOT EXISTS _" + userID + " (_id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT, img TEXT,date TEXT,isCome TEXT,message TEXT)"); int isCome = 0; if (entity.getMsgType()) {//如果是收到的消息,保存在数据库的值为1 isCome = 1; } db.execSQL( "insert into _" + userID + " (name,img,date,isCome,message) values(?,?,?,?,?)", new Object[]{entity.getName(), entity.getImg(), entity.getDate(), isCome, entity.getMessage()}); } public List<ChatMsgEntity> getMsg(String userId) { List<ChatMsgEntity> list = new ArrayList<ChatMsgEntity>(); db.execSQL("CREATE table IF NOT EXISTS _" + userId + " (_id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT, img TEXT,date TEXT,isCome TEXT,message TEXT)"); Cursor c = db.rawQuery("SELECT * from _" + userId + " ORDER BY _id DESC LIMIT 5", null); while (c.moveToNext()) { String name = c.getString(c.getColumnIndex("name")); String img = c.getString(c.getColumnIndex("img")); String date = c.getString(c.getColumnIndex("date")); int isCome = c.getInt(c.getColumnIndex("isCome")); String message = c.getString(c.getColumnIndex("message")); boolean isComMsg = false; if (isCome == 1) { isComMsg = true; } ChatMsgEntity entity = new ChatMsgEntity(name, date, message, img, isComMsg); list.add(entity); } c.close(); return list; } public void delete(String userId) { db.execSQL("CREATE table IF NOT EXISTS _" + userId + " (_id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT, img TEXT,date TEXT,isCome TEXT,message TEXT)"); db.execSQL("delete from _" + userId); } public void close() { if (db != null) db.close(); } }
package loaderman.im.dao; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import java.util.ArrayList; import java.util.List; import loaderman.im.bean.User; import loaderman.util.LoggerUtil; /** * Created by Administrator on 2018/12/7 0007. */ public class IMUserDao { private IMChatOpenHelper helper; private static IMUserDao mInstance; public IMUserDao(Context context) { helper = new IMChatOpenHelper(context); } public static IMUserDao getInstance(Context ctx) { //懒汉: 考虑线程安全问题, 两种方式: 1. 给方法加同步锁 synchronized, 效率低; 2. 给创建对象的代码块加同步锁 //读数据不会出现线程安全问题, 写数据会出现线程安全问题 //a, B, C if (mInstance == null) { //B, C synchronized (IMUserDao.class) { //a if (mInstance == null) { mInstance = new IMUserDao(ctx); } } } return mInstance; } public User selectInfo(String userId) { User u = new User(); SQLiteDatabase db = helper.getReadableDatabase(); Cursor c =db.query("user",null,"userId= ?",new String[]{userId},null,null,null); // Cursor c = db.rawQuery("select * from user where userId= ? ", new String[] { userId}); if (c.moveToFirst()) { u.setUserHeadPhoto(c.getString(c.getColumnIndex("userHeadPhoto"))); u.setUserName(c.getString(c.getColumnIndex("userName"))); u.setUserId(userId); } return u; } public void addUser(List<User> list) { SQLiteDatabase db = helper.getWritableDatabase(); for (User u : list) { ContentValues values = new ContentValues(); values.put("userID", u.getUserId()); values.put("userName", u.getUserName()); values.put("userHeadPhoto", u.getUserHeadPhoto()); long id = db.insert("user", null, values); if (id == -1) { LoggerUtil.systemOut("插入失败"); } else { LoggerUtil.systemOut("插入成功"); } } db.close(); } public void updateUser(List<User> list) { if (list.size() > 0) { delete(); addUser(list); } } public List<User> getUser() { SQLiteDatabase db = helper.getWritableDatabase(); List<User> list = new ArrayList<User>(); Cursor c =db.query("user",null,null,null,null,null,null); while (c.moveToNext()) { User u = new User(); u.setUserId(c.getString(c.getColumnIndex("userId"))); u.setUserHeadPhoto(c.getString(c.getColumnIndex("userHeadPhoto"))); u.setUserName(c.getString(c.getColumnIndex("userName"))); list.add(u); } c.close(); db.close(); return list; } public void delete() { try { SQLiteDatabase db = helper.getWritableDatabase(); int id = db.delete("user", null, null); if (id==-1){ LoggerUtil.systemOut("删除失败"); }else { LoggerUtil.systemOut("删除成功"); } db.close(); }catch (Exception e){ LoggerUtil.systemOut(e.toString()); } } }
package loaderman.im.dao; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import java.util.ArrayList; import loaderman.im.bean.IMMessage; import loaderman.util.LoggerUtil; /** * 消息列表 */ public class IMUserMessageDao { private static IMUserMessageDao mInstance; private String TableName = "imchat"; public Context context; private final IMChatOpenHelper helper; private IMUserMessageDao(Context ctx) { this.context = ctx; helper = new IMChatOpenHelper(context); } public static IMUserMessageDao getInstance(Context ctx) { //懒汉: 考虑线程安全问题, 两种方式: 1. 给方法加同步锁 synchronized, 效率低; 2. 给创建对象的代码块加同步锁 //读数据不会出现线程安全问题, 写数据会出现线程安全问题 //a, B, C if (mInstance == null) { //B, C synchronized (IMUserMessageDao.class) { //a if (mInstance == null) { mInstance = new IMUserMessageDao(ctx); } } } return mInstance; } public boolean insert(Context context, IMMessage imMessage) { boolean isSucceed = false; // 1. 在内存中创建数据库帮助类的对象 // 2. 在磁盘上创建数据库文件 SQLiteDatabase db = helper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("userId", imMessage.getUserId()); values.put("userName", imMessage.getUserName()); values.put("userHeadPhoto", imMessage.getUserHeadPhoto()); values.put("messageType", imMessage.getMessageType()); values.put("lastMsgContent", imMessage.getLastMsgContent()); values.put("time", imMessage.getTime()); values.put("noReadCount", imMessage.getNoReadCount()); long id = db.insert(TableName, null, values); if (id == -1) { LoggerUtil.systemOut("插入失败"); isSucceed = false; } else { LoggerUtil.systemOut("插入成功"); isSucceed = true; } // 释放资源 db.close(); return isSucceed; } public boolean update(Context context, IMMessage imMessage) { boolean isSucceed = false; // 2. 在磁盘上创建数据库文件 SQLiteDatabase db = helper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("userId", imMessage.getUserId()); values.put("userName", imMessage.getUserName()); values.put("userHeadPhoto", imMessage.getUserHeadPhoto()); values.put("messageType", imMessage.getMessageType()); values.put("lastMsgContent", imMessage.getLastMsgContent()); values.put("time", imMessage.getTime()); values.put("noReadCount", imMessage.getNoReadCount()); int update = db.update(TableName, values, "userId=?", new String[]{imMessage.getUserId()}); // 释放资源 db.close(); return update > 0; } public boolean updateNoReadNUmByUserId(Context context,String userId){ // 1. 在内存中创建数据库帮助类的对象 // 2. 在磁盘上创建数据库文件 SQLiteDatabase database = helper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("noReadCount",0); int update = database.update(TableName, values, "userId=? ", new String[]{userId}); return update>0; } public int queryNoReadNumByUserId(Context context,String userId){ // 1. 在内存中创建数据库帮助类的对象 // 2. 在磁盘上创建数据库文件 SQLiteDatabase database = helper.getWritableDatabase(); Cursor cursor = database.query(TableName, null, "userId=? ", new String[]{userId}, null, null, null); if (cursor != null) { if (!cursor.moveToFirst()) { //插入 LoggerUtil.systemOut("没有查询到"); return 0; } else { //更新 LoggerUtil.systemOut("查询到了"); int noReadCount = cursor.getInt(cursor.getColumnIndex("noReadCount")); if (noReadCount>=0){ return noReadCount; } } } else { LoggerUtil.systemOut("cursor为空"); return 0; } return 0; } public boolean queryIsHasUserID(Context context, String userId) { // 1. 在内存中创建数据库帮助类的对象 // 2. 在磁盘上创建数据库文件 SQLiteDatabase database = helper.getWritableDatabase(); Cursor cursor = database.query(TableName, null, "userId=? ", new String[]{userId}, null, null, null); if (cursor != null) { if (!cursor.moveToFirst()) { //插入 LoggerUtil.systemOut("没有查询到"); return false; } else { //更新 LoggerUtil.systemOut("查询到了"); return true; } } else { LoggerUtil.systemOut("cursor为空"); return false; } } public ArrayList<IMMessage> query(Context context) { // 1. 在内存中创建数据库帮助类的对象 // 2. 在磁盘上创建数据库文件 SQLiteDatabase database = helper.getWritableDatabase(); Cursor cursor = database.query(TableName, null, null, null, null, null, null); ArrayList<IMMessage> list = new ArrayList<>(); if (cursor != null) { LoggerUtil.systemOut("查询个数 " + cursor.getCount()); while (cursor.moveToNext()) { String user = cursor.getString(cursor.getColumnIndex("userId")); String nickname = cursor.getString(cursor.getColumnIndex("userName")); String icon = cursor.getString(cursor.getColumnIndex("userHeadPhoto")); int type = cursor.getInt(cursor.getColumnIndex("messageType")); String content = cursor.getString(cursor.getColumnIndex("lastMsgContent")); String time = cursor.getString(cursor.getColumnIndex("time")); int noReadCount = cursor.getInt(cursor.getColumnIndex("noReadCount")); IMMessage imMessage = new IMMessage(user, nickname, icon, type, content, time,noReadCount); list.add(imMessage); } cursor.close(); } database.close(); return list; } public boolean delete(Context context, String userID) { boolean isDelete = false; // 1. 在内存中创建数据库帮助类的对象 // 2. 在磁盘上创建数据库文件 SQLiteDatabase database = helper.getWritableDatabase(); int d = database.delete(TableName, "userId = ?", new String[]{userID}); if (d == 0) { LoggerUtil.systemOut("删除失败"); isDelete = false; } else { LoggerUtil.systemOut("删除成功" + userID); isDelete = true; } // 释放资源 database.close(); return isDelete; } public boolean deleteAll(Context context) { boolean isDelete = false; // 1. 在内存中创建数据库帮助类的对象 // 2. 在磁盘上创建数据库文件 SQLiteDatabase database = helper.getWritableDatabase(); int d = database.delete(TableName, null, null); if (d == 0) { LoggerUtil.systemOut("删除失败"); isDelete = false; } else { LoggerUtil.systemOut("删除成功"); isDelete = true; } // 释放资源 database.close(); return isDelete; } }
传输对象
package loaderman.im.tran; import java.io.Serializable; /** * 传输的对象,直接通过Socket传输的最大对象 * */ public class TranObject<T> implements Serializable { /** * */ private static final long serialVersionUID = 1L; private TranObjectType type;// 发送的消息类型 private String fromUser;// 来自哪个用户 private String toUser;// 发往哪个用户 private T object;// 传输的对象 public TranObject(TranObjectType type) { this.type = type; } public String getFromUser() { return fromUser; } public void setFromUser(String fromUser) { this.fromUser = fromUser; } public String getToUser() { return toUser; } public void setToUser(String toUser) { this.toUser = toUser; } public T getObject() { return object; } public void setObject(T object) { this.object = object; } public TranObjectType getType() { return type; } @Override public String toString() { return "TranObject [type=" + type + ", fromUser=" + fromUser + ", toUser=" + toUser + ", object=" + object + "]"; } }
package loaderman.im.tran; /** * 传输对象类型 * * */ public enum TranObjectType { REGISTER, // 注册 LOGIN, // 用户登录 LOGOUT, // 用户退出登录 FRIENDLOGIN, // 好友上线 FRIENDLOGOUT, // 好友下线 MESSAGE, // 用户发送消息 UNCONNECTED, // 无法连接 FILE, // 传输文件 REFRESH, // 刷新 OFFLINEMESSAGE//离线消息 }
工具类
package loaderman.im.util; import android.app.ActivityManager; import android.content.Context; import java.util.List; /** * 判断是否是后台运行 */ public class AppUtil { public static boolean isAppOnForeground(Context context) { // Returns a list of application processes that are running on the // device ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); String packageName = context.getApplicationContext().getPackageName(); List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager .getRunningAppProcesses(); if (appProcesses == null) return false; for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { // The name of the process that this object is associated with. if (appProcess.processName.equals(packageName) && appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { return true; } } return false; } }
package loaderman.im.util; import java.text.SimpleDateFormat; import java.util.Date; public class MyDate { public static String getDateCN() { SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String date = format.format(new Date(System.currentTimeMillis())); return date;// 2012年10月03日 23:41:31 } public static String getDateEN() { SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date1 = format1.format(new Date(System.currentTimeMillis())); return date1;// 2012-10-03 23:41:31 } public static String getDate() { SimpleDateFormat format = new SimpleDateFormat("HH:mm"); String date = format.format(new Date(System.currentTimeMillis())); return date; } }
package loaderman.im.util; import android.app.ActivityManager; import android.app.Service; import android.content.Context; import java.util.List; /** * 判断服务是否运行 */ public class ServiceStatusUtil { //PackageManager, TelephoyManager, DevicePolicyManager, Vibrator, SmsManager, LocationManager //ActivityManager public static boolean isServiceRunning(Context ctx, Class<? extends Service> clazz) { //活动管理器, 管理一切正在运行的东西 ActivityManager am = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE); //获取正在运行的服务, 100表示最多返回100条记录 List<ActivityManager.RunningServiceInfo> runningServices = am.getRunningServices(100); for (ActivityManager.RunningServiceInfo info : runningServices) { //遍历所有正在运行的服务,查看有没有我们要找的服务 String className = info.service.getClassName();//获取当前运行服务的类全名称 if (className.equals(clazz.getName())) { //服务正在运行 return true; } } return false; } }
客户端实现
package loaderman.im.client; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import loaderman.util.LoggerUtil; /** * 客户端 * * */ public class Client { private Socket client; private ClientThread clientThread; private String ip; private int port; public Client(String ip, int port) { this.ip = ip; this.port = port; } public boolean start() { try { client = new Socket(); client.setKeepAlive(true); client.connect(new InetSocketAddress(ip, port), 1000); if (client.isConnected()) { System.out.println("Connected......"); clientThread = new ClientThread(client); clientThread.start(); } } catch (IOException e) { e.printStackTrace(); return false; } return true; } public Socket getSocket(){ return client; } // 直接通过client得到读线程 public ClientInputThread getClientInputThread() { LoggerUtil.systemOut(clientThread.toString()); return clientThread.getIn(); } // 直接通过client得到写线程 public ClientOutputThread getClientOutputThread() { return clientThread.getOut(); } // 直接通过client停止读写消息 public void setIsStart(boolean isStart) { clientThread.getIn().setStart(isStart); clientThread.getOut().setStart(isStart); } public class ClientThread extends Thread { private ClientInputThread in; private ClientOutputThread out; public ClientThread(Socket socket) { in = new ClientInputThread(socket); out = new ClientOutputThread(socket); } public void run() { in.setStart(true); out.setStart(true); in.start(); out.start(); } // 得到读消息线程 public ClientInputThread getIn() { return in; } // 得到写消息线程 public ClientOutputThread getOut() { return out; } } }
package loaderman.im.client; import java.io.IOException; import java.io.ObjectInputStream; import java.net.Socket; import loaderman.im.bean.TextMessage; import loaderman.im.tran.TranObject; import loaderman.util.LoggerUtil; /** * 客户端读消息线程 * */ public class ClientInputThread extends Thread { private Socket socket; private TranObject msg; private boolean isStart = true; private ObjectInputStream ois; private MessageListener messageListener;// 消息监听接口对象 public ClientInputThread(Socket socket) { this.socket = socket; try { ois = new ObjectInputStream(socket.getInputStream()); } catch (IOException e) { e.printStackTrace(); } } /** * 提供给外部的消息监听方法 * * @param messageListener 消息监听接口对象 */ public void setMessageListener(MessageListener messageListener) { this.messageListener = messageListener; } public void setStart(boolean isStart) { this.isStart = isStart; } @Override public void run() { try { while (isStart) { if (ois != null) { msg = (TranObject) ois.readObject(); // 每收到一条消息,就调用接口的方法,并传入该消息对象,外部在实现接口的方法时,就可以及时处理传入的消息对象了 // 我不知道我有说明白没有? LoggerUtil.systemOut("来消息了" + msg.toString()); messageListener.Message(msg); switch (msg.getType()) { case MESSAGE: TextMessage tm = (TextMessage) msg.getObject(); String message = tm.getMessage(); LoggerUtil.systemOut(message); break; } } } ois.close(); if (socket != null) socket.close(); } catch (Exception e) { e.printStackTrace(); } } }
package loaderman.im.client; import java.io.IOException; import java.io.ObjectOutputStream; import java.net.Socket; import loaderman.im.tran.TranObject; import loaderman.im.tran.TranObjectType; /** * 客户端写消息线程 * * @author way */ public class ClientOutputThread extends Thread { private Socket socket; private ObjectOutputStream oos; private boolean isStart = true; private TranObject msg; public ClientOutputThread(Socket socket) { this.socket = socket; try { oos = new ObjectOutputStream(socket.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } } public void setStart(boolean isStart) { this.isStart = isStart; } // 这里处理跟服务器是一样的 public void setMsg(TranObject msg) { this.msg = msg; synchronized (this) { notify(); } } int length = 0; double sumL = 0; @Override public void run() { try { while (isStart) { if (msg != null) { oos.writeObject(msg); oos.flush(); if (msg.getType() == TranObjectType.LOGOUT) {// 如果是发送下线的消息,就直接跳出循环 break; } synchronized (this) { wait();// 发送完消息后,线程进入等待状态 } } } if (oos!=null){ oos.close();// 循环结束后,关闭输出流和socket } if (socket != null){ socket.close(); } } catch (Exception e) { e.printStackTrace(); } } }
package loaderman.im.client; import java.util.List; import loadermanim.bean.User; import loaderman.im.tran.TranObject; /** * 消息监听接口 * * */ public interface MessageListener { public List<User> Message(TranObject msg); }
packageloaderman.im; public class Constants { public static final String SERVER_IP = "192.168.0.195";// 服务器ip public static final int SERVER_PORT = 8080;// 服务器端口 public static final int REGISTER_FAIL = 0;//注册失败 public static final String ACTION = "loaderman.message";//消息广播action public static final String MSGKEY = "message";//消息的key public static final String IP_PORT = "ipPort";//保存ip、port的xml文件名 public static final String SAVE_USER = "saveUser";//保存用户信息的xml文件名 public static final String BACKKEY_ACTION="loaderman.back";//返回键发送广播的action public static final int NOTIFY_ID = 0x911;//通知ID public static final int NOTIFY_ID2 = 0x912;//通知ID public static final String LoginID = "test";//模拟登录ID public static final String DBNAME = LoginID+"IM.db";//数据库名称 public static final String Password = "e10adc3949ba59abbe56e057f20f883e";//登录密码 }
消息服务
package loaderman.im; import android.app.ActivityManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.media.SoundPool; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Vibrator; import android.support.v4.app.NotificationCompat; import android.widget.RemoteViews; import org.greenrobot.eventbus.EventBus; import java.util.List; import loaderman.R; import loaderman.activity.MainActivity; import loaderman.constant.ConfigConstants; import loaderman.event.RefreshChatMessageEvent; import loaderman.im.bean.ChatMsgEntity; import loaderman.im.bean.IMMessage; import loaderman.im.bean.TextMessage; import loaderman.im.bean.User; import loaderman.im.client.Client; import loaderman.im.client.ClientInputThread; import loaderman.im.client.MessageListener; import loaderman.im.dao.ChatDB; import loaderman.im.dao.IMUserDao; import loaderman.im.dao.IMUserMessageDao; import loaderman.im.tran.TranObject; import loaderman.im.tran.TranObjectType; import loaderman.im.util.MyDate; import loaderman.im.view.Chat2Activity; import loaderman.util.LoggerUtil; /** * 收取消息服务 */ public class GetMsgService extends Service { private static final int MSG = 0x001; private static final int HEART_MSG = 0x002; private Client client; private NotificationManager mNotificationManager; private boolean isStart = false;// 是否与服务器连接上 private Notification mNotification; private Context mContext = this; public static SoundPool pool; public static Vibrator vibrator; // 收到用户按返回键发出的广播,就显示通知栏 private BroadcastReceiver backKeyReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub // Toast.makeText(context, "进入后台运行", Toast.LENGTH_SHORT).show(); setMsgNotification(); } }; public NotificationManager manager = null; private ChatDB chatDB; boolean isLogin = false; // 用来更新通知栏消息的handler private Handler handler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case MSG: IMChatManager imChatManager = IMChatManager.getInstance(GetMsgService.this); int newMsgNum = imChatManager.getNewMsgNum();// 从全局变量中获取 newMsgNum++;// 每收到一次消息,自增一次 imChatManager.setNewMsgNum(newMsgNum);// 再设置为全局变量 TranObject<TextMessage> textObject = (TranObject<TextMessage>) msg .getData().getSerializable("msg"); if (textObject != null) { String form = textObject.getFromUser();// 消息从哪里来 String content = textObject.getObject().getMessage();// 消息内容 boolean has = IMUserMessageDao.getInstance(mContext).queryIsHasUserID(mContext, form); if (has) { User user = IMUserDao.getInstance(mContext).selectInfo(form); int noReadCount = IMUserMessageDao.getInstance(mContext).queryNoReadNumByUserId(mContext, form); LoggerUtil.systemOut("未读个数" + noReadCount); IMMessage imMessage = new IMMessage(user.getUserId(), user.getUserName(), user.getUserHeadPhoto(), 0, content, MyDate.getDateEN(), noReadCount + 1); IMUserMessageDao.getInstance(mContext).update(mContext, imMessage); } else { User user = IMUserDao.getInstance(mContext).selectInfo(form); IMMessage imMessage = new IMMessage(user.getUserId(), user.getUserName(), user.getUserHeadPhoto(), 0, content, MyDate.getDateEN(), 1); IMUserMessageDao.getInstance(mContext).insert(mContext, imMessage); } EventBus.getDefault().post(new RefreshChatMessageEvent()); if (Chat2Activity.ca == null) { ChatDB chatDB = new ChatDB(mContext); User user = IMUserDao.getInstance(mContext).selectInfo(form); ChatMsgEntity entity = new ChatMsgEntity(user.getUserName(), MyDate.getDateEN(), content, user.getUserHeadPhoto(), true);// 收到的消息 chatDB.saveMsg(form, entity); } // 更新通知栏 if (manager == null) { manager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); } Intent intent = new Intent(mContext, MainActivity.class); PendingIntent contentIntent = PendingIntent.getActivity( mContext, 0, intent, 0); Notification notify = new Notification.Builder(mContext) .setAutoCancel(true) .setTicker("有新消息") .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("来自" + form + "的消息") .setContentText("请点击查看消息,或者进入应用查看详情") .setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL) .setWhen(System.currentTimeMillis()) .setContentIntent(contentIntent).build(); manager.notify(Constants.NOTIFY_ID2, notify); // mNotificationManager.notify(Constants.NOTIFY_ID, mNotification);// 通知一下才会生效哦 } break; case HEART_MSG: Boolean serverConnetion = IMChatManager.getInstance(mContext).isServerConnetion(); boolean clientStart = IMChatManager.getInstance(mContext).isClientStart(); if (clientStart && serverConnetion) { LoggerUtil.systemOut("连接正常"); if (!isLogin) { LoggerUtil.systemOut(ConfigConstants.getUserID(mContext)); if (ConfigConstants.getUserID(mContext).equals("NaN")) { isLogin = false; IMChatManager.getInstance(mContext).login(Constants.LoginID, Constants.Password); } else { isLogin = true; } } } else { LoggerUtil.systemOut("已经断开连接"); isStart = false; isLogin = false; if (!isStart){ getServerClientConnection(); } ConfigConstants.logOutRemoveConfig(mContext); } handler.sendEmptyMessageDelayed(HEART_MSG, 5000); break; default: break; } } }; public boolean isAppOnForeground(Context context) { // Returns a list of application processes that are running on the // device ActivityManager activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); String packageName = context.getApplicationContext().getPackageName(); List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager .getRunningAppProcesses(); if (appProcesses == null) return false; for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { // The name of the process that this object is associated with. if (appProcess.processName.equals(packageName) && appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { return true; } } return false; } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() {// 在onCreate方法里面注册广播接收者 // TODO Auto-generated method stub super.onCreate(); chatDB = new ChatDB(this); IntentFilter filter = new IntentFilter(); filter.addAction(Constants.BACKKEY_ACTION); registerReceiver(backKeyReceiver, filter); mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); LoggerUtil.systemOut("服务开启了"); client = IMChatManager.getInstance(this).getClient(); IMChatManager.getInstance(this).setmNotificationManager(mNotificationManager); handler.sendEmptyMessageDelayed(HEART_MSG, 10); pool = new SoundPool(10, AudioManager.STREAM_SYSTEM, 5); pool.load(this, R.raw.tishi, 1); vibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); if (!isStart){ getServerClientConnection(); } } private void getServerClientConnection() { new Thread() { @Override public void run() { isStart = client.start(); IMChatManager.getInstance(GetMsgService.this).setClientStart(isStart); System.out.println("client 是否开启:" + isStart); if (isStart) { ClientInputThread in = client.getClientInputThread(); in.setMessageListener(new MessageListener() { @Override public List<User> Message(TranObject msg) { //是在后台运行,就更新通知栏,否则就发送广播给Activity LoggerUtil.systemOut("服务监听消息"); if (msg.getType() == TranObjectType.MESSAGE) { if (pool != null && ConfigConstants.getSound(GetMsgService.this)) { pool.play(1, 1, 1, 0, 0, 1); } if (vibrator != null && ConfigConstants.getShake(GetMsgService.this)) { vibrator.vibrate(500); } } if (!isAppOnForeground(mContext)) { LoggerUtil.systemOut("后台运行"); if (msg.getType() == TranObjectType.MESSAGE) {// 只处理文本消息类型 // System.out.println("收到新消息"); // // 把消息对象发送到handler去处理 Message message = handler.obtainMessage(); message.what = MSG; message.getData().putSerializable("msg", msg); handler.sendMessage(message); } } else { LoggerUtil.systemOut("前台运行"); Intent broadCast = new Intent(); broadCast.setAction(Constants.ACTION); broadCast.putExtra(Constants.MSGKEY, msg); sendBroadcast(broadCast);// 把收到的消息已广播的形式发送出去 } return null; } }); } } }.start(); } @Override // 在服务被摧毁时,做一些事情 public void onDestroy() { super.onDestroy(); LoggerUtil.systemOut("服务关闭"); handler.removeCallbacksAndMessages(null); if (chatDB != null) { chatDB.close(); } unregisterReceiver(backKeyReceiver); mNotificationManager.cancel(Constants.NOTIFY_ID); // 给服务器发送下线消息 if (isStart) { IMChatManager.getInstance(this).logout(); } } /** * 创建通知 */ private void setMsgNotification() { int icon = R.mipmap.ic_launcher; CharSequence tickerText = ""; long when = System.currentTimeMillis(); mNotification = new Notification(icon, tickerText, when); // 放置在"正在运行"栏目中 mNotification.flags = Notification.FLAG_ONGOING_EVENT; RemoteViews contentView = new RemoteViews(mContext.getPackageName(), R.layout.notify_view); contentView.setTextViewText(R.id.tv1, "点击了解详情或者停止应用"); contentView.setTextViewText(R.id.notify_msg, "风城移动应用正在运行"); // 指定个性化视图 mNotification.contentView = contentView; Intent intent = new Intent(this, MainActivity.class); PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); // 指定内容意图 mNotification.contentIntent = contentIntent; mNotificationManager.notify(Constants.NOTIFY_ID, mNotification); } }
消息广播
package loaderman.im; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.media.MediaPlayer; import android.widget.Toast; import org.greenrobot.eventbus.EventBus; import java.util.ArrayList; import java.util.List; import loaderman.R; import loaderman.constant.ConfigConstants; import loaderman.event.RefreshChatMessageEvent; import loaderman.event.RefreshFriendListEvent; import loaderman.im.bean.ChatMsgEntity; import loaderman.im.bean.IMMessage; import loaderman.im.bean.OffLineMessage; import loaderman.im.bean.TextMessage; import loaderman.im.bean.User; import loaderman.im.dao.ChatDB; import loaderman.im.dao.IMUserDao; import loaderman.im.dao.IMUserMessageDao; import loaderman.im.tran.TranObject; import loaderman.im.tran.TranObjectType; import loaderman.im.util.MyDate; import loaderman.im.view.Chat2Activity; import loaderman.util.LoggerUtil; import loaderman.util.ToastUtil; /** * Created by Administrator on 2018/12/10 0010. */ public class GetMsgReceiver extends BroadcastReceiver { UpdateMsg updateMsg = null; public GetMsgReceiver(UpdateMsg updateMsg) { this.updateMsg = updateMsg; } @Override public void onReceive(Context context, Intent intent) { TranObject msg = (TranObject) intent .getSerializableExtra(Constants.MSGKEY); if (msg != null) {//如果不是空,说明是消息广播 LoggerUtil.systemOut("GetMsgReceiver接收广播消息" + msg.toString()); updateMsg.update(msg);// 把收到的消息传递给子类 switch (msg.getType()) { case MESSAGE: LoggerUtil.systemOut("来好友消息了"); TextMessage textMsg = (TextMessage) msg.getObject(); boolean has = IMUserMessageDao.getInstance(context).queryIsHasUserID(context, msg.getFromUser()); if (has) { User user = IMUserDao.getInstance(context).selectInfo(msg.getFromUser()); int noReadCount = IMUserMessageDao.getInstance(context).queryNoReadNumByUserId(context, msg.getFromUser()); LoggerUtil.systemOut("未读个数" + noReadCount); IMMessage imMessage = new IMMessage(user.getUserId(), user.getUserName(), user.getUserHeadPhoto(), 0, textMsg.getMessage(), MyDate.getDateEN(), noReadCount + 1); IMUserMessageDao.getInstance(context).update(context, imMessage); } else { User user = IMUserDao.getInstance(context).selectInfo(msg.getFromUser()); IMMessage imMessage = new IMMessage(user.getUserId(), user.getUserName(), user.getUserHeadPhoto(), 0, textMsg.getMessage(), MyDate.getDateEN(), 1); IMUserMessageDao.getInstance(context).insert(context, imMessage); } EventBus.getDefault().post(new RefreshChatMessageEvent()); if (Chat2Activity.ca == null) { ChatDB chatDB = new ChatDB(context); User user = IMUserDao.getInstance(context).selectInfo(msg.getFromUser()); ChatMsgEntity entity = new ChatMsgEntity(user.getUserName(), MyDate.getDateEN(), textMsg.getMessage(), user.getUserHeadPhoto(), true);// 收到的消息 chatDB.saveMsg(msg.getFromUser(), entity); } break; case LOGIN: LoggerUtil.systemOut("登录"); try { boolean login = (Boolean) msg.getObject(); if (login) { ConfigConstants.setUserID(context, Constants.LoginID); LoggerUtil.systemOut("登录成功"); IMChatManager.getInstance(context).getFriendlist(context); IMChatManager.getInstance(context).getOffLineMessage(context); ToastUtil.showMsg(context, "登录成功"); } else { LoggerUtil.systemOut("登录失败"); ToastUtil.showMsg(context, "登录失败"); } } catch (Exception e) { try { LoggerUtil.systemOut(e.toString()); User loginUser = (User) msg.getObject(); if (!loginUser.getUserId().equals(Constants.LoginID)) { Toast.makeText(context, loginUser.getUserId() + "上线了", Toast.LENGTH_SHORT).show(); MediaPlayer.create(context, R.raw.msg).start(); } } catch (Exception e1) { } } break; case REFRESH: LoggerUtil.systemOut("刷新"); if (msg != null && msg.getType() == TranObjectType.REFRESH) { List<User> list = (List<User>) msg.getObject(); LoggerUtil.systemOut("好友个数 : " + list.size()); if (list.size() > 0) { IMUserDao.getInstance(context).updateUser(list);// 保存到数据库 } } EventBus.getDefault().post(new RefreshFriendListEvent()); break; case LOGOUT: LoggerUtil.systemOut("登出"); break; case OFFLINEMESSAGE: if (msg != null && msg.getType() == TranObjectType.OFFLINEMESSAGE) { ArrayList<OffLineMessage> offLineMessage = (ArrayList<OffLineMessage>) msg.getObject(); if (offLineMessage != null && offLineMessage.size() > 0) { LoggerUtil.systemOut("离线消息个数 : " + offLineMessage.size()); for (int i = 0; i < offLineMessage.size(); i++) { boolean has1 = IMUserMessageDao.getInstance(context).queryIsHasUserID(context, msg.getFromUser()); if (has1) { User user = IMUserDao.getInstance(context).selectInfo(msg.getFromUser()); int noReadCount = IMUserMessageDao.getInstance(context).queryNoReadNumByUserId(context, msg.getFromUser()); LoggerUtil.systemOut("未读个数" + noReadCount); IMMessage imMessage = new IMMessage(user.getUserId(), user.getUserName(), user.getUserHeadPhoto(), 0, offLineMessage.get(i).getTextMsg(),offLineMessage.get(i).getTime(), noReadCount + 1); IMUserMessageDao.getInstance(context).update(context, imMessage); } else { User user = IMUserDao.getInstance(context).selectInfo(msg.getFromUser()); IMMessage imMessage = new IMMessage(user.getUserId(), user.getUserName(), user.getUserHeadPhoto(), 0, offLineMessage.get(i).getTextMsg(),offLineMessage.get(i).getTime(), 1); IMUserMessageDao.getInstance(context).insert(context, imMessage); } EventBus.getDefault().post(new RefreshChatMessageEvent()); if (Chat2Activity.ca == null) { ChatDB chatDB = new ChatDB(context); User user = IMUserDao.getInstance(context).selectInfo(msg.getFromUser()); ChatMsgEntity entity = new ChatMsgEntity(user.getUserName(), offLineMessage.get(i).getTime(), offLineMessage.get(i).getTextMsg(), user.getUserHeadPhoto(), true);// 收到的消息 chatDB.saveMsg(msg.getFromUser(), entity); } } } } break; } } else {//如果是空消息,说明是关闭应用的广播 } } public interface UpdateMsg { void update(TranObject msg); } }
package loaderman.im; import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; import java.net.Socket; import java.util.ArrayList; import java.util.List; import loaderman.constant.ConfigConstants; import loaderman.im.bean.TextMessage; import loaderman.im.bean.User; import loaderman.im.client.Client; import loaderman.im.client.ClientOutputThread; import loaderman.im.tran.TranObject; import loaderman.im.tran.TranObjectType; import loaderman.im.util.ServiceStatusUtil; import loaderman.util.LoggerUtil; import static petrochina.xjyt.fcydyy.MyApplication.context; /** * 聊天管理类 */ public class IMChatManager { private static final IMChatManager chatManager = new IMChatManager(); private static final Client client =new Client(Constants.SERVER_IP, Constants.SERVER_PORT); /** * 单例模式 * */ public synchronized static IMChatManager getInstance(Context context) { return chatManager; } //连接 public boolean startIMService(Context context) { getClient(); if (isNetworkAvailable()) { Intent service = new Intent(context, GetMsgService.class); context.startService(service); return true; } else { return false; } } //判断连接状态 public boolean isConnection(){ return false; } //得到客户端 public Client getClient() { return client; } private boolean isClientStart;// 客户端连接是否启动 //判断客户端是否启动 public boolean isClientStart() { return isClientStart; } //设置客户端启动状态 public void setClientStart(boolean isClientStart) { this.isClientStart = isClientStart; } //登录 public boolean login(String userID,String userPassword){ LoggerUtil.systemOut("开始登录"); if (isClientStart()){ Client client = getClient(); ClientOutputThread out = client.getClientOutputThread(); TranObject<User> o = new TranObject<User>(TranObjectType.LOGIN); User u = new User(); u.setUserId(userID); u.setPassword(userPassword); o.setObject(u); out.setMsg(o); return true; }else { LoggerUtil.systemOut("服务器没有开启"); return false; } } //登出 public boolean logout(){ LoggerUtil.systemOut("登出"); if (isClientStart()){ Client client = getClient(); ClientOutputThread out = client.getClientOutputThread(); TranObject<User> o = new TranObject<User>(TranObjectType.LOGOUT); User u = new User(); u.setUserId(Constants.LoginID); o.setObject(u); out.setMsg(o); // out.setStart(false); // getClient().getClientInputThread().setStart(false); return true; }else { LoggerUtil.systemOut("服务器没有开启"); return false; } } public boolean getOffLineMessage(Context context){ if (isClientStart()) { ClientOutputThread out = getClient() .getClientOutputThread(); TranObject o = new TranObject(TranObjectType.OFFLINEMESSAGE); o.setFromUser(ConfigConstants.getUserID(context)); out.setMsg(o); return true; } return false; } //注册 public boolean register(String userID,String userPassword,String userName){ if (isClientStart()){ Client client = getClient(); ClientOutputThread out = client.getClientOutputThread(); TranObject<User> o = new TranObject<User>( TranObjectType.REGISTER); User u = new User(); u.setUserId(userID); u.setUserName(userName); u.setPassword(userPassword); o.setObject(u); out.setMsg(o); return true; }else { LoggerUtil.systemOut("服务器没有开启"); return false; } } //发送消息 public void setMessage(String msgContent,String fromUser ,String toUser) { if (msgContent.length() > 0) { Client client = getClient(); ClientOutputThread out = client.getClientOutputThread(); if (out != null) { TranObject<TextMessage> o = new TranObject<TextMessage>( TranObjectType.MESSAGE); TextMessage message = new TextMessage(); message.setMessage(msgContent); o.setObject(message); o.setFromUser(fromUser); o.setToUser(toUser); out.setMsg(o); } } } //退出 public void exit(Context context){ if (ServiceStatusUtil.isServiceRunning(context,GetMsgService.class)) { Intent service = new Intent(context, GetMsgService.class); context.stopService(service); LoggerUtil.systemOut("服务准备停止"); }else { LoggerUtil.systemOut("服务已经停止"); } // close(context);// 父类关闭方法 } //获取离线消息 // private NotificationManager mNotificationManager; private int recentNum = 0; private int newMsgNum = 0;// 后台运行的消息 public NotificationManager getmNotificationManager() { return mNotificationManager; } public void setmNotificationManager(NotificationManager mNotificationManager) { this.mNotificationManager = mNotificationManager; } private List<User> list =new ArrayList<>(); //获取好友列表 public boolean getFriendlist( Context context){ list.clear(); if (isClientStart()) { ClientOutputThread out = getClient() .getClientOutputThread(); TranObject o = new TranObject(TranObjectType.REFRESH); o.setFromUser(ConfigConstants.getUserID(context)); out.setMsg(o); return true; } return false; } public int getNewMsgNum() { return newMsgNum; } public void setNewMsgNum(int newMsgNum) { this.newMsgNum = newMsgNum; } /** * 判断手机网络是否可用 * * @return */ private boolean isNetworkAvailable() { ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo[] info = mgr.getAllNetworkInfo(); if (info != null) { for (int i = 0; i < info.length; i++) { if (info[i].getState() == NetworkInfo.State.CONNECTED) { return true; } } } return false; } /** * 关闭 */ public void close(Context context) { Intent i = new Intent(); i.setAction(Constants.ACTION); context.sendBroadcast(i); } /** * 判断是否断开连接,连接返回true,断开返回false * @return */ public Boolean isServerConnetion(){ Socket socket = getClient().getSocket(); try{ boolean connected = getClient().getSocket().isConnected(); LoggerUtil.systemOut("连接情况"+connected); // socket.sendUrgentData(0xFF);//发送1个字节的紧急数据,默认情况下,服务器端没有开启紧急数据处理,不影响正常通信 return true; }catch(Exception se){ LoggerUtil.systemOut("心跳包发送异常"+se.toString()); return false; } } }
最后,关注【码上加油站】微信公众号后,有疑惑有问题想加油的小伙伴可以码上加入社群,让我们一起码上加油吧!!!