smack+openfire实现Android实时通信(实现大部分基本功能)

1. 开发环境

  • smack-4.3.4
  • openfire-4.6.3
  • 开发工具:AndroidStudio

openfire下载及安装:

本文项目地址:https://blog.csdn.net/JieZhongBa/article/details/118460210

2. 依赖

//  UI 可选
    implementation 'com.github.stfalcon-studio:Chatkit:0.4.1'
//  Smack
    implementation "org.igniterealtime.smack:smack-tcp:4.3.4"
//  下面两个二选一,带extensions的包大一点,顾名思义。
//  implementation "org.igniterealtime.smack:smack-android:4.3.4"
	implementation "org.igniterealtime.smack:smack-android-extensions:4.3.4"

其中UI是github的一个开源框架,用起来还行,有兴趣的可以试试,就是文档比较少,官方是纯英文的。
官方地址:https://github.com/stfalcon-studio/ChatKit
教程博客:https://blog.csdn.net/JieZhongBa/article/details/118459924

3. 连接服务

Config,供全局使用,注意服务器名称和主机名不要混淆了,其实就是初始化时的域名,避免错误就直接设成一样的就好了。

//Config
public static final String C_HOST = "IP";//可访问到openfire服务的IP地址
public static final int C_PORT = 5222;//端口,默认5222
public static final String C_DOMAIN = "up55";//服务器名称
public static final String C_SOURCE = "YunShang";//源,自己随便写,也可以不加这个

连接服务器

try {
    System.out.println("正在连接Chat服务。。。");
    XMPPTCPConnectionConfiguration configuration = XMPPTCPConnectionConfiguration.builder()
            .setResource(Resourcepart.from(C_SOURCE))//固定源
            .setHostAddress(InetAddress.getByName(C_HOST))//IP
            .setPort(C_PORT)//端口
            .setXmppDomain(C_DOMAIN)//服务器名称
            .setSendPresence(false)//以离线方式登录,以便获取离线消息
            .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
            .build();
    connection = new XMPPTCPConnection(configuration);
    connection.connect();
    System.out.println("Chat服务连接成功");
} catch (Exception e) {
    System.out.println("Chat服务连接失败" + e.getLocalizedMessage());
    e.printStackTrace();
}

4. 登录

connection.login(user, pass);

5. 登出

connection.sendStanza(new Presence(Presence.Type.unavailable));//设为下线
connection.disconnect();//断开连接

6. 注册

其中map里的信息是选填,key对应openfire的用户属性,account和password为必填项。

HashMap<String, String> map = new HashMap<>();
map.put("name", "张三");//可选
map.put("email", "zs@csdn.com");//可选
AccountManager instance = AccountManager.getInstance(connection);
instance.sensitiveOperationOverInsecureConnection(true);
instance.createAccount(Localpart.from(account), password, map);//map为可选

7. 注销(慎用)

删除当前用户的所有信息,慎用!

AccountManager.getInstance(connection).deleteAccount();

8. 搜索用户

之前网上找的,注释也比较多,原文不知道在哪了

try {
	UserSearchManager usm = new UserSearchManager(connection);
	 //本例用的smack:4.3.4版本,getSearchForm方法传的是DomainBareJid类型,而之前的版本是String类型,大家在使用的时候需要特别注意
	 //而转换DomainBareJid的方式如下面的例子所示:JidCreate.domainBareFrom("search." + getConnection().getXMPPServiceDomain())
	 Form searchForm = usm.getSearchForm(JidCreate.domainBareFrom("search." + connection.getXMPPServiceDomain()));
	 if (searchForm == null) {
	     return;
	 }
	 //这里设置了Username为true代码是根据用户名查询用户,search代表查询字段
	 //smack:4.3.4版本是下面的字段,但之前的版本会有些不一样,所以在用的时候最好看下xmpp交互的log,里面有相应的字段值
	 Form answerForm = searchForm.createAnswerForm();
	 answerForm.setAnswer("Username", true);
	 answerForm.setAnswer("Name", true);
	 answerForm.setAnswer("search", "张三");
	 ReportedData data = usm.getSearchResults(answerForm, JidCreate.domainBareFrom("search." + connection.getXMPPServiceDomain()));
	 List<ReportedData.Row> rowList = data.getRows();
	
	 //此处返回的字段名如下所示,之前的版本可能有所变化,使用的时候需要注意
	 for (ReportedData.Row row : rowList) {
	     String jid = row.getValues("jid").toString();
	     String username = row.getValues("Username").toString();
	     String name = row.getValues("Name").toString();
	     String email = row.getValues("Email").toString();
	     System.out.println(jid + "," + username + "," + name + "," + email);
	     // 若存在,则有返回,UserName一定非空,其他两个若是有设,一定非空
	 }
} catch (Exception e) {
	 e.printStackTrace();
}

9. 连接状态

主要是供外部调用,获取连接状态。

//是否连接服务器
public boolean isConnected() {
    return connection.isConnected();
}

//是否登录
public boolean isLogin() {
    return connection.isAuthenticated();
}

10. 接收离线消息

try {
    OfflineMessageManager offlineMessageManager = new OfflineMessageManager(connection);
    List<Message> messageList = offlineMessageManager.getMessages();
    System.out.println("离线消息:" + messageList.size()+"条");
    for (Message message : messageList) {
        System.out.println(message.getFrom() + ",," + message + ":" + message.getBody());
    }
    offlineMessageManager.deleteMessages();//通知服务器删除离线消息,否则下次还在
    connection.sendStanza(new Presence(Presence.Type.available));//上线
} catch (Exception e) {
    e.printStackTrace();
}

11. 发送消息

为了方便其实是用idea写的,Android的话测试比较麻烦,后面会吧我写的Android项目发出来。
简单的从控制台输入。

ChatManager chatManager = ChatManager.getInstanceFor(connection);
try {
    EntityBareJid jid = JidCreate.entityBareFrom("540@up55");//对方的jid(账号+@+服务器名称)
    Chat chat = chatManager.chatWith(jid);
    Scanner sc = new Scanner(System.in);
    if (chat != null) {
        while (true) {
            String mes = sc.next();
            chat.send(mes);
            if ("exit".equals(mes))
                break;
        }
    }
} catch (Exception e) {
    e.printStackTrace();
}

12. 消息收发监听

ChatManager chatManager = ChatManager.getInstanceFor(connection);
//消息发送监听
chatManager.addOutgoingListener((entityBareJid, message, chat1) -> {
    System.out.println("发送成功:" + message.getBody());
});
//接收消息监听
chatManager.addIncomingListener((entityBareJid, message, chat) -> {
    System.out.println(message.getFrom().getLocalpartOrThrow() + ":" + message.getBody());
});

13. Android实现的思路

我是用一个service保持和服务器的通信,然后自定义了回调接口,在activity中实现,service监听到消息则调用回调,activity刷新页面,同时service提供了发送消息功能,具体实现当然会比这个复杂点,而且要规范也不能这样写,有兴趣可以去看看我的项目,不过写的比较乱,写完也觉得很多地方写的不太好,比如应该继承AppCompatActivity写一个自己的,方便activity和service的通信和即时通讯功能的管理,另外也不一定要用回调的形式,也可以用广播之类的实现消息传递。

14. 踩坑记录

  • 连局域网的时候不加端口号也行,因为默认5222,后来部署到远程,一直连接超时,用idea测试也没问题(不知道为什么idea不加端口号也行),Android里一直不行,后来加上端口号就好了。。。
  • smack版本变化不少,而且网上教程比较少,很多是老版本的,只能稍微参考,得找相近版本的。
  • openfire的搜索服务有时候会出毛病,把服务器缓存清掉就好了,再不行重启服务器,千万不要在插件里重新启动,会出问题,之后就只能删掉这个插件在重新安装了。
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值