Smack登录,注册,搜索用户,添加双向好友总结

一做界面代码就会显得冗余。所以关于界面布局的代码就不写了。总结一下自己在实现samck登录,注册,添加好友,搜索好友,显示好友列表的时候遇到的一些问题和解决办法吧:

①创建用户添加除账户密码外的其他属性

这里可以使用void createAccount​(Localpart username, String password, Map<String,​String> attributes)创建用户,attributes表示除了账户和密码之外的字段属性

查看ofuser表可以查看,用户数据所有字段:

 

 openfire也可以自定义用户表,这个暂时还没有用到

②关于搜索好友时获得连接

 实例化UserSearchManager的时候,要传入一个conn~

var userSearchManager:UserSearchManager=UserSearchManager(conn)

这个连接conn是一个需要已经登录的连接,而在这里不可能再去登录一次了。所以是需要获得之前在登录界面登录了的连接。所以不能再去实例化一个ConnectionManage类,而使用单例模式,同时为ConnectionManage增加一个getConnection方法:

class ConnectionManage private constructor(){
    private var conn:XMPPTCPConnection?=null
    //单例模式
    companion object{
        val smackConnection by lazy{
            ConnectionManage()
        }
    }
    fun conn(): XMPPTCPConnection {
        val addr: InetAddress = InetAddress.getByName("169.254.235.122")
        val config: XMPPTCPConnectionConfiguration = XMPPTCPConnectionConfiguration.builder()
//            .setUsernameAndPassword(username,password)
            .setXmppDomain("169.254.235.122")
            .setHost("169.254.235.122")
            .setHostAddress(addr)
            .setPort(5222)
            .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
//            .setSendPresence(true)
//            .setDebuggerEnabled(true)
            .build()
            conn= XMPPTCPConnection(config)
//        conn.replyTimeout=5000
        return conn!!
    }
    fun getConnection():XMPPTCPConnection?{
        if(conn!=null){
            return conn
        }else{
            return null
        }
    }
...

再次获得连接的时候可以使用ConnectionManage.smackConnection.getConnection()

③搜索好友代码

fun search(){
        var searchText=search_text.text.toString()
        var userSearchManager:UserSearchManager=UserSearchManager(conn)
        if(conn!=null){
            var searchService="search."+conn!!.xmppServiceDomain
            println("searchService值为${JidCreate.domainBareFrom(searchService)}")
            var searchFrom=userSearchManager.getSearchForm(JidCreate.domainBareFrom(searchService))
            //设置搜索条件
            var answerForm=searchFrom.createAnswerForm()
            //设置根据用户名查询
            answerForm.setAnswer("Username",true)
            answerForm.setAnswer("search",searchText.trim())

            //获得查询结果
            var result:ReportedData=userSearchManager.getSearchResults(answerForm,JidCreate.domainBareFrom(searchService))
            //获得查询结果的行
            var rows:List<ReportedData.Row> =result.rows
            for(row in rows){
                var user=row.getValues("Username").get(0)
                println("查询到的结果$user")
            }
            //添加搜索结果显示的listView或其他控件
        }else{
            Toast.makeText(this,"尚未登录,显示错误",Toast.LENGTH_SHORT).show()
        }

    }

 运行:

 

 

 ④添加好友

点击搜索结果条目的监听:

search_list_view.setOnItemClickListener(object: AdapterView.OnItemClickListener{
            override fun onItemClick(
                parent: AdapterView<*>?,
                view: View?,
                position: Int,
                id: Long
            ) {
                //Toast.makeText(applicationContext, searchList[position], Toast.LENGTH_SHORT).show()
                //如果是双向好友点击则进入聊天,不是双向好友提示添加好友
                //这里先不做判断,点击直接提示添加好友
                var builder:AlertDialog.Builder=AlertDialog.Builder(this@SearchUserActivity)
                builder.setTitle("添加好友")
                builder.setMessage("确认添加${searchList[position]}为好友吗?")
                builder.setPositiveButton("确定",object:DialogInterface.OnClickListener{
                    override fun onClick(dialog: DialogInterface?, which: Int) {
                        var addToJid:String= XmppStringUtils.completeJidFrom(searchList[position],"169.254.235.122")
                        var p=Presence(Presence.Type.subscribe)
                        p.to = JidCreate.from(addToJid)
                        conn!!.sendStanza(p)
                    }
                })
                builder.setNegativeButton("取消",object:DialogInterface.OnClickListener{
                    override fun onClick(dialog: DialogInterface?, which: Int) {

                    }

                })
                builder.create().show()

            }

        })

在好友页面(FriendFragment)里面监听:

conn!!.addSyncStanzaListener(object:StanzaListener{
override fun processStanza(packet: Stanza?) {
if(packet is Presence){
var presence:Presence=packet
if(presence.type==Presence.Type.subscribe){
println("收到来自${presence.from}的好友申请消息")
//判断是否已经添加
if(!rosterManager!!.isSubscribed(presence.from)){
//弹出对话框,发送Handler消息
var message=handle.obtainMessage(1,presence.from)
handle.sendMessage(message)
}

}else if(presence.type==Presence.Type.available){
println("收到${presence.from}上线消息")
}else if(presence.type==Presence.Type.unavailable){
println("收到${presence.from}下线消息")
}else if(presence.type==Presence.Type.subscribed){
println("${presence.from}同意了你的好友申请")
}
}
}

},object:StanzaFilter{
override fun accept(stanza: Stanza?): Boolean {
return true
}

})

Handler设置

private var handle: Handler =object:Handler(){
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when(msg.what){
                //弹出提示框
                1->alterInvestDialog(msg.obj.toString())
            }
        }
    }
    private fun alterInvestDialog(str:String){
        var builder: AlertDialog.Builder= AlertDialog.Builder(activity)
        builder.setTitle("好友申请")
        builder.setMessage("${str}请求添加为好友,是否同意?")
        builder.setPositiveButton("添加",object: DialogInterface.OnClickListener{
            override fun onClick(dialog: DialogInterface?, which: Int) {
                //
            }
        })
        builder.setNegativeButton("拒绝",object: DialogInterface.OnClickListener{
            override fun onClick(dialog: DialogInterface?, which: Int) {

            }

        })
        builder.create().show()
    }

 

运行

 

 

 一个bug

在一个Fragment(FriendFragment)的onActivityCreate声明周期函数中,对连接进行监听,如果收到好友申请就调用AlertDialog.Builder(activity)弹出对话框。当你不在该Fragment,而切换到其他的Fragment中的时候,再调用AlertDialog.Builder(activity),这个activity就是null,程序会异常退出。

因为FriendFragment的activity一旦被创建了,在隐藏状态(切换到其他的Fragment),也能监听到好友申请。

但是正常的程序不应该是收到好友申请之后,不管当前在哪个页面都在当前页面弹出一个对话框。我看一些资料,收到好友申请的时候可以发送一个广播。所以这个bug也就暂时不处理了

 ⑤同意添加好友

关于测试的时候清除缓存

每次测试添加好友的时候,可以清除一下缓存,清空一下数据库。不然可能没有效果

清除openfire服务器缓存:

服务器管理器-缓存摘要-找到Roster勾选-清除已选项目

 

 

 

 清空数据库表ofroster:

用户关系保存在ofroster表中,可以使用清空数据库表的指令,比如:

truncate table ofroster;

 

双向好友的建立

openfire添加好友是这样的,A订阅B,B订阅A(换种说法A给B发送一条好友申请,B受到后,再给A发送一条好友申请);在B收到A的订阅消息之后,再给A发送一条Type.subscribed消息;A接收到B发来的Type.subscribed消息之后,再给A发送一条Type.subscribed消息。这样双向好友才成立。

可以创建一个RosterManage类,使用roster.createEntry方法添加好友;不过直接发送类型Type.subscribe消息也是可以的吧:

class RosterManager(conn:XMPPTCPConnection) {
private var conn:XMPPTCPConnection?=null
private var roster:Roster?=null
init{
this.conn=conn
roster= Roster.getInstanceFor(conn)
}

fun addFriend(username:String){
roster!!.createEntry(JidCreate.bareFrom(username),"", arrayOf("我的好友"))
//发送已添加消息
var p:Presence= Presence(Presence.Type.subscribed)
//var jdiString=XmppStringUtils.completeJidFrom(username,"169.254.235.122")
p.to=JidCreate.bareFrom(username)
conn!!.sendStanza(p)
}

//判断是否已订阅
fun isSubscribed(username: Jid):Boolean{
var entry:RosterEntry?=null
entry=roster!!.getEntry(JidCreate.bareFrom(username))
return entry!=null
}
}

监听的时候可以添加:

if(presence.type==Presence.Type.subscribed){
                        println("${presence.from}同意了你的好友申请")
                    }

运行:

关于ofroster表中的sub字段值问题

这样一来,ofroster数据库显示的记录为:

 相当于是subType为2,而双方互为好友,sub字段值应该为3。

出错的原因是,赵四收到张三发送过来的subscribed消息(通知“已订阅”)之后,没有给张三也回复一条subscribed消息。正确的做法应该是:

conn!!.addSyncStanzaListener(object:StanzaListener{
            override fun processStanza(packet: Stanza?) {
                if(packet is Presence){
                    var presence:Presence=packet
                    if(presence.type==Presence.Type.subscribe){
                        //有好友申请
                    }else if(presence.type==Presence.Type.subscribed){
                        println("${presence.from}同意了你的好友申请")
              // var p
=Presence(Presence.Type.subscribed) p.to=presence.from conn!!.sendStanza(p) } } }

⑥未完成

接下来应该是判断双向好友关系和显示好友列表,之前写过了,主要是做页面比较麻烦

⑦一些参考

openfire好友关系解析

 Openfire+smack打造IM即时通讯【动脑学院】(第八、九、十节)

posted @ 2021-01-06 04:27  vocus  阅读(503)  评论(0编辑  收藏  举报