android asmack 注册 登陆 聊天 多人聊天室 文件传输
2014-03-31 14:20
465 查看
XMPP协议简介
XMPP协议(ExtensibleMessagingandPresenceProtocol,可扩展消息处理现场协议)是一种基于XML的协议,目的是为了解决及时通信标准而提出来的,最早是在Jabber上实现的。它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。并且XML很易穿过防火墙,所以用XMPP构建的应用不易受到防火墙的阻碍。利用XMPP作为通用的传输机制,不同组织内的不同应用都可以进行有效的通信。这篇文章有基本的介绍,
IM
InstantMessenger,及时通信软件,就是大家使用的QQ、MSNMessenger和Gtalk等等。其中Gtalk就是基于XMPP协议的一个实现,其他的则不是。当前IM几乎作为每个上网者必然使用的工具,在国外的大型企业中有一些企业级的IM应用,但是其商业价值还没完全发挥出来。设想既然XMPP协议是一个公开的协议,那么每个企业都可以利用它来开发适合本身企业工作,提高自身生产效率的IM;甚至,你还可以在网络游戏中集成这种通信软件,不但让你可以边游戏边聊天,也可以开发出适合游戏本身的IM应用,比如说一些游戏关键场景提醒功能,团队语音交流等等都可以基于IM来实现。本文主要讲解在android使用xmpp协议进行即时通信,所涉及3个主要的东西,它们是openfire、smack和spark,这个三个东东结合起来就是完整的xmppIM实现,这里简单介绍一下这3个东东在下文的作用:
openfire主要是作为服务器,负责管理客户端的通信连接,以及提供客户端一些通信信息和连接信息。
Smack主要是xmpp协议的实现,提供了一套很好的api,所以下面操作xmpp都是通过使用smack的api来实现,当然因为是在android里,所以使用的是asmack这个包,里面方法跟smack包差不多。
Spark是IM客户端的实现,其实就是使用了smack的api实现的。
下图展示了三者之间的关系:(很明显这个图是偷别人的,具体是哪里我忘了,因为资料都是复制到文档后慢慢研究看的)
从图上可以了解到,client端和server端都可以通过插件的方式来进行扩展,smack是二者传递数据的媒介。
配置openfire服务器
具体步骤请移步:配置成功如果以后ip地址变了,那肯定又是开不了,解决办法请移步:
配置成功后,在服务器创建一个简单的用户来测试,然后安装spark,设置好服务器的ip与端口,使用刚才创建的用户登录,登录OK说明服务器成功搭建。
AndroidIM功能(因为是测试demo,因此界面超级简陋,代码都是给出重要的一部分,剩余的可以在最后下面项目查看)
配置要求
android2.2、asmack-jse.jar、myeclipse
连接服务器
在打开软件后会开始初始化,完成与openfire服务器的连接,设置一些配置
static{ XMPPConnection.DEBUG_ENABLED=true; finalConnectionConfigurationconnectionConfig=newConnectionConfiguration( host,5222,""); //Googletalk //ConnectionConfigurationconnectionConfig=new //ConnectionConfiguration( //"talk.google.com",5222,"gmail.com");
//connectionConfig.setSASLAuthenticationEnabled(false); ActivityMain.connection=newXMPPConnection(connectionConfig); ActivityMain.connection.DEBUG_ENABLED=true; ProviderManagerpm=ProviderManager.getInstance(); configure(pm); }
注册模块
注册有两种方法:一种是用createAccount,不过我测试了一下发现不能创建用户,具体原因不详,下面介绍第二种。如上图:注册成功后服务器将多了ggg用户。
具体实现如下:
Registrationreg=newRegistration(); reg.setType(IQ.Type.SET); reg.setTo(ConnectionSingleton.getInstance().getServiceName()); reg.setUsername(username.getText().toString()); reg.setPassword(password.getText().toString()); reg.addAttribute("android","geolo_createUser_android"); System.out.println("reg:"+reg); PacketFilterfilter=newAndFilter(newPacketIDFilter(reg .getPacketID()),newPacketTypeFilter(IQ.class)); PacketCollectorcollector=ConnectionSingleton.getInstance() .createPacketCollector(filter); ConnectionSingleton.getInstance().sendPacket(reg); result=(IQ)collector.nextResult(SmackConfiguration .getPacketReplyTimeout()); //Stopqueuingresults collector.cancel(); if(result==null){ Toast.makeText(getApplicationContext(),"服, Toast.LENGTH_SHORT).show(); }elseif(result.getType()==IQ.Type.ERROR){ if(result.getError().toString().equalsIgnoreCase( "conflict(409)")){ Toast.makeText(getApplicationContext(),"这, Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(getApplicationContext(),"注, Toast.LENGTH_SHORT).show(); } }elseif(result.getType()==IQ.Type.RESULT){ Toast.makeText(getApplicationContext(),"恭, Toast.LENGTH_SHORT).show(); }
使用注册类,设置好注册的用户名密码和一些属性字段,直接设置包过滤,根据这个过滤创建一个结果集合,发送注册信息包,等待获取结果,剩余就是判断结果内容.
登录模块
登录比较简单ConnectionSingleton.getInstance().connect();//connect Stringaccount=etUsername.getText().toString(); Stringpassword=etPassword.getText().toString(); //保存用户和密码
ActivityLogin.util.saveString(ACCOUNT_KEY,account); ActivityLogin.util.saveString(PASSWORD_KEY,password); ConnectionSingleton.getInstance().login(account,password);//login //loginsuccess System.out.println("loginsuccess"); ActivityLogin.mCurrentAccount=account; System.out.println(ConnectionSingleton.getInstance() .getUser()); //登录成功后发现在线状态
Presencepresence=newPresence(Presence.Type.available); ConnectionSingleton.getInstance().sendPacket(presence); //开始主界面
Intentintent=newIntent(ActivityLogin.this, ActivityMain.class); startActivity(intent);
获取联系人模块(ActivityMain主界面)
获取联系人并将相关信息保存到一个list数组里,最后通知listview更新界面
roster=ActivityMain.connection.getRoster();
publicvoidupdateRoster(){ Collection<RosterEntry>entries=roster.getEntries(); for(RosterEntryentry:entries){ System.out.print(entry.getName()+"-"+entry.getUser()+"-" +entry.getType()+"-"+entry.getGroups().size()); Presencepresence=roster.getPresence(entry.getUser()); System.out.println("-"+presence.getStatus()+"-" +presence.getFrom()); Useruser=newUser(); user.setName(entry.getName()); user.setUser(entry.getUser()); user.setType(entry.getType()); user.setSize(entry.getGroups().size()); user.setStatus(presence.getStatus()); user.setFrom(presence.getFrom()); userinfos.add(user); } rosterAdapter.notifyDataSetChanged(); }
单人聊天模块
第一次修改:在主界面点击选择一个用户,进入聊天Activity,ActivityChat先获取传过来的用户,创建聊天类并对该用户设置消息监听
ChatManagerchatmanager=ConnectionSingleton.getInstance()
.getChatManager();
//getuser
Intentintent=getIntent();
Stringuser=intent.getStringExtra("user");
System.out.println("user:"+user);
//newasession
newChat=chatmanager.createChat(user,null);
//监听聊天消息
chatmanager.addChatListener(newChatManagerListenerEx());
//sendmessage
try{
newChat.sendMessage("imbirdman");
}catch(XMPPExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
监听类
publicclassChatManagerListenerEximplementsChatManagerListener{
@Override
publicvoidchatCreated(Chatchat,booleanarg1){
//TODOAuto-generatedmethodstub
chat.addMessageListener(ml);
}
}
publicclassMessageListenerEximplementsMessageListener{
@Override
publicvoidprocessMessage(Chatarg0,Messagemessage){
Stringresult=message.getFrom()+":"+message.getBody();
System.out.println(result);
android.os.Messagemsg=newandroid.os.Message();
msg.what=0;
Bundlebd=newBundle();
bd.putString("msg",result);
msg.setData(bd);
handler.sendMessage(msg);
}
}
所获取到的消息都是通过handler来更新UI
publicHandlerhandler=newHandler(){
@Override
publicvoidhandleMessage(android.os.Messagemsg){
switch(msg.what){
case0:{
Stringresult=msg.getData().getString("msg");
record.setText(record.getText()+"\n"+result);
}
break;
default:
break;
}
}
};
aaa跟bbb的聊天
第二次修改:
第一次的测试,发现如果多个人之间都成为好友,那么他们之间的聊天就出现了接收不到的信息,当然在跟spark测试时,spark可以收到android端的信息,不过android客户端是收到这个信息,不过却没有显示出来,具体原因还不太清楚。因此在第二次修改我改成监听所有聊天信息包,然后再分析包的归属,分发到对应的聊天窗口。
这里就是监听到包后打印的消息,打印出了jid和消息内容
publicclassXmppMessageManagerimplementsChatManagerListener{
privateXMPPConnection_connection;
privateChatManagermanager=null;
publicvoidinitialize(XMPPConnectionconnection){
_connection=connection;
manager=_connection.getChatManager();
manager.addChatListener(this);
}
@Override
publicvoidchatCreated(Chatchat,booleanarg1){
//TODOAuto-generatedmethodstub
chat.addMessageListener(newMessageListener(){
publicvoidprocessMessage(Chatnewchat,Messagemessage){
//若是聊天窗口存在,将消息转往目前窗口
//若窗口不存在,创建新的窗口
System.out
.println(message.getFrom()+":"+message.getBody());
if(!ActivityMain.chats.containsKey(message.getFrom())){
ActivityMain.chats.put(message.getFrom(),newchat);
}else{
}
}
});
}
}
主要就是重写了ChatManagerListener类的监听,分发处理暂时没有想好怎么写。接着在后台启动service就可以开始监听,行了第一次修改那些可以去掉了^0^。
多人聊天模块
也是在主界面的菜单进入ActivityMultiChat,该界面显示所创建的房间,点击就跳转到ActivityMultiRoom。获取所有房间比较简单,只需执行下面这段代码
hostrooms=MultiUserChat.getHostedRooms(ActivityMain.connection,
"conference.zhanghaitao-pc");
跳转到后获取要加入的房间的jid,并创建监听。
jid=getIntent().getStringExtra("jid");
//后面服务名称必需是创建房间的那个服务
StringmultiUserRoom=jid;
try{
muc=newMultiUserChat(ActivityMain.connection,multiUserRoom);
//创建聊天室,进入房间后的nickname
muc.join(ActivityLogin.mCurrentAccount);
Log.v(TAG,"joinsuccess");
}catch(XMPPExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
ChatPacketListenerchatListener=newChatPacketListener(muc);
muc.addMessageListener(chatListener);
监听大概的流程跟单人聊天差不多,都是handler来操作。不过多人聊天是重写了PacketListener。具体如下(不过该方法是监听房间的信息,也就是说显示的是以房间为名字的消息):
classChatPacketListenerimplementsPacketListener{
privateString_number;
privateDate_lastDate;
privateMultiUserChat_muc;
privateString_roomName;
publicChatPacketListener(MultiUserChatmuc){
_number="0";
_lastDate=newDate(0);
_muc=muc;
_roomName=muc.getRoom();
}
@Override
publicvoidprocessPacket(Packetpacket){
Messagemessage=(Message)packet;
Stringfrom=message.getFrom();
if(message.getBody()!=null){
DelayInformationinf=(DelayInformation)message.getExtension(
"x","jabber:x:delay");
DatesentDate;
if(inf!=null){
sentDate=inf.getStamp();
}else{
sentDate=newDate();
}
Log.w(TAG,"Receiveoldmessage:date="
+sentDate.toLocaleString()+";message="
+message.getBody());
android.os.Messagemsg=newandroid.os.Message();
msg.what=RECEIVE;
Bundlebd=newBundle();
bd.putString("from",from);
bd.putString("body",message.getBody());
msg.setData(bd);
handler.sendMessage(msg);
}
}
}
下载模块
在主界面对着用户名长按,进入下载activity。进入activityFileTransfer,点击传输按钮即可将文件传输给之前选择的用户,当然这里做得比较简单,并没有拒绝功能,一旦发现有文件就接受。FileTransferManagertransfer=newFileTransferManager(
ActivityMain.connection);
Stringdestination=user;
OutgoingFileTransferout=transfer
.createOutgoingFileTransfer(destination+"/Smack");
那用户是如何监听到有文件并且接受呢?在进入主界面的时候就已经开始了一个service(fileListenerService),该服务创建文件的监听类(XmppFileManager),监听类主要继承FileTransferListener重写里面的fileTransferRequest方法。
FilesaveTo;
//setanswerToforrepliesandsend()
answerTo=request.getRequestor();
if(!Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())){
send("ExternalMedianotmountedread/write");
return;
}elseif(!landingDir.isDirectory()){
send("Thedirectory"+landingDir.getAbsolutePath()
+"isnotadirectory");
return;
}
saveTo=newFile(landingDir,request.getFileName());
if(saveTo.exists()){
send("Thefile"+saveTo.getAbsolutePath()+"alreadyexists");
//delete
saveTo.delete();
//return;
}
IncomingFileTransfertransfer=request.accept();
send("Filetransfer:"+saveTo.getName()+"-"
+request.getFileSize()/1024+"KB");
try{
transfer.recieveFile(saveTo);
send("Filetransfer:"+saveTo.getName()+"-"
+transfer.getStatus());
doublepercents=0.0;
while(!transfer.isDone()){
if(transfer.getStatus().equals(Status.in_progress)){
percents=((int)(transfer.getProgress()*10000))/100.0;
send("Filetransfer:"+saveTo.getName()+"-"
+percents+"%");
}elseif(transfer.getStatus().equals(Status.error)){
send(returnAndLogError(transfer));
return;
}
Thread.sleep(1000);
}
if(transfer.getStatus().equals(Status.complete)){
send("Filetransfercomplete.Filesavedas"
+saveTo.getAbsolutePath());
}else{
send(returnAndLogError(transfer));
}
}catch(Exceptionex){
Stringmessage="Cannotreceivethefilebecauseanerroroccuredduringtheprocess."
+ex;
Log.e(TAG,message,ex);
send(message);
}
网上说文件的传输遇到几个比较重要的问题,我也查看了很多资料(国内的基本是寥寥无几,对此我感到挺无奈的,只能看国外,这样每次我的英语水平都提高了太无奈了^0^)。在这个androidasmack的最新版本好像是解决了几个比较重要的问题,剩下一个传输文件没反应的问题我在初始化连接时调用了configure方法解决。不过不知道是不是这个原因,后面出现了一个神奇的问题,就是有时可以成功传输有时传输时客户端没响应(如果有人完美解决了这个问题,那就………赶紧将代码放出来一起共享^-^,以提高我国程序员的整体水平,多伟大遥远的理想啊~~)。
项目下载
本文为原创翻译,如需转载,请注明作者和出处,谢谢!
出处:
文章其实在一个星期前就写好了,不过一直忙着看asmack源码,发现有很多可以学习的地方,可以自己定制自己喜欢的类和功能,因此越到后面越发现自己这篇文章很多地方都写得挺简单的,差点就不想发了^-^。不过为了上面那个伟大的理想……
相关文章推荐
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- Android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输
- android asmack 注册 登陆 聊天 多人聊天室 文件传输XMPP协议简介
- android asmack 注册 登陆 聊天 多人聊天室 文件传输 (zz)
- android asmack 注册 登陆 聊天 多人聊天室 文件传输【1】
- android asmack 注册 登陆 聊天 多人聊天室 文件传输