您的位置:首页 > 移动开发 > Android开发

android asmack 注册 登陆 聊天 多人聊天室 文件传输

2016-04-13 12:01 561 查看

本篇文章为转载文章,转自博客园的一篇文章,作者:没有代码。

/article/5680341.html

XMPP协议简介

XMPP协议(ExtensibleMessagingandPresenceProtocol,可扩展消息处理现场协议)是一种基于XML的协议,目的是为了解决及时通信标准而提出来的,最早是在Jabber上实现的。它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。并且XML很易穿过防火墙,所以用XMPP构建的应用不易受到防火墙的阻碍。利用XMPP作为通用的传输机制,不同组织内的不同应用都可以进行有效的通信。

这篇文章有基本的介绍,/article/8446300.html

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服务器

具体步骤请移步:http://javatech.blog.163.com/blog/static/1766322992010111725339587/

配置成功如果以后ip地址变了,那肯定又是开不了,解决办法请移步:http://blog.csdn.net/HappySheepherder/article/details/4707124

配置成功后,在服务器创建一个简单的用户来测试,然后安装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方法解决。不过不知道是不是这个原因,后面出现了一个神奇的问题,就是有时可以成功传输有时传输时客户端没响应(如果有人完美解决了这个问题,那就………赶紧将代码放出来一起共享^-^,以提高我国程序员的整体水平,多伟大遥远的理想啊~~)。

项目下载

http://files.cnblogs.com/not-code/ASmack.zip

本文为原创翻译,如需转载,请注明作者和出处,谢谢!

出处:/article/5680341.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: