用xmmp+openfire+smack搭建简易IM实现
功能实现:注册,登录,单聊表情,文本,图片,语音的发送接收,添加好友,删除好友,查找好友,修改密码,消息提醒设置,获取离线消息等功能
1.前期准备
1.下载opnefire软件:https://www.igniterealtime.org/downloads/index.jsp
2.下载一款数据库软件:mysql
4.在AS中添加smack相关依赖包:
compile 'org.igniterealtime.smack:smack-android-extensions:4.1.4'compile 'org.igniterealtime.smack:smack-tcp:4.1.4'compile 'org.igniterealtime.smack:smack-im:4.1.4'compile 'org.igniterealtime.smack:smack-extensions:4.1.4'compile 'com.rockerhieu.emojicon:library:1.3.3'
5.核心代码块:
XmppConnection 工具类
1 import android.content.Context; 2 import android.database.Cursor; 3 import android.os.Environment; 4 import android.text.TextUtils; 5 import android.util.Log; 6 7 import org.jivesoftware.smack.AbstractXMPPConnection; 8 import org.jivesoftware.smack.ConnectionConfiguration; 9 import org.jivesoftware.smack.MessageListener; 10 import org.jivesoftware.smack.SmackConfiguration; 11 import org.jivesoftware.smack.SmackException; 12 import org.jivesoftware.smack.XMPPException; 13 import org.jivesoftware.smack.chat.Chat; 14 import org.jivesoftware.smack.chat.ChatManager; 15 import org.jivesoftware.smack.chat.ChatMessageListener; 16 import org.jivesoftware.smack.packet.Message; 17 import org.jivesoftware.smack.packet.Presence; 18 import org.jivesoftware.smack.provider.ProviderManager; 19 import org.jivesoftware.smack.roster.Roster; 20 import org.jivesoftware.smack.roster.RosterEntry; 21 import org.jivesoftware.smack.roster.RosterGroup; 22 import org.jivesoftware.smack.tcp.XMPPTCPConnection; 23 import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration; 24 import org.jivesoftware.smackx.address.provider.MultipleAddressesProvider; 25 import org.jivesoftware.smackx.bytestreams.ibb.provider.CloseIQProvider; 26 import org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProvider; 27 import org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider; 28 import org.jivesoftware.smackx.chatstates.packet.ChatStateExtension; 29 import org.jivesoftware.smackx.commands.provider.AdHocCommandDataProvider; 30 import org.jivesoftware.smackx.delay.provider.DelayInformationProvider; 31 import org.jivesoftware.smackx.disco.provider.DiscoverInfoProvider; 32 import org.jivesoftware.smackx.disco.provider.DiscoverItemsProvider; 33 import org.jivesoftware.smackx.filetransfer.FileTransferListener; 34 import org.jivesoftware.smackx.filetransfer.FileTransferManager; 35 import org.jivesoftware.smackx.filetransfer.FileTransferRequest; 36 import org.jivesoftware.smackx.filetransfer.IncomingFileTransfer; 37 import org.jivesoftware.smackx.filetransfer.OutgoingFileTransfer; 38 import org.jivesoftware.smackx.iqlast.packet.LastActivity; 39 import org.jivesoftware.smackx.iqprivate.PrivateDataManager; 40 import org.jivesoftware.smackx.iqregister.AccountManager; 41 import org.jivesoftware.smackx.muc.DiscussionHistory; 42 import org.jivesoftware.smackx.muc.HostedRoom; 43 import org.jivesoftware.smackx.muc.MultiUserChat; 44 import org.jivesoftware.smackx.muc.MultiUserChatManager; 45 import org.jivesoftware.smackx.muc.packet.GroupChatInvitation; 46 import org.jivesoftware.smackx.muc.provider.MUCAdminProvider; 47 import org.jivesoftware.smackx.muc.provider.MUCOwnerProvider; 48 import org.jivesoftware.smackx.muc.provider.MUCUserProvider; 49 import org.jivesoftware.smackx.offline.OfflineMessageManager; 50 import org.jivesoftware.smackx.offline.packet.OfflineMessageInfo; 51 import org.jivesoftware.smackx.offline.packet.OfflineMessageRequest; 52 import org.jivesoftware.smackx.privacy.provider.PrivacyProvider; 53 import org.jivesoftware.smackx.search.ReportedData; 54 import org.jivesoftware.smackx.search.UserSearch; 55 import org.jivesoftware.smackx.search.UserSearchManager; 56 import org.jivesoftware.smackx.sharedgroups.packet.SharedGroupsInfo; 57 import org.jivesoftware.smackx.si.provider.StreamInitiationProvider; 58 import org.jivesoftware.smackx.vcardtemp.provider.VCardProvider; 59 import org.jivesoftware.smackx.xdata.Form; 60 import org.jivesoftware.smackx.xdata.FormField; 61 import org.jivesoftware.smackx.xdata.provider.DataFormProvider; 62 import org.jivesoftware.smackx.xhtmlim.provider.XHTMLExtensionProvider; 63 64 import java.io.BufferedInputStream; 65 import java.io.BufferedReader; 66 import java.io.File; 67 import java.io.FileInputStream; 68 import java.io.IOException; 69 import java.io.InputStreamReader; 70 import java.net.URL; 71 import java.net.URLConnection; 72 import java.util.ArrayList; 73 import java.util.Collection; 74 import java.util.Date; 75 import java.util.HashMap; 76 import java.util.Iterator; 77 import java.util.List; 78 import java.util.Map; 79 import java.util.Set; 80 81 import cnpc.fcyt.fcydyy.util.LoggerUtil; 82 import cnpc.fcyt.fcydyy.xmpp.bean.XmppChat; 83 import cnpc.fcyt.fcydyy.xmpp.bean.XmppMessage; 84 import cnpc.fcyt.fcydyy.xmpp.bean.XmppUser; 85 import cnpc.fcyt.fcydyy.xmpp.dao.FriendChatDao; 86 import cnpc.fcyt.fcydyy.xmpp.dao.MessageDao; 87 import cnpc.fcyt.fcydyy.xmpp.util.TimeUtil; 88 import cnpc.fcyt.fcydyy.xmpp.util.UserConstants; 89 90 /** 91 * XmppConnection 工具类 92 */ 93 94 public class XmppConnection { 95 private int SERVER_PORT = 5222; 96 private String SERVER_HOST = "192.168.0.195"; 97 private String SERVER_NAME = "192.168.0.195"; 98 public AbstractXMPPConnection connection = null; 99 private static XmppConnection xmppConnection = new XmppConnection(); 100 private XMConnectionListener connectionListener; 101 102 /** 103 * 单例模式 104 * 105 * @return XmppConnection 106 */ 107 public synchronized static XmppConnection getInstance() { 108 configure(new ProviderManager()); 109 return xmppConnection; 110 } 111 112 /** 113 * 创建连接 114 */ 115 public AbstractXMPPConnection getConnection() { 116 117 if (connection == null) { 118 // 开线程打开连接,避免在主线程里面执行HTTP请求 119 // Caused by: android.os.NetworkOnMainThreadException 120 new Thread(new Runnable() { 121 @Override 122 public void run() { 123 openConnection(); 124 } 125 }).start(); 126 } 127 return connection; 128 } 129 130 public void setConnectionToNull() { 131 connection = null; 132 } 133 134 /** 135 * 判断是否已连接 136 */ 137 public boolean checkConnection() { 138 return null != connection && connection.isConnected(); 139 } 140 141 /** 142 * 打开连接 143 */ 144 public boolean openConnection() { 145 try { 146 if (null == connection || !connection.isAuthenticated()) { 147 SmackConfiguration.DEBUG = true; 148 XMPPTCPConnectionConfiguration.Builder config = XMPPTCPConnectionConfiguration.builder(); 149 //设置openfire主机IP 150 config.setHost(SERVER_HOST); 151 //设置openfire服务器名称 152 config.setServiceName(SERVER_NAME); 153 //设置端口号:默认5222 154 config.setPort(SERVER_PORT); 155 //禁用SSL连接 156 config.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled).setCompressionEnabled(false); 157 //设置Debug 158 config.setDebuggerEnabled(true); 159 //设置离线状态 160 config.setSendPresence(false); 161 //设置开启压缩,可以节省流量 162 config.setCompressionEnabled(true); 163 164 //需要经过同意才可以添加好友 165 Roster.setDefaultSubscriptionMode(Roster.SubscriptionMode.accept_all); 166 167 // 将相应机制隐掉 168 //SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1"); 169 //SASLAuthentication.blacklistSASLMechanism("DIGEST-MD5"); 170 171 connection = new XMPPTCPConnection(config.build()); 172 connection.connect();// 连接到服务器 173 174 return true; 175 } 176 } catch (Exception xe) { 177 xe.printStackTrace(); 178 connection = null; 179 } 180 return false; 181 } 182 183 /** 184 * 关闭连接 185 */ 186 public void closeConnection() { 187 if (connection != null) { 188 // 移除连接监听 189 connection.removeConnectionListener(connectionListener); 190 if (connection.isConnected()) 191 connection.disconnect(); 192 connection = null; 193 } 194 195 Log.i("XmppConnection", "关闭连接"); 196 } 197 198 /** 199 * 删除好友 200 * 201 * @param 202 */ 203 public boolean deleteRosterEntry(RosterEntry rosterEntry) { 204 try { 205 Roster.getInstanceFor(connection).removeEntry(rosterEntry); 206 return true; 207 } catch (Exception e) { 208 e.printStackTrace(); 209 return false; 210 } 211 } 212 213 /** 214 * 判断连接是否通过了身份验证 215 * 即是否已登录 216 * 217 * @return 218 */ 219 public boolean isAuthenticated() { 220 return connection != null && connection.isConnected() && connection.isAuthenticated(); 221 } 222 223 /** 224 * 添加好友 无分组 225 * 226 * @param userName userName 227 * @param name name 228 * @return boolean 229 */ 230 public boolean addUser(String userName, String name) { 231 if (getConnection() == null) 232 return false; 233 try { 234 235 Roster.getInstanceFor(connection).createEntry(userName, name, null); 236 237 return true; 238 } catch (Exception e) { 239 e.printStackTrace(); 240 return false; 241 } 242 } 243 244 /** 245 * 获取账号的全部信息 246 */ 247 public void getAccountAttributes() { 248 try { 249 250 Set<String> accountAttributes = AccountManager.getInstance(connection).getAccountAttributes(); 251 Iterator<String> iterator = accountAttributes.iterator(); 252 while (iterator.hasNext()) { 253 String trim = iterator.next().toString().trim(); 254 Log.e("Account", "获取账号信息成功===" + trim); 255 } 256 } catch (SmackException.NoResponseException e) { 257 e.printStackTrace(); 258 Log.e("Account", "连接服务器失败"); 259 } catch (XMPPException.XMPPErrorException e) { 260 e.printStackTrace(); 261 Log.e("Account", "该账户已存在"); 262 } catch (SmackException.NotConnectedException e) { 263 e.printStackTrace(); 264 Log.e("Account", "服务器连接失败"); 265 } 266 } 267 268 /** 269 * 登录 270 * 271 * @param account 登录帐号 272 * @param password 登录密码 273 * @return true登录成功 274 */ 275 public boolean login(String account, String password) { 276 try { 277 if (getConnection() == null) 278 return false; 279 280 getConnection().login(account, password); 281 282 // 更改在线状态 283 // setPresence(0); 284 285 // 添加连接监听 286 connectionListener = new XMConnectionListener(account, password); 287 getConnection().addConnectionListener(connectionListener); 288 receivedFile(); 289 return true; 290 } catch (Exception xe) { 291 xe.printStackTrace(); 292 } 293 return false; 294 } 295 296 297 /** 298 * 获取用户离线在线状态 1 在线 2 离线 299 */ 300 public int getStatus(RosterEntry entry) { 301 Roster roster = Roster.getInstanceFor(connection); 302 Presence presence = roster.getPresence(entry.getUser() + UserConstants.chatDoMain); 303 304 LoggerUtil.systemOut(entry.getUser() + "用户名"); 305 LoggerUtil.systemOut(presence.getType().name() + "获取到的 类型状态"); 306 LoggerUtil.systemOut(presence.getType().toString() + "获取到的 类型状态"); 307 if (presence.getType() == Presence.Type.available) { 308 return 1;//在线 309 } 310 return 2;//离线 311 } 312 313 /** 314 * 更改用户状态 315 */ 316 public void setPresence(int code) { 317 org.jivesoftware.smack.XMPPConnection con = getConnection(); 318 if (con == null) 319 return; 320 Presence presence; 321 try { 322 switch (code) { 323 case 0: 324 presence = new Presence(Presence.Type.available); 325 con.sendStanza(presence); 326 Log.v("state", "设置在线"); 327 break; 328 case 1: 329 presence = new Presence(Presence.Type.available); 330 presence.setMode(Presence.Mode.chat); 331 con.sendStanza(presence); 332 Log.v("state", "设置Q我吧"); 333 break; 334 case 2: 335 presence = new Presence(Presence.Type.available); 336 presence.setMode(Presence.Mode.dnd); 337 con.sendStanza(presence); 338 Log.v("state", "设置忙碌"); 339 break; 340 case 3: 341 presence = new Presence(Presence.Type.available); 342 presence.setMode(Presence.Mode.away); 343 con.sendStanza(presence); 344 Log.v("state", "设置离开"); 345 break; 346 case 4: 347 // Roster roster = con.getRoster(); 348 // Collection<RosterEntry> entries = roster.getEntries(); 349 // for (RosterEntry entry : entries) { 350 // presence = new Presence(Presence.Type.unavailable); 351 // presence.setPacketID(Packet.ID_NOT_AVAILABLE); 352 // presence.setFrom(con.getUser()); 353 // presence.setTo(entry.getUser()); 354 // con.sendPacket(presence); 355 // Log.v("state", presence.toXML()); 356 // } 357 // // 向同一用户的其他客户端发送隐身状态 358 // presence = new Presence(Presence.Type.unavailable); 359 // presence.setPacketID(Packet.ID_NOT_AVAILABLE); 360 // presence.setFrom(con.getUser()); 361 // presence.setTo(StringUtils.parseBareAddress(con.getUser())); 362 // con.sendStanza(presence); 363 // Log.v("state", "设置隐身"); 364 // break; 365 case 5: 366 presence = new Presence(Presence.Type.unavailable); 367 con.sendStanza(presence); 368 Log.v("state", "设置离线"); 369 break; 370 default: 371 break; 372 } 373 } catch (Exception e) { 374 e.printStackTrace(); 375 } 376 } 377 378 /** 379 * 获取所有组 380 * 381 * @return 所有组集合 382 */ 383 public List<RosterGroup> getGroups() { 384 if (getConnection() == null) 385 return null; 386 List<RosterGroup> groupList = new ArrayList<>(); 387 Collection<RosterGroup> rosterGroup = Roster.getInstanceFor(connection).getGroups(); 388 for (RosterGroup aRosterGroup : rosterGroup) { 389 groupList.add(aRosterGroup); 390 } 391 return groupList; 392 } 393 394 /** 395 * 获取某个组里面的所有好友 396 * 397 * @param groupName 组名 398 * @return List<RosterEntry> 399 */ 400 public List<RosterEntry> getEntriesByGroup(String groupName) { 401 if (getConnection() == null) 402 return null; 403 List<RosterEntry> EntriesList = new ArrayList<>(); 404 RosterGroup rosterGroup = Roster.getInstanceFor(connection).getGroup(groupName); 405 Collection<RosterEntry> rosterEntry = rosterGroup.getEntries(); 406 for (RosterEntry aRosterEntry : rosterEntry) { 407 EntriesList.add(aRosterEntry); 408 } 409 return EntriesList; 410 } 411 412 /** 413 * 获取所有好友信息 414 * 415 * @return List<RosterEntry> 416 */ 417 public List<RosterEntry> getAllEntries() { 418 if (getConnection() == null) 419 return null; 420 List<RosterEntry> Enlist = new ArrayList<>(); 421 Collection<RosterEntry> rosterEntry = Roster.getInstanceFor(connection).getEntries(); 422 for (RosterEntry aRosterEntry : rosterEntry) { 423 Enlist.add(aRosterEntry); 424 } 425 return Enlist; 426 } 427 428 429 /** 430 * 添加一个分组 431 * 432 * @param groupName groupName 433 * @return boolean 434 */ 435 public boolean addGroup(String groupName) { 436 if (getConnection() == null) 437 return false; 438 try { 439 Roster.getInstanceFor(connection).createGroup(groupName); 440 Log.v("addGroup", groupName + "創建成功"); 441 return true; 442 } catch (Exception e) { 443 e.printStackTrace(); 444 return false; 445 } 446 } 447 448 /** 449 * 删除分组 450 * 451 * @param groupName groupName 452 * @return boolean 453 */ 454 public boolean removeGroup(String groupName) { 455 return true; 456 } 457 458 459 /** 460 * 文件转字节 461 * 462 * @param file file 463 * @return byte[] 464 * @throws IOException 465 */ 466 private byte[] getFileBytes(File file) throws IOException { 467 BufferedInputStream bis = null; 468 try { 469 bis = new BufferedInputStream(new FileInputStream(file)); 470 int bytes = (int) file.length(); 471 byte[] buffer = new byte[bytes]; 472 int readBytes = bis.read(buffer); 473 if (readBytes != buffer.length) { 474 throw new IOException("Entire file not read"); 475 } 476 return buffer; 477 } finally { 478 if (bis != null) { 479 bis.close(); 480 } 481 } 482 } 483 484 485 /** 486 * 发送群组聊天消息 487 * 488 * @param muc muc 489 * @param message 消息文本 490 */ 491 public void sendGroupMessage(MultiUserChat muc, String message) { 492 try { 493 muc.sendMessage(message); 494 } catch (Exception e) { 495 e.printStackTrace(); 496 } 497 } 498 499 500 /** 501 * 修改密码 502 * 503 * @return true成功 504 */ 505 public boolean changePassword(String pwd) { 506 if (getConnection() == null) 507 return false; 508 try { 509 AccountManager.getInstance(connection).changePassword(pwd); 510 return true; 511 } catch (Exception e) { 512 e.printStackTrace(); 513 return false; 514 } 515 } 516 517 518 519 520 /** 521 * 创建群聊聊天室 522 * 523 * @param roomName 聊天室名字 524 * @param nickName 创建者在聊天室中的昵称 525 * @param password 聊天室密码 526 * @return 527 */ 528 public MultiUserChat createChatRoom(String roomName, String nickName, String password) { 529 MultiUserChat muc; 530 try { 531 // 创建一个MultiUserChat 532 muc = MultiUserChatManager.getInstanceFor(connection).getMultiUserChat(roomName + "@conference.192.168.0.195"); 533 // 创建聊天室 534 boolean isCreated = muc.createOrJoin(nickName); 535 if (isCreated) { 536 // 获得聊天室的配置表单 537 Form form = muc.getConfigurationForm(); 538 // 根据原始表单创建一个要提交的新表单。 539 Form submitForm = form.createAnswerForm(); 540 // 向要提交的表单添加默认答复 541 List<FormField> fields = form.getFields(); 542 for (int i = 0; fields != null && i < fields.size(); i++) { 543 if (FormField.Type.hidden != fields.get(i).getType() && 544 fields.get(i).getVariable() != null) { 545 // 设置默认值作为答复 546 submitForm.setDefaultAnswer(fields.get(i).getVariable()); 547 } 548 } 549 // 设置聊天室的新拥有者 550 List owners = new ArrayList(); 551 owners.add(connection.getUser());// 用户JID 552 submitForm.setAnswer("muc#roomconfig_roomowners", owners); 553 // 设置聊天室是持久聊天室,即将要被保存下来 554 submitForm.setAnswer("muc#roomconfig_persistentroom", true); 555 // 房间仅对成员开放 556 submitForm.setAnswer("muc#roomconfig_membersonly", false); 557 // 允许占有者邀请其他人 558 submitForm.setAnswer("muc#roomconfig_allowinvites", true); 559 if (password != null && password.length() != 0) { 560 // 进入是否需要密码 561 submitForm.setAnswer("muc#roomconfig_passwordprotectedroom", true); 562 // 设置进入密码 563 submitForm.setAnswer("muc#roomconfig_roomsecret", password); 564 } 565 // 能够发现占有者真实 JID 的角色 566 // submitForm.setAnswer("muc#roomconfig_whois", "anyone"); 567 // 登录房间对话 568 submitForm.setAnswer("muc#roomconfig_enablelogging", true); 569 // 仅允许注册的昵称登录 570 submitForm.setAnswer("x-muc#roomconfig_reservednick", true); 571 // 允许使用者修改昵称 572 submitForm.setAnswer("x-muc#roomconfig_canchangenick", false); 573 // 允许用户注册房间 574 submitForm.setAnswer("x-muc#roomconfig_registration", false); 575 // 发送已完成的表单(有默认值)到服务器来配置聊天室 576 muc.sendConfigurationForm(submitForm); 577 578 } else { 579 580 } 581 } catch (XMPPException | SmackException e) { 582 e.printStackTrace(); 583 584 return null; 585 } 586 return muc; 587 } 588 /** 589 * 加入一个群聊聊天室 590 * 591 * @param jid 聊天室ip 格式为>>群组名称@conference.ip 592 * @param nickName 用户在聊天室中的昵称 593 * @param password 聊天室密码 没有密码则传"" 594 * @return 595 */ 596 public MultiUserChat join(String jid, String nickName, String password) { 597 try { 598 // 使用XMPPConnection创建一个MultiUserChat窗口 599 MultiUserChat muc = MultiUserChatManager.getInstanceFor(connection).getMultiUserChat(jid); 600 // 聊天室服务将会决定要接受的历史记录数量 601 DiscussionHistory history = new DiscussionHistory(); 602 history.setMaxChars(0); 603 // 用户加入聊天室 604 muc.join(nickName, password); 605 return muc; 606 } catch (XMPPException | SmackException e) { 607 e.printStackTrace(); 608 if ("XMPPError: not-authorized - auth".equals(e.getMessage())) { 609 //需要密码加入 610 } 611 return null; 612 } 613 } 614 /** 615 * 获取服务器上的聊天室 616 */ 617 public List<HostedRoom> getHostedRoom() { 618 MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(connection); 619 try { 620 //serviceNames->conference.106.14.20.176 621 List<String> serviceNames = manager.getServiceNames(); 622 for (int i = 0; i < serviceNames.size(); i++) { 623 return manager.getHostedRooms(serviceNames.get(i)); 624 } 625 } catch (Exception e) { 626 e.printStackTrace(); 627 } 628 return null; 629 } 630 /** 631 * @param jid 格式为>>群组名称@conference.ip 632 */ 633 private void initRoomListener(String jid) { 634 MultiUserChat multiUserChat = MultiUserChatManager.getInstanceFor(connection).getMultiUserChat(jid); 635 multiUserChat.addMessageListener(new MessageListener() { 636 @Override 637 public void processMessage(final Message message) { 638 //当消息返回为空的时候,表示用户正在聊天窗口编辑信息并未发出消息 639 if (!TextUtils.isEmpty(message.getBody())) { 640 //收到的消息 641 } 642 } 643 }); 644 //发送群聊消息 645 // MultiUserChat multiUserChat = MultiUserChatManager.getInstanceFor(connection).getMultiUserChat(jid); 646 // multiUserChat.sendMessage("Hello World"); 647 648 } 649 650 // /** 651 // * 创建房间 652 // * 653 // * @param roomName 房间名称 654 // */ 655 // public MultiUserChat createRoom(String roomName, String password) { 656 // if (getConnection() == null) 657 // return null; 658 // 659 // MultiUserChat muc = null; 660 // try { 661 // // 创建一个MultiUserChat 662 // muc = MultiUserChatManager.getInstanceFor(connection).getMultiUserChat(roomName+"@conference.192.168.0.195"); 663 // // 创建聊天室 664 // muc.create(roomName); 665 // // 获得聊天室的配置表单 666 // Form form = muc.getConfigurationForm(); 667 // // 根据原始表单创建一个要提交的新表单。 668 // Form submitForm = form.createAnswerForm(); 669 // // 向要提交的表单添加默认答复 670 // for (FormField formField : form.getFields()) { 671 // if (FormField.Type.hidden == formField.getType() 672 // && formField.getVariable() != null) { 673 // // 设置默认值作为答复 674 // submitForm.setDefaultAnswer(formField.getVariable()); 675 // } 676 // } 677 // // 设置聊天室的新拥有者 678 // List<String> owners = new ArrayList<>(); 679 // owners.add(getConnection().getUser());// 用户JID 680 // submitForm.setAnswer("muc#roomconfig_roomowners", owners); 681 // // 设置聊天室是持久聊天室,即将要被保存下来 682 // submitForm.setAnswer("muc#roomconfig_persistentroom", true); 683 // // 房间仅对成员开放 684 // submitForm.setAnswer("muc#roomconfig_membersonly", false); 685 // // 允许占有者邀请其他人 686 // submitForm.setAnswer("muc#roomconfig_allowinvites", true); 687 // if (!password.equals("")) { 688 // // 进入是否需要密码 689 // submitForm.setAnswer("muc#roomconfig_passwordprotectedroom", 690 // true); 691 // // 设置进入密码 692 // submitForm.setAnswer("muc#roomconfig_roomsecret", password); 693 // } 694 // // 能够发现占有者真实 JID 的角色 695 // // submitForm.setAnswer("muc#roomconfig_whois", "anyone"); 696 // // 登录房间对话 697 // submitForm.setAnswer("muc#roomconfig_enablelogging", true); 698 // // 仅允许注册的昵称登录 699 // submitForm.setAnswer("x-muc#roomconfig_reservednick", true); 700 // // 允许使用者修改昵称 701 // submitForm.setAnswer("x-muc#roomconfig_canchangenick", false); 702 // // 允许用户注册房间 703 // submitForm.setAnswer("x-muc#roomconfig_registration", false); 704 // // 发送已完成的表单(有默认值)到服务器来配置聊天室 705 // muc.sendConfigurationForm(submitForm); 706 // } catch (Exception e) { 707 // e.printStackTrace(); 708 // LoggerUtil.systemOut(e.toString()); 709 // return null; 710 // } 711 // return muc; 712 // } 713 714 /** 715 * 加入会议室 716 * 717 * @param user 昵称 718 * @param roomsName 会议室名 719 */ 720 public MultiUserChat joinMultiUserChat(String user, String roomsName) { 721 if (getConnection() == null) 722 return null; 723 try { 724 // 使用XMPPConnection创建一个MultiUserChat窗口 725 MultiUserChat muc = MultiUserChatManager.getInstanceFor(connection).getMultiUserChat(connection.getServiceName()); 726 727 // 用户加入聊天室 728 muc.join(user); 729 730 Log.i("MultiUserChat", "会议室【" + roomsName + "】加入成功........"); 731 return muc; 732 } catch (Exception e) { 733 e.printStackTrace(); 734 Log.i("MultiUserChat", "会议室【" + roomsName + "】加入失败........"); 735 return null; 736 } 737 } 738 739 740 741 742 /** 743 * 判断是否是好友 744 * 745 * @param 746 * @param user 747 * @return 748 */ 749 public boolean isMyFriend(String user) { 750 751 List<RosterEntry> allEntries = XmppConnection.getInstance().getAllEntries(); 752 for (int i = 0; i < allEntries.size(); i++) { 753 LoggerUtil.systemOut("allEntries.get(i).getUser() == " + allEntries.get(i).getUser()); 754 LoggerUtil.systemOut("user == " + user); 755 if (allEntries.get(i).getUser().equals(user)) { 756 LoggerUtil.systemOut(allEntries.get(i).getType().toString() + "type"); 757 if (allEntries.get(i).getType().toString().equals("both")) { 758 return true; 759 } else { 760 return false; 761 } 762 } 763 } 764 return false; 765 766 } 767 /** 768 * 查询会议室成员名字 769 * 770 * @param muc 771 */ 772 public List<String> findMulitUser(MultiUserChat muc) { 773 if (getConnection() == null) 774 return null; 775 List<String> listUser = new ArrayList<>(); 776 List<String> occupants = muc.getOccupants(); 777 // 遍历出聊天室人员名称 778 for (String entityFullJid : occupants) { 779 // 聊天室成员名字 780 String name = entityFullJid; 781 listUser.add(name); 782 } 783 return listUser; 784 } 785 786 787 /** 788 * 判断OpenFire用户的状态 strUrl : 789 * url格式 - http://my.openfire.com:9090/plugins/presence 790 * /status?jid=user1@SERVER_NAME&type=xml 791 * 返回值 : 0 - 用户不存在; 1 - 用户在线; 2 - 用户离线 792 * 说明 :必须要求 OpenFire加载 presence 插件,同时设置任何人都可以访问 793 */ 794 public int IsUserOnLine(String user) { 795 String url = "http://" + SERVER_HOST + ":9090/plugins/presence/status?" + 796 "jid=" + user; 797 int shOnLineState = 0; // 不存在 798 try { 799 URL oUrl = new URL(url); 800 URLConnection oConn = oUrl.openConnection(); 801 if (oConn != null) { 802 BufferedReader oIn = new BufferedReader(new InputStreamReader( 803 oConn.getInputStream())); 804 String strFlag = oIn.readLine(); 805 oIn.close(); 806 System.out.println("strFlag" + strFlag); 807 if (strFlag.contains("type=\"unavailable\"")) { 808 shOnLineState = 2; 809 } 810 if (strFlag.contains("type=\"error\"")) { 811 shOnLineState = 0; 812 } else if (strFlag.contains("priority") || strFlag.contains("id=\"")) { 813 shOnLineState = 1; 814 } 815 } 816 } catch (Exception e) { 817 e.printStackTrace(); 818 } 819 820 return shOnLineState; 821 } 822 823 /** 824 * 创建聊天窗口 825 * 826 * @param JID JID 827 * @return Chat 828 */ 829 public Chat getFriendChat(String JID, ChatMessageListener listener) { 830 try { 831 return ChatManager.getInstanceFor(XmppConnection.getInstance().getConnection()) 832 .createChat(JID, listener); 833 } catch (Exception e) { 834 e.printStackTrace(); 835 } 836 return null; 837 } 838 839 /** 840 * 发送单人聊天消息 841 * 842 * @param chat chat 843 * @param message 消息文本 844 */ 845 public void sendSingleMessage(Chat chat, String message) { 846 try { 847 chat.sendMessage(message); 848 } catch (SmackException.NotConnectedException e) { 849 e.printStackTrace(); 850 } 851 } 852 853 /** 854 * 发消息 855 * 856 * @param chat chat 857 * @param muc muc 858 * @param message message 859 */ 860 public void sendMessage(Chat chat, MultiUserChat muc, String message) { 861 if (chat != null) { 862 sendSingleMessage(chat, message); 863 } else if (muc != null) { 864 sendGroupMessage(muc, message); 865 } 866 } 867 868 869 /** 870 * 获取离线消息 871 * 872 * @return 873 */ 874 int i = 0; 875 876 public void getHisMessage(Context context) { 877 LoggerUtil.systemOut("访问次数 " + (i++)); 878 setPresence(5); 879 if (getConnection() == null) 880 return; 881 882 try { 883 OfflineMessageManager offlineManager = new OfflineMessageManager(getConnection()); 884 List<Message> messageList = offlineManager.getMessages(); 885 int count = offlineManager.getMessageCount(); 886 LoggerUtil.systemOut("离线消息个数" + count); 887 if (count <= 0) { 888 setPresence(0); 889 return; 890 } 891 for (Message message : messageList) { 892 String[] send = message.getFrom().split("@");// 发送方 893 String[] receiver = message.getTo().split("@");// 接收方 894 saveofflineMessage(context, receiver[0], send[0], send[0], message.getBody(), "chat"); 895 saveofflineChatData(context, receiver[0], send[0], send[0], message.getBody()); 896 897 } 898 offlineManager.deleteMessages(); 899 } catch (Exception e) { 900 e.printStackTrace(); 901 } 902 setPresence(0); 903 } 904 905 public boolean saveofflineMessage(Context context, String main, final String users, final String to, final String content, String type) { 906 907 Cursor cursor = MessageDao.getInstance(context).queryIshasResult(context, main, type); 908 909 if (cursor != null) { 910 //更新 911 if (type.equals("add")) { 912 int result = cursor.getInt(cursor.getColumnIndex("result")); 913 if (result == 0) { 914 int id = cursor.getInt(cursor.getColumnIndex("id")); 915 MessageDao.getInstance(context).update(context, id, content, 1); 916 return true; 917 } else { 918 return false; 919 } 920 } else { 921 int id = cursor.getInt(cursor.getColumnIndex("id")); 922 MessageDao.getInstance(context).update(context, id, content, 1); 923 return true; 924 } 925 926 } else { 927 //插入 928 List<XmppUser> list1 = XmppConnection.getInstance().searchUsers(users); 929 XmppMessage xm = new XmppMessage(to, 930 type, 931 new XmppUser(list1.get(0).getUserName(), list1.get(0).getName()), 932 TimeUtil.getDate(), 933 content, 934 1, 935 main 936 ); 937 LoggerUtil.systemOut("to" + to); 938 MessageDao.getInstance(context).inserts(context, xm); 939 940 return true; 941 } 942 943 944 } 945 946 public void saveofflineChatData(Context context, String main, final String users, final String to, final String content) { 947 XmppChat xc = new XmppChat(main, users, "", "", 2, content, to, 1, new Date().getTime()); 948 FriendChatDao.getInstance(context).insert(context, xc); 949 } 950 951 /** 952 * 注册 953 * 954 * @param account 注册帐号 955 * @param password 注册密码 956 * @return 1、注册成功 0、注册失败 957 */ 958 public boolean register(String account, String password, String nickName) { 959 if (getConnection() == null) 960 return false; 961 try { 962 // new 963 Map<String, String> attributes = new HashMap<>(); 964 attributes.put("name", nickName); 965 AccountManager.getInstance(connection).createAccount(account, password, attributes); 966 967 } catch (XMPPException | SmackException e) { 968 e.printStackTrace(); 969 return false; 970 } 971 972 return true; 973 } 974 975 976 /** 977 * 设置昵称 978 * 979 * @param 980 * @param rosterEntry 981 * @return 982 */ 983 public boolean setNickName(RosterEntry rosterEntry, String nickName) { 984 try { 985 rosterEntry.setName(nickName); 986 return true; 987 } catch (SmackException.NotConnectedException e) { 988 e.printStackTrace(); 989 return false; 990 } catch (SmackException.NoResponseException e) { 991 e.printStackTrace(); 992 return false; 993 } catch (XMPPException.XMPPErrorException e) { 994 e.printStackTrace(); 995 return false; 996 } 997 } 998 999 private OutgoingFileTransfer fileTransfer; 1000 1001 // 发送文件 1002 public void sendFile(String user, File file) throws Exception { 1003 1004 FileTransferManager instanceFor = FileTransferManager.getInstanceFor(connection); 1005 1006 fileTransfer = instanceFor.createOutgoingFileTransfer(user); 1007 fileTransfer.sendFile(file, "send file"); 1008 1009 System.out.println("发送成功" + user + file.exists() + "文件路径" + file.getPath()); 1010 } 1011 1012 /* 1013 * 语音接收文件监听接收文件 1014 * 1015 * @author Administrator 1016 * 1017 */ 1018 private FileTransferListener mfiletransfransferlistener;//接受语音文件监听 1019 1020 public void receivedFile() { 1021 1022 // Create the file transfer manager 1023 1024 FileTransferManager manager = FileTransferManager.getInstanceFor(connection); 1025 mfiletransfransferlistener = new FileTransferListener() { 1026 public void fileTransferRequest(FileTransferRequest request) { 1027 // Check to see if the request should be accepted 1028 1029 LoggerUtil.systemOut("有文件接收了" + request.toString()); 1030 // Accept it 1031 IncomingFileTransfer transfer = request.accept(); 1032 try { 1033 1034 File filePath = new File(Environment.getExternalStorageDirectory(), "fcyt"); 1035 1036 if (!filePath.exists()) { 1037 filePath.mkdirs(); 1038 } 1039 // File file = new File( Environment 1040 // .getExternalStorageDirectory()+"/fcyt/" 1041 // +"/"+ request.getFileName()); 1042 1043 1044 String streamID = request.getStreamID(); 1045 1046 LoggerUtil.systemOut("streamID", streamID); 1047 File file = new File(filePath, request.getFileName()); 1048 System.out.println(request.getFileName() + "接收路径" + file.getPath() + "接收语音文件名称" + file.exists()); 1049 1050 transfer.recieveFile(file); 1051 1052 } catch (Exception e) { 1053 e.printStackTrace(); 1054 } 1055 1056 } 1057 }; 1058 1059 1060 manager.addFileTransferListener(mfiletransfransferlistener); 1061 1062 } 1063 1064 /** 1065 * 查找用户 1066 * 1067 * @param 1068 * @param userName 1069 * @return 1070 */ 1071 public List<XmppUser> searchUsers(String userName) { 1072 List<XmppUser> list = new ArrayList<XmppUser>(); 1073 UserSearchManager userSearchManager = new UserSearchManager(connection); 1074 try { 1075 Form searchForm = userSearchManager.getSearchForm("search." 1076 + connection.getServiceName()); 1077 Form answerForm = searchForm.createAnswerForm(); 1078 answerForm.setAnswer("Username", true); 1079 answerForm.setAnswer("Name", true); 1080 answerForm.setAnswer("search", userName); 1081 ReportedData data = userSearchManager.getSearchResults(answerForm, "search." + connection.getServiceName()); 1082 List<ReportedData.Row> rows = data.getRows(); 1083 for (ReportedData.Row row : rows) { 1084 XmppUser user = new XmppUser(null, null); 1085 user.setUserName(row.getValues("Username").toString().replace("]", "").replace("[", "")); 1086 user.setName(row.getValues("Name").toString().replace("]", "").replace("[", "")); 1087 list.add(user); 1088 } 1089 1090 } catch (Exception e) { 1091 1092 } 1093 return list; 1094 } 1095 1096 public static void configure(ProviderManager pm) { 1097 1098 try { 1099 Class.forName("org.jivesoftware.smack.ReconnectionManager"); 1100 } catch (Exception e) { 1101 e.printStackTrace(); 1102 } 1103 1104 // Private Data Storage 1105 pm.addIQProvider("query", "jabber:iq:private", new PrivateDataManager.PrivateDataIQProvider()); 1106 1107 // Time 1108 try { 1109 pm.addIQProvider("query", "jabber:iq:time", Class.forName("org.jivesoftware.smackx.packet.Time")); 1110 } catch (ClassNotFoundException e) { 1111 Log.w("TestClient", "Can't load class for org.jivesoftware.smackx.packet.Time"); 1112 } 1113 1114 // // Roster Exchange 1115 // pm.addExtensionProvider("x", "jabber:x:roster", new RosterLoadedListener() { 1116 // }); 1117 // 1118 // // Message Events 1119 // pm.addExtensionProvider("x", "jabber:x:event", new MessageEventProvider()); 1120 1121 // Chat State 1122 pm.addExtensionProvider("active", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider()); 1123 pm.addExtensionProvider("composing", "http://jabber.org/protocol/chatstates", 1124 new ChatStateExtension.Provider()); 1125 pm.addExtensionProvider("paused", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider()); 1126 pm.addExtensionProvider("inactive", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider()); 1127 pm.addExtensionProvider("gone", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider()); 1128 1129 // XHTML 1130 pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im", new XHTMLExtensionProvider()); 1131 1132 // Group Chat Invitations 1133 pm.addExtensionProvider("x", "jabber:x:conference", new GroupChatInvitation.Provider()); 1134 1135 // Service Discovery # Items 1136 pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider()); 1137 1138 // Service Discovery # Info 1139 pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider()); 1140 1141 // Data Forms 1142 pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider()); 1143 1144 // MUC User 1145 pm.addExtensionProvider("x", "http://jabber.org/protocol/muc#user", new MUCUserProvider()); 1146 1147 // MUC Admin 1148 pm.addIQProvider("query", "http://jabber.org/protocol/muc#admin", new MUCAdminProvider()); 1149 1150 // MUC Owner 1151 pm.addIQProvider("query", "http://jabber.org/protocol/muc#owner", new MUCOwnerProvider()); 1152 1153 // Delayed Delivery 1154 pm.addExtensionProvider("x", "jabber:x:delay", new DelayInformationProvider()); 1155 1156 // Version 1157 try { 1158 pm.addIQProvider("query", "jabber:iq:version", Class.forName("org.jivesoftware.smackx.packet.Version")); 1159 } catch (ClassNotFoundException e) { 1160 // Not sure what's happening here. 1161 } 1162 1163 // VCard 1164 pm.addIQProvider("vCard", "vcard-temp", new VCardProvider()); 1165 1166 // Offline Message Requests 1167 pm.addIQProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageRequest.Provider()); 1168 1169 // Offline Message Indicator 1170 pm.addExtensionProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageInfo.Provider()); 1171 1172 // Last Activity 1173 pm.addIQProvider("query", "jabber:iq:last", new LastActivity.Provider()); 1174 1175 // User Search 1176 pm.addIQProvider("query", "jabber:iq:search", new UserSearch.Provider()); 1177 1178 // SharedGroupsInfo 1179 pm.addIQProvider("sharedgroup", "http://www.jivesoftware.org/protocol/sharedgroup", 1180 new SharedGroupsInfo.Provider()); 1181 1182 // JEP-33: Extended Stanza Addressing 1183 pm.addExtensionProvider("addresses", "http://jabber.org/protocol/address", new MultipleAddressesProvider()); 1184 1185 // FileTransfer 1186 pm.addIQProvider("si", "http://jabber.org/protocol/si", new StreamInitiationProvider()); 1187 pm.addIQProvider("query", "http://jabber.org/protocol/bytestreams", new BytestreamsProvider()); 1188 pm.addIQProvider("open", "http://jabber.org/protocol/ibb", new OpenIQProvider()); 1189 pm.addIQProvider("close", "http://jabber.org/protocol/ibb", new CloseIQProvider()); 1190 // pm.addExtensionProvider("data", "http://jabber.org/protocol/ibb", new DataPacketProvider()); 1191 1192 // Privacy 1193 pm.addIQProvider("query", "jabber:iq:privacy", new PrivacyProvider()); 1194 pm.addIQProvider("command", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider()); 1195 pm.addExtensionProvider("malformed-action", "http://jabber.org/protocol/commands", 1196 new AdHocCommandDataProvider.MalformedActionError()); 1197 pm.addExtensionProvider("bad-locale", "http://jabber.org/protocol/commands", 1198 new AdHocCommandDataProvider.BadLocaleError()); 1199 pm.addExtensionProvider("bad-payload", "http://jabber.org/protocol/commands", 1200 new AdHocCommandDataProvider.BadPayloadError()); 1201 pm.addExtensionProvider("bad-sessionid", "http://jabber.org/protocol/commands", 1202 new AdHocCommandDataProvider.BadSessionIDError()); 1203 pm.addExtensionProvider("session-expired", "http://jabber.org/protocol/commands", 1204 new AdHocCommandDataProvider.SessionExpiredError()); 1205 } 1206 1207 1208 }
XmppService后台监听接收消息
import android.app.Service; import android.content.ContentResolver; import android.content.Intent; import android.database.Cursor; import android.media.AudioManager; import android.media.SoundPool; import android.os.Handler; import android.os.IBinder; import android.os.Vibrator; import android.util.Log;import org.greenrobot.eventbus.EventBus; import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.Stanza;import java.util.Date; import java.util.HashMap; import java.util.List;import cnpc.fcyt.fcydyy.R; import cnpc.fcyt.fcydyy.constant.ConfigConstants; import cnpc.fcyt.fcydyy.event.LogoutEvent; import cnpc.fcyt.fcydyy.event.RefreshChatMessageEvent; import cnpc.fcyt.fcydyy.event.RefreshRedDotEvent; import cnpc.fcyt.fcydyy.util.LoggerUtil; import cnpc.fcyt.fcydyy.xmpp.bean.XmppChat; import cnpc.fcyt.fcydyy.xmpp.bean.XmppMessage; import cnpc.fcyt.fcydyy.xmpp.bean.XmppUser; import cnpc.fcyt.fcydyy.xmpp.dao.FriendChatDao; import cnpc.fcyt.fcydyy.xmpp.dao.MessageDao; import cnpc.fcyt.fcydyy.xmpp.util.SaveUserUtil; import cnpc.fcyt.fcydyy.xmpp.util.TimeUtil; import cnpc.fcyt.fcydyy.xmpp.util.UserConstants;public class XmppService extends Service {private XMPPConnection con;public static ContentResolver resolver;public static HashMap<String, Object> map;String send[];//发送方String receiver[];//接收方public static String user;public static SoundPool pool;public static Vibrator vibrator;Handler handler = new Handler() {@Overridepublic void handleMessage(android.os.Message msg) {super.handleMessage(msg);if (msg.what == 1) {boolean authenticated = XmppConnection.getInstance().isAuthenticated();AbstractXMPPConnection connection = XmppConnection.getInstance().getConnection();if (connection!=null){boolean connected = connection.isConnected();if (!connected){LoggerUtil.systemOut("IM服务器无法连接");XmppConnection.getInstance().setConnectionToNull();EventBus.getDefault().post(new LogoutEvent());}else {if (!authenticated) {LoggerUtil.systemOut("掉线了");EventBus.getDefault().post(new LogoutEvent());}else {LoggerUtil.systemOut("连接ing");}}}handler.sendEmptyMessageDelayed(1, 3000);}}};@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();resolver = getContentResolver();map = new HashMap<>();con = XmppConnection.getInstance().getConnection();user = SaveUserUtil.loadAccount(XmppService.this).getUser();pool = new SoundPool(10, AudioManager.STREAM_SYSTEM, 5);pool.load(this, R.raw.tishi, 1);vibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);LoggerUtil.systemOut("启动服务......");handler.sendEmptyMessageDelayed(1, 3000);}@Overridepublic IBinder onBind(Intent intent) {return null;}String messageID = "";@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {//'zS4Xw-62if (null != con && con.isConnected()) {con.addAsyncStanzaListener(new StanzaListener() {@Overridepublic void processPacket(Stanza packet) throws SmackException.NotConnectedException {String stanzaId = packet.getStanzaId();LoggerUtil.systemOut(stanzaId + "消息ID");LoggerUtil.systemOut("messageID" + messageID);if (messageID.equals(stanzaId) || stanzaId == null) {} else {if (stanzaId != null) {messageID = stanzaId;}LoggerUtil.systemOut(packet.toString() + "通知来了");if (packet instanceof Presence) {Presence presence = (Presence) packet;send = presence.getFrom().split("@");// 发送方receiver = presence.getTo().split("@");// 接收方// Presence.Type有7中状态LoggerUtil.systemOut(presence.getType() + "信息类型");if (presence.getType().equals(Presence.Type.subscribe)) {// 好友申请LoggerUtil.systemOut(send[0] + "\t好友申请加为好友\t type="+ presence.getType().toString());sendBroad("add");} else if (presence.getType().equals(Presence.Type.subscribed)) {// 同意添加好友LoggerUtil.systemOut(send[0] + "\t同意添加好友\t type="+ presence.getType().toString());sendBroad("tongyi");} else if (presence.getType().equals(Presence.Type.unsubscribe)) {// 删除好友LoggerUtil.systemOut(send[0] + "\t删除好友\t type="+ presence.getType().toString());} else if (presence.getType().equals(Presence.Type.unsubscribed)) {// 拒绝对放的添加请求LoggerUtil.systemOut(send[0] + "\t拒绝添加好友\t type="+ presence.getType().toString());sendBroad("jujue");} else if (presence.getType().equals(Presence.Type.unavailable)) {// 好友下线Log.i("service", send[0] + "\t 下线了");LoggerUtil.systemOut(send[0] + "\t下线了\t type="+ presence.getType().toString());sendBroad("status", 6);} else if (presence.getType().equals(Presence.Type.available)) {// 好友上线//0.在线 1.Q我吧 2.忙碌 3.勿扰 4.离开 5.隐身 6.离线if (presence.getMode() == Presence.Mode.chat) {//Q我吧Log.i("service", send[0] + "\t 的状态改为了 Q我吧");sendBroad("status", 1);} else if (presence.getMode() == Presence.Mode.dnd) {//忙碌Log.i("service", send[0] + "\t 的状态改为了 忙碌了");sendBroad("status", 2);} else if (presence.getMode() == Presence.Mode.xa) {//忙碌Log.i("service", send[0] + "\t 的状态改为了 勿扰了");sendBroad("status", 3);} else if (presence.getMode() == Presence.Mode.away) {//离开Log.i("service", send[0] + "\t 的状态改为了 离开了");sendBroad("status", 4);} else {Log.i("service", send[0] + "\t 的状态改为了 上线了");sendBroad("status", 0);}}} else if (packet instanceof Message) {Message msg = (Message) packet;EventBus.getDefault().post(new RefreshRedDotEvent());int viewType;if (msg.getBody() != null) {if (msg.getBody().length() > 3 && msg.getBody().toString().substring(0, 4).equals("http")) {viewType = 2;} else {viewType = 1;}XmppChat xc = new XmppChat(UserConstants.loginuser, packet.getFrom().replace(UserConstants.chatDoMain, ""), "", "", 2, msg.getBody().toString(), packet.getFrom().replace(UserConstants.chatDoMain, ""), viewType, new Date().getTime());FriendChatDao.getInstance(XmppService.this).insert(XmppService.this, xc);sendBroad("chat", xc);}}}}}, null);}return super.onStartCommand(intent, flags, startId);}private void sendBroad(String type, XmppChat xc) {Intent intent;intent = new Intent("xmpp_receiver");intent.putExtra("type", type);intent.putExtra("chat", xc);sendBroadcast(intent);}private void sendBroad(String type, int status) {map.put(send[0], status);Intent intent;intent = new Intent("xmpp_receiver");intent.putExtra("type", type);sendBroadcast(intent);}private void sendBroad(String type) {String str_content = "";String str_type = "";switch (type) {case "add":str_content = "请求加为好友";str_type = "add";break;case "tongyi":str_content = "同意添加好友";str_type = "tongyi";break;case "jujue":str_content = "拒绝添加好友";str_type = "jujue";break;}LoggerUtil.systemOut(send[0] + "发送人");LoggerUtil.systemOut(receiver[0] + "接收人");if (msgDatas(receiver[0], send[0], send[0], str_content, str_type)) {if (pool != null && ConfigConstants.getSound(this)) {pool.play(1, 1, 1, 0, 0, 1);}if (vibrator != null && ConfigConstants.getShake(this)) {vibrator.vibrate(500);}Intent intent;intent = new Intent("xmpp_receiver");intent.putExtra("type", type);sendBroadcast(intent);}}public boolean msgDatas(final String main, final String users, final String to, final String content, String type) {Cursor cursor = MessageDao.getInstance(this).queryIshasResult(this, main, type);if (cursor != null) {//更新if (type.equals("add")) {int result = cursor.getInt(cursor.getColumnIndex("result"));if (result == 0) {int id = cursor.getInt(cursor.getColumnIndex("id"));MessageDao.getInstance(this).update(this, id, content, 1);//刷新聊天页面EventBus.getDefault().post(new RefreshChatMessageEvent());return true;} else {return false;}} else {int id = cursor.getInt(cursor.getColumnIndex("id"));MessageDao.getInstance(this).update(this, id, content, 1);//刷新聊天页面EventBus.getDefault().post(new RefreshChatMessageEvent());return true;}} else {//插入List<XmppUser> list1 = XmppConnection.getInstance().searchUsers(users);XmppMessage xm = new XmppMessage(to,type,new XmppUser(list1.get(0).getUserName(), list1.get(0).getName()),TimeUtil.getDate(),content,1,main);LoggerUtil.systemOut("to" + to);MessageDao.getInstance(this).inserts(this, xm);//刷新聊天页面EventBus.getDefault().post(new RefreshChatMessageEvent());return true;}}}
XmppReceiver消息广播处理消息
import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.os.Build; import android.support.v4.app.NotificationCompat;import org.greenrobot.eventbus.EventBus;import java.util.List;import cnpc.fcyt.fcydyy.R; import cnpc.fcyt.fcydyy.constant.ConfigConstants; import cnpc.fcyt.fcydyy.event.RefreshChatMessageEvent; import cnpc.fcyt.fcydyy.util.LoggerUtil; import cnpc.fcyt.fcydyy.xmpp.bean.XmppChat; import cnpc.fcyt.fcydyy.xmpp.bean.XmppMessage; import cnpc.fcyt.fcydyy.xmpp.bean.XmppUser; import cnpc.fcyt.fcydyy.xmpp.dao.MessageDao; import cnpc.fcyt.fcydyy.xmpp.util.TimeUtil;public class XmppReceiver extends BroadcastReceiver {updateActivity ua = null;public NotificationManager manager = null;Context context;public XmppReceiver(updateActivity ua) {this.ua = ua;}@TargetApi(Build.VERSION_CODES.JELLY_BEAN)@Overridepublic void onReceive(Context context, Intent intent) {this.context = context;String type = intent.getStringExtra("type");if (type.equals("chat")) {LoggerUtil.systemOut("有新的接收消息");XmppChat xc = (XmppChat) intent.getSerializableExtra("chat");if (ChatActivity.ca != null) {LoggerUtil.systemOut(ChatActivity.ca.user + "当前聊天用户");LoggerUtil.systemOut(xc.getUser() + "新信息的用户");LoggerUtil.systemOut(xc.getToo() + "当前聊天用户too");if ((ChatActivity.ca.user).equals(xc.getToo())) {ua.update(xc);}chatDatas(xc.getMain(), xc.getUser(), xc.getToo(), xc.getContent());} else {int num = chatData(xc.getMain(), xc.getUser(), xc.getToo(), xc.getContent());if (XmppService.vibrator != null && ConfigConstants.getShake(context)) {XmppService.vibrator.vibrate(500);}if (!isAppOnForeground(context)) {//在message界面更新信息if (manager == null) {manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);}Intent intent1 = new Intent(context, ChatActivity.class);intent1.putExtra("user", xc.getUser());PendingIntent pi = PendingIntent.getActivity(context, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);List<XmppUser> xmppUsers = XmppConnection.getInstance().searchUsers(xc.getUser());Notification notify = new Notification.Builder(context).setAutoCancel(true).setTicker("有新消息").setSmallIcon(R.mipmap.ic_launcher).setContentTitle("来自" + xmppUsers.get(0).getName() + "的消息").setContentText(xc.getContent()).setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE).setWhen(System.currentTimeMillis()).setNumber(num).setContentIntent(pi).build();manager.notify(0, notify);} else {if (XmppService.pool != null && ConfigConstants.getSound(context)) {XmppService.pool.play(1, 1, 1, 0, 0, 1);}}}}ua.update(type);}public interface updateActivity {public void update(String type);public void update(XmppChat xc);}public int chatData(final String main, final String users, final String to, final String content) {Cursor cursor = MessageDao.getInstance(context).queryIshasResult(context, main, "chat");if (cursor != null) {//更新int id = cursor.getInt(cursor.getColumnIndex("id"));int result = cursor.getInt(cursor.getColumnIndex("result"));MessageDao.getInstance(context).update(context, id, content, result + 1);//刷新聊天页面EventBus.getDefault().post(new RefreshChatMessageEvent());return (result + 1);} else {//插入List<XmppUser> list1 = XmppConnection.getInstance().searchUsers(users);XmppMessage xm = new XmppMessage(to,"chat",new XmppUser(list1.get(0).getUserName(), list1.get(0).getName()),TimeUtil.getDate(),content,1,main);LoggerUtil.systemOut("to3" + to);MessageDao.getInstance(context).inserts(context, xm);//刷新聊天页面EventBus.getDefault().post(new RefreshChatMessageEvent());return 1;}}public void chatDatas(final String main, final String users, final String to, final String content) {int id = MessageDao.getInstance(context).queryIshas(context, main, "chat");if (id != -1) {//更新MessageDao.getInstance(context).update(context, id, content, 0);} else {//插入List<XmppUser> list1 = XmppConnection.getInstance().searchUsers(users);XmppMessage xm = new XmppMessage(to,"chat",new XmppUser(list1.get(0).getUserName(), list1.get(0).getName()),TimeUtil.getDate(),content,0,main);LoggerUtil.systemOut("to1" + to);MessageDao.getInstance(context).inserts(context, xm);}//刷新聊天页面EventBus.getDefault().post(new RefreshChatMessageEvent());}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;}}
本地数据库建立,用于储存历史消息记录等
import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase;import java.util.ArrayList;import cnpc.fcyt.fcydyy.util.LoggerUtil; import cnpc.fcyt.fcydyy.xmpp.bean.XmppChat;public class FriendChatDao {private static FriendChatDao mInstance;public Context context;private FriendChatDao(Context ctx) {this.context = ctx;}public static FriendChatDao getInstance(Context ctx) {//懒汉: 考虑线程安全问题, 两种方式: 1. 给方法加同步锁 synchronized, 效率低; 2. 给创建对象的代码块加同步锁//读数据不会出现线程安全问题, 写数据会出现线程安全问题//a, B, Cif (mInstance == null) {//B, Csynchronized (FriendChatDao.class) {//aif (mInstance == null) {mInstance = new FriendChatDao(ctx);}}}return mInstance;}public boolean insert(Context context, XmppChat xc) {boolean isSucceed = false;// 1. 在内存中创建数据库帮助类的对象FriendChatOpenHelper helper = new FriendChatOpenHelper(context);// 2. 在磁盘上创建数据库文件SQLiteDatabase db = helper.getWritableDatabase();/*** table :表名* nullColumnHack:*/ContentValues values = new ContentValues();values.put("main", xc.getMain());values.put("user", xc.getUser());values.put("nickname", xc.getNickname());values.put("icon", xc.getIcon());values.put("type", xc.getType());values.put("content", xc.getContent());values.put("too", xc.getToo());values.put("viewtype", xc.getViewType());values.put("time", xc.getTime());long id = db.insert("chat", null, values);if (id == -1) {LoggerUtil.systemOut("插入失败");isSucceed = false;} else {LoggerUtil.systemOut("插入成功");isSucceed = true;}// 释放资源 db.close();return isSucceed;}public ArrayList<XmppChat> query(Context context, String main, String too) {// 1. 在内存中创建数据库帮助类的对象FriendChatOpenHelper helper = new FriendChatOpenHelper(context);// 2. 在磁盘上创建数据库文件SQLiteDatabase database = helper.getWritableDatabase();Cursor cursor = database.query("chat", null, "too=?", new String[]{too}, null, null, null);ArrayList<XmppChat> list = new ArrayList<>();if (cursor != null) {LoggerUtil.systemOut("查询个数 " + cursor.getCount());while (cursor.moveToNext()) {String user = cursor.getString(cursor.getColumnIndex("user"));String nickname = cursor.getString(cursor.getColumnIndex("nickname"));String icon = cursor.getString(cursor.getColumnIndex("icon"));int type = cursor.getInt(cursor.getColumnIndex("type"));String content = cursor.getString(cursor.getColumnIndex("content"));String times = cursor.getString(cursor.getColumnIndex("time"));long time = Long.parseLong(times);int viewType = cursor.getInt(cursor.getColumnIndex("viewtype"));XmppChat xmppChat = new XmppChat(main, user, nickname, icon, type, content, too, viewType, time);list.add(xmppChat);}cursor.close();}database.close();return list;}public boolean delete(Context context, String too) {boolean isDelete = false;// 1. 在内存中创建数据库帮助类的对象FriendChatOpenHelper helper = new FriendChatOpenHelper(context);// 2. 在磁盘上创建数据库文件SQLiteDatabase database = helper.getWritableDatabase();int d = database.delete("chat", "too = ?", new String[]{too + ""});if (d == 0) {LoggerUtil.systemOut("删除失败");isDelete = false;} else {LoggerUtil.systemOut("删除失败" + too);isDelete = true;}// 释放资源 database.close();return isDelete;}}
import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class FriendChatOpenHelper extends SQLiteOpenHelper {private Context context;public FriendChatOpenHelper(Context context) {super(context, "XMPPChat.db",null,1);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("create table chat(id integer primary key autoincrement,main text,user text,nickname text,icon text,type integer,content text,too text,viewtype integer,time text)");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} }
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 cnpc.fcyt.fcydyy.util.LoggerUtil; import cnpc.fcyt.fcydyy.xmpp.bean.XmppUser; import cnpc.fcyt.fcydyy.xmpp.bean.XmppMessage; import cnpc.fcyt.fcydyy.xmpp.util.TimeUtil;public class MessageDao {public Context context;private static MessageDao mInstance;private MessageDao(Context ctx) {this.context = ctx;}public static MessageDao getInstance(Context ctx) {//懒汉: 考虑线程安全问题, 两种方式: 1. 给方法加同步锁 synchronized, 效率低; 2. 给创建对象的代码块加同步锁//读数据不会出现线程安全问题, 写数据会出现线程安全问题//a, B, Cif (mInstance == null) {//B, Csynchronized (MessageDao.class) {//aif (mInstance == null) {mInstance = new MessageDao(ctx);}}}return mInstance;}public List<XmppMessage> queryMessage(Context context,String main ) {// 1. 在内存中创建数据库帮助类的对象MessageOpenHelper helper = new MessageOpenHelper(context);// 2. 在磁盘上创建数据库文件SQLiteDatabase database = helper.getReadableDatabase();Cursor cursor = database.query("message", null, "main = ?", new String[]{main}, null, null, null);ArrayList<XmppMessage> list = new ArrayList<>();while (cursor.moveToNext()) {String type = cursor.getString(cursor.getColumnIndex("type"));int id = cursor.getInt(cursor.getColumnIndex("id"));String to = cursor.getString(cursor.getColumnIndex("too"));String username = cursor.getString(cursor.getColumnIndex("username"));String name = cursor.getString(cursor.getColumnIndex("name"));XmppUser user = new XmppUser(username, name);String time = cursor.getString(cursor.getColumnIndex("time"));String content = cursor.getString(cursor.getColumnIndex("content"));int result = cursor.getInt(cursor.getColumnIndex("result"));XmppMessage xm = new XmppMessage(id, to, type, user, time, content, result, main);list.add(xm);}return list;}public int queryIshas(Context context, String main, String type) {// 1. 在内存中创建数据库帮助类的对象MessageOpenHelper helper = new MessageOpenHelper(context);// 2. 在磁盘上创建数据库文件SQLiteDatabase database = helper.getReadableDatabase();Cursor cursor = database.query("message", null, "main=? and type=?", new String[]{main, type}, null, null, null);if (cursor != null) {if (!cursor.moveToFirst()) {//插入LoggerUtil.systemOut("没有查询到");return -1;} else {//更新LoggerUtil.systemOut("查询到了");int id = cursor.getInt(cursor.getColumnIndex("id"));return id;}} else {LoggerUtil.systemOut("cursor为空");return -1;}}public int queryhasMsg(Context context, String too, String type) {// 1. 在内存中创建数据库帮助类的对象MessageOpenHelper helper = new MessageOpenHelper(context);// 2. 在磁盘上创建数据库文件SQLiteDatabase database = helper.getReadableDatabase();Cursor cursor = database.query("message", null, "too=? and type=?", new String[]{too, type}, null, null, null);if (cursor != null) {if (!cursor.moveToFirst()) {//插入LoggerUtil.systemOut("没有查询到");return -1;} else {//更新LoggerUtil.systemOut("查询到了");int id = cursor.getInt(cursor.getColumnIndex("id"));return id;}} else {LoggerUtil.systemOut("cursor为空");return -1;}}public Cursor queryIshasResult(Context context, String main, String type) {// 1. 在内存中创建数据库帮助类的对象MessageOpenHelper helper = new MessageOpenHelper(context);// 2. 在磁盘上创建数据库文件SQLiteDatabase database = helper.getReadableDatabase();Cursor cursor = database.query("message", null, "main=? and type=?", new String[]{main, type}, null, null, null);if (cursor != null) {if (!cursor.moveToFirst()) {//插入LoggerUtil.systemOut("没有查询到");return null;} else {//更新LoggerUtil.systemOut("查询到了");int result = cursor.getInt(cursor.getColumnIndex("result"));return cursor;}} else {LoggerUtil.systemOut("cursor为空");return null;}}public boolean inserts(Context context, XmppMessage xm) {boolean isSucceed = false;// 1. 在内存中创建数据库帮助类的对象MessageOpenHelper helper = new MessageOpenHelper(context);// 2. 在磁盘上创建数据库文件SQLiteDatabase database = helper.getWritableDatabase();/*** table :表名* nullColumnHack:*/ContentValues values = new ContentValues();values.put("main", xm.getMain());values.put("name", xm.getUser().getName());values.put("username", xm.getUser().getUserName());values.put("too", xm.getTo());values.put("type", xm.getType());values.put("content", xm.getContent());values.put("time", xm.getTime());values.put("result", xm.getResult());long id = database.insert("message", null, values);if (id == -1) {LoggerUtil.systemOut("插入失败");isSucceed = false;} else {LoggerUtil.systemOut("插入成功");isSucceed = true;}// 释放资源 database.close();return isSucceed;}public boolean update(Context context, int id, String content, int result) {// 1. 在内存中创建数据库帮助类的对象MessageOpenHelper helper = new MessageOpenHelper(context);// 2. 在磁盘上创建数据库文件SQLiteDatabase database = helper.getWritableDatabase();/*** table :表名* nullColumnHack:*/ContentValues values = new ContentValues();values.put("content", content);values.put("time", TimeUtil.getDate());values.put("result", result);//返回更新的行数int update = database.update("message", values, "id=?", new String[]{id + ""});database.close();return update > 0;}public boolean updateResult(Context context, String too,String type ,String content) {// 1. 在内存中创建数据库帮助类的对象MessageOpenHelper helper = new MessageOpenHelper(context);// 2. 在磁盘上创建数据库文件SQLiteDatabase database = helper.getWritableDatabase();/*** table :表名* nullColumnHack:*/ContentValues values = new ContentValues();values.put("result", 0);if (!content.isEmpty()){values.put("content", content);}values.put("time", TimeUtil.getDate());//返回更新的行数int update = database.update("message", values, "too=? and type = ? ", new String[]{too,type});database.close();return update > 0;}public boolean delete(Context context, int id) {boolean isDelete = false;// 1. 在内存中创建数据库帮助类的对象MessageOpenHelper helper = new MessageOpenHelper(context);// 2. 在磁盘上创建数据库文件SQLiteDatabase database = helper.getWritableDatabase();int d = database.delete("message", "id = ?", new String[]{id + ""});if (d == 0) {LoggerUtil.systemOut("删除失败");isDelete = false;} else {LoggerUtil.systemOut("删除失败" + id);isDelete = true;}// 释放资源 database.close();return isDelete;} }
import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper;public class MessageOpenHelper extends SQLiteOpenHelper {private Context context;public MessageOpenHelper(Context context) {super(context, "XMPPMessage.db",null,1);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("create table message(id integer primary key autoincrement,main text,too text,name varchar(20),username text,type text,content text,time text,result integer)");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} }
6.开发过程中,注意注册的用户名和聊天等相关JID的区别,聊天发送的JID需要后缀标识,建群同理,无法与注册用户的JID对应,这点比较无语....
7.项目demo地址下载:https://download.csdn.net/download/jcf0706/10787309
欢迎大家下载学习,对于上面6有啥的好的解决办法,欢迎大家评论或者发邮箱给我建议jcf0706@163.com,一起学习进步!!!
转载于:https://www.cnblogs.com/loaderman/p/9967605.html
用xmmp+openfire+smack搭建简易IM实现相关推荐
- Android之基于xmpp openfire smack开发之Android消息推送技术原理分析和实践[4]
http://blog.csdn.net/shimiso/article/details/8156439 前面几篇给大家系统讲解的有关xmpp openfire smack asmack相关的技术和使 ...
- 搭建简易Linux局网服务器
搭建简易Linux局网服务器 该文章转自 联信软件 作为桌面操作系统,Linux的人机界面可真是不敢恭维,但是,作为网络操作系统,其易用性(对于NOS而言)和高性能恐怕是很难有能出其右的.当然,这并不 ...
- Django搭建简易博客
Django简易博客,主要实现了以下功能 连接数据库 创建超级用户与后台管理 利用django-admin-bootstrap美化界面 template,view与动态URL 多说评论功能 Markd ...
- CDH5.15离线搭建简易版集群(完整版)
运用CDH5.15离线搭建简易版集群 关于CDH和Cloudera CDH(Cloudera的发行版,包括Apache Hadoop),是Hadoop众多分支中的一种,由Cloudera维护,基于稳定 ...
- 如何搭建简易又安全的企业内部文件服务器?
提到搭建企业内部文件服务器,很多人的第一反应是Samba文件服务器. 确实,在过去的很多年里,大部分企业都是通过Windows Server的域控制器使企业内部员工方便地进行资源共享和使用网络打印机. ...
- Linux Centos7 搭建简易堡垒机安装jailkit实现chroot
Linux Centos7 搭建简易堡垒机安装jailkit实现chroot 一.什么是堡垒机 堡垒机,即在一个特定的网络环境下,为了保障网络和数据不受来自外部和内部用户的入侵和破坏,而运用各种技术手 ...
- 用 kali 工具 apache 搭建简易网站 LAMP
搭建简易网站 内网和外网的原理图示: 搭建步骤为LAMP L为linux,A为Apache,M代表Mysql,P代表php ~ 打开apache服务 service apache2 start # 打 ...
- iverilog搭建简易仿真平台
iverilog搭建简易仿真平台 对于xsim和modelsim这种仿真测试平台,对操作系统要求过于严格,为了实现远程verilog编译仿真调试,我选择了linux+iverilog+gtkwave来 ...
- 搭建简易的物联网服务端和客户端-Maibu控制(二十一)
创建麦布应用程序,麦步按键控制.原理和网页控制差不多,就是麦步访问之前创建的两个buttonclick接口.感谢qs100371大神. 代码地址:https://github.com/ZZES-ZVD ...
最新文章
- 解决AttributeError: module ‘tensorflow_core._api.v2.config‘ has no attribute ‘experimental_list_device
- 每天多一点(2016.12.04)》Javascript隐式转换
- websocket导致spring boot 项目单元测试启动失败的问题解决
- pytorch图像分类_使用PyTorch和Streamlit创建图像分类Web应用
- 洛谷 P4016 负载平衡问题 【最小费用最大流】
- iOS coredata 避免添加重复数据
- from torchcrf import CRF
- mysql中怎么在列中使用时间函数_mysql中关于时间的函数使用教程
- PowerDesigner 表名、字段大小写转换
- 自下而上分析法基本问题
- Android JetPack –导航架构
- Swagger 生成 PHP API 接口文档
- 记一次西安thoughtworks的面试经历
- 毕业论文页码及目录设置方法
- 利用jsPDF把图片转成pdf格式保存本地指定目录
- 福特汉姆大学计算机科学专业,福特汉姆大学计算机科学排名第131(2018年TFE美国排名)...
- 解决导出excel导出名字乱码
- Equalize Prices
- 厦门商业贷款转公积金攻略
- 欧美IT外包的几种业务模式
热门文章
- Windows7笔记本正版COA标签辨别
- 梦境画成现实,Meta最新AI图像生成工具
- SQL UCASE() 函数、SQL LCASE() 函数、SQL MID() 函数
- 年终收藏! 一文看尽2020年度最「出圈」AI论文合集
- 用php模拟斗地主发牌,网络编程php模拟实现斗地主发牌
- 【小月电子】安路国产FPGA开发板系统学习教程-LESSON1点亮LED灯
- Shopee物流信息出现问题要怎么查询,你知道吗?
- 日立HDS VM存储更换硬盘
- APT组织Reaper新Dropper公开:NOKKI和DOGCALL存在关联性
- 在Shell或Bat脚本中激活conda环境