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) } } }
⑥未完成
接下来应该是判断双向好友关系和显示好友列表,之前写过了,主要是做页面比较麻烦