Android asmack使用介绍

Android asmack使用介绍

文章最后附上Demo地址

一、简介

1.什么是Xmpp协议?

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

2.什么是IM?

IM(Instant Messenger,即时通信软件),比如QQ、微信、Gtalk(谷歌推出的一款IM软件),其中Gtalk 就是基于XMPP 协议的一个实现。

3.什么是asmack?

smack是一套很好的开源即时通讯api,是基于Xmpp协议的实现,而在Android,则提供了一个asmack的jar包,其功能以及用法跟smack一致。

4.如何搭建一个Android平台完整的XMPP IM 实现?

服务器端——Openfire,Android客户端——基于asmack的使用

这里写图片描述

(注:openfire是基于XMPP 协议的IM 的服务器端的一个实现。搭建OpenFire服务器请看另外篇文章OpenFire服务器搭建)

5.asmack优点
  • aSmack是一个简单的,功能强大的类库。给用户发送信息只需三行代码便可完成

    XMPPConnection connection = new XMPPTCPConnection(”jabber.org“);
    
    connection.login(”mtucker”, “password”);
    
    connection.createChat(”jsmith@jivesoftware.com“).sendMessage(”Howdy!”);
  • 不会强迫你向其他类库那样,在信息包层面进行编码。它提供了更加智能化的类比如Chat,能使你的工作更富效率。

  • 不需要你熟悉XMPP,甚至是XML格式。

  • 易于实现机-机对话。

  • Apace License下的开源软件。你可以把它用于你的商业或非商业程序。

二、准备工作

  1. 使用asmack之前本地或者远程必须有个服务器openFire支持,如何搭建请看另外篇文章OpenFire服务器搭建

  2. 搭建完开启openfire服务器(进入Openfire安装目录,点击bin目录下的openfired.exe)

这里写图片描述
测试是否开启服务器 在浏览器输入 127.0.0.1:9090

这里写图片描述

  1. 在项目中添加 这里写图片描述asmack的jar包

三、在Android中使用asmack

​ 在Android客户端需要实现IM及时通讯的功能点有:

  • 保持与服务器的通信
  • 用户注册
  • 用户登录
  • ​好友添加与申请、删除好友
  • 好友列表、群列表

  • 好友聊天:消息发送与接收,图片发送与接收

  • 群聊天:消息发送与接收

下面将会介绍如何去使用asmack去实现各个功能点

1.保持与服务器的通信

在两人进行聊天时,需要实时的拿到消息,必须保证对服务器的实时访问,不断地拿数据,所有需要保持一个与服务器的长连接,进行数据交互。

所以asmack提供了一个XMPPConnection的类来保持一个与服务器的长连接(建议采用单例模式)。

具体使用如下:

XMPPConnection.DEBUG_ENABLED = true;
//配置文件  参数(服务地地址,端口号,域)
ConnectionConfiguration conConfig = new ConnectionConfiguration(
      SERVER_HOST, SERVER_PORT, SERVER_NAME);
//设置断网重连 默认为true
conConfig.setReconnectionAllowed(true);
//设置登录状态 true-为在线
conConfig.setSendPresence(true);
//设置不需要SAS验证
conConfig.setSASLAuthenticationEnabled(true);
//开启连接
XMPPConnection connection = new XMPPConnection(conConfig);
connection.connect();
//添加额外配置信息
configureConnection();
2.用户注册

​ 用户必须注册一个账户,作为一个用户身份识别的作用。

asmack提供了一个Registration的类使用(XmppConnection是自定义的一个XMPPconnection配置工具类,下文将会经常用到),注册操作是一个网络耗时操作,要放在子线程中去执行。

Registration reg = new Registration();
//设置类型
reg.setType(IQ.Type.SET);
//发送到服务器
reg.setTo(XmppConnection.getConnection().getServiceName());
//设置用户名
reg.setUsername(account);
//设置密码
reg.setPassword(pass);

//设置其余属性 不填可能会报500异常 连接不到服务器 asmack一个Bug
//设置昵称(其余属性)
reg.addAttribute("name", name);
//设置邮箱(其余属性)
reg.addAttribute("email", email);
//设置android端注册
reg.addAttribute("android", "geolo_createUser_android");
//创建包过滤器
PacketFilter filter = new AndFilter(new PacketIDFilter(reg
        .getPacketID()), new PacketTypeFilter(IQ.class));
//创建包收集器
PacketCollector collector = XmppConnection.getConnection()
        .createPacketCollector(filter);
//发送包
XmppConnection.getConnection().sendPacket(reg);
//获取返回信息
IQ result = (IQ) collector.nextResult(SmackConfiguration
        .getPacketReplyTimeout());
// 停止请求results(是否成功的结果)
collector.cancel();
//通过返回信息判断
if (result == null) {   //无返回,连接不到服务器
} else if (result.getType() == IQ.Type.ERROR) {     //错误状态
    if (result.getError().toString()
            .equalsIgnoreCase("conflict(409)")) {   //账户存在 409判断
    } else {
    }
} else if (result.getType() == IQ.Type.RESULT) {//注册成功跳转登录

}

这里写图片描述

​ 在OpenFire网页端上查看是否注册成功

这里写图片描述

3.用户登录

​ asmack提供了Presence的一个类

​ Presence介绍:

​ Presence是继承自XMPP的基类Packet信息包,Presence主要有两个用途:

​ ① 告诉服务器所有客户端当前所处的状态,

​ ② 发出添加/删除好友请求;每个Presence信息包都有一个类型属性Presence.Type 如下:

​ available: 表示处于在线状态

​ unavailable: 表示处于离线状态

​ subscribe: 表示发出添加好友的申请

​ unsubscribe: 表示发出删除好友的申请

​ unsubscribed: 表示拒绝添加对方为好友

​ error: 表示presence信息报中包含了一个错误消息。

// 连接服务器,用户登录
XmppConnection.getConnection().login(userStr, passStr);
// 连接服务器成功,更改在线状态
Presence presence = new Presence(Presence.Type.available);
XmppConnection.getConnection().sendPacket(presence);

这里写图片描述

4.好友添加与申请

​ 分为三部分:①自己发送好友添加请求,②监听发送过来的好友请求,③处理添加好友请求(拒绝和允许)。

​ 1) 发起用户添加申请

​ 使用Presence类,通过Presence.Type.subscribe表示发出添加好友的申请的状态,让服务器知道发送的数据是一个好友添加申请。

String name = et_name.getText().toString();
//设置添加好友请求
Presence subscription = new Presence(Presence.Type.subscribe);
//拼接好友全称
subscription.setTo(name + "@" + XmppConnection.SERVER_NAME);
//发送请求
XmppConnection.getConnection().sendPacket(subscription);

​ 2) 监听添加好友申请

​ 需要解决的是如何知道服务器发送过来的是好友添加的一个请求?通过asmack提供的一个PacketFilter筛选器和PacketListener数据监听类,发起一个addPacketListener(PacketListener,PacketListener),开启数据监听,通过PacketFilter筛选器筛选packet,监听是否是Presence.Type.subscribe添加好友申请的状态。

/**
 * 添加一个监听,监听好友添加请求。
 */
private void addSubscriptionListener() {
    //创建包过滤器
    PacketFilter filter = new PacketFilter() {
        @Override
        public boolean accept(Packet packet) {
            if (packet instanceof Presence) {
                Presence presence = (Presence) packet;
                //是好友邀请状态就返回true 向下执行
                if (presence.getType().equals(Presence.Type.subscribe)) {
                    return true;
                }
            }
            return false;
        }
    };
    //开启监听
    XmppConnection.getConnection().addPacketListener(subscriptionPacketListener, filter);
}

/**
 * 好友监听
 */
private PacketListener subscriptionPacketListener = new PacketListener() {

    @Override
    public void processPacket(final Packet packet) {
        //过滤自己加自己的状态
        if (packet.getFrom().contains(((AppGlobal) getApplication()).getName()
                + "@" + XmppConnection.SERVER_NAME))
            return;
        //弹出好友添加对话框
        Message msg = new Message();
        msg.obj = packet;
        msg.what = ADD_FRIEND;
        handler.sendMessage(msg);
    }
};

​ 3)处理好友添加申请

​ asmack提供一个Roster类

​ Roster:表示一个用户的所有好友清单以及申请加好友的用户清单的一个类

​ ①允许添加:添加好友进入自己的好友列表

//获取Roster对象
Roster roster = XmppConnection.getConnection().getRoster();
try {
    //packet是上文PacketListener监听返回的packet
    roster.createEntry(packet.getFrom(), name, null);
} catch (XMPPException e) {
    e.printStackTrace();
}

​ ②拒绝好友申请(注:虽然拒绝好友添加。但是对方仍可以把你添加进对方列表。asmack添加好友的方式是通过订阅的方式,只要订阅你,对方就可以添加进他的好友列表)

Presence presenceRes = new Presence(Presence.Type.unsubscribe);
presenceRes.setTo(packet.getFrom());
XmppConnection.getConnection().sendPacket(presenceRes);

这里写图片描述

5.删除好友

​ 通过Rooster中的removeEntry方法将好友移除

​ RosterEntry类:表示Roster中的每条记录.它包含了用户的JID,用户名,或用户分配的昵称.

XMPPConnection conn = XmppConnection.getConnection();
Roster roster = conn.getRoster();
RosterEntry entry = roster.getEntry(friendList.get(position).get("User"));
try {
    roster.removeEntry(entry);
} catch (XMPPException e) {
    e.printStackTrace();
}

这里写图片描述

6.获取好友列表

RosterEntry类:表示Roster中的每条记录.它包含了用户的JID,用户名,或用户分配的昵称.

Roster roster = XmppConnection.getConnection().getRoster();
Collection<RosterEntry> entries = roster.getEntries();
//循环拿到所有好友信息
for (RosterEntry entry : entries) {
}
7.进行好友聊天

​ 针对消息发送与接受的数据,asmack单独提供了一个ChatManager的聊天室工具类和Chat消息对象

​ 1)获取好友消息

​ 通过获取到ChatManager类,然后开启聊天室监听,通过获取到的chat对象,开启消息监听 MessageListener

//获取消息管理类
ChatManager chatMan = XmppConnection.getConnection().getChatManager();
//添加聊天室监听
chatMan.addChatListener(new ChatManagerListener() {
            @Override
            public void chatCreated(Chat chat, boolean able) {
                // 添加消息监听
                chat.addMessageListener(new MessageListener() {
                    @Override
                    public void processMessage(Chat chat, Message message) {
                        // 获取当前好友发来的信息
                        if (message.getFrom().contains(toUserID)) {

                        }
                    }
                });
            }
        });

​ 2)发送消息

//获取消息管理类
ChatManager chatMan = XmppConnection.getConnection().getChatManager();
//创建消息对象 参数(用户名称,MessageListener消息监听)
Chat newchat = chatMan.createChat(toUserID, null);
try {
       Message msg = new Message();
       String content = "消息内容"
       msg.setBody(content);
       // 发送消息
       newchat.sendMessage(msg);
} catch (XMPPException e) {
      e.printStackTrace();
}

这里写图片描述

8.收发图片

​ 针对发送图片,asmack提供了FileTransferManager文件传输的工具类,文件接收类IncomingFileTransfe

文件发送类OutgoingFileTransfer。我们可以通过把图片转换成数据的格式进行传输。

​ 1)接收图片

​ 文件消息监听,注册一个文件消息监听类FileTransferListener,接收到文件后先保存到本地,然后通过Handler传递保存路径的字符串,然后去刷新adapter,在adapter中再去加载下载到的本地图片。(只贴重要代码)


FileTransferManager manager = XmppConnection.getFileTransferManager();
  manager.addFileTransferListener(new FileTransferListener() {
        @Override
            public void fileTransferRequest(final FileTransferRequest request) {
                new Thread() {
                    @Override
                    public void run() {
                        //文件接收
                        IncomingFileTransfer transfer = request.accept();
                        //获取文件名字
                        String fileName = transfer.getFileName();
                        //本地创建文件
                        File sdCardDir = new File(ALBUM_PATH);
                        if (!sdCardDir.exists()) {//判断文件夹目录是否存在
                            sdCardDir.mkdir();//如果不存在则创建
                        }
                        String save_path = ALBUM_PATH + fileName;
                        File file = new File(save_path);
                        //接收文件
                        try {
                            transfer.recieveFile(file);
                            while (!transfer.isDone()) {
                                if (transfer.getStatus().equals(FileTransfer.Status.error)) {
                                    System.out.println("ERROR!!! " + transfer.getError());
                                } else {
                                    System.out.println(transfer.getStatus());
                                    System.out.println(transfer.getProgress());
                                }
                                try {
                                    Thread.sleep(1000L);
                                } catch (Exception e) {
                                }
                            }
                            //判断是否完全接收文件
                            if (transfer.isDone()) {
                                String[] args = new String[]{toUserName, fileName};
                                android.os.Message msg = handler.obtainMessage();
                                msg.what = 2;
                                msg.obj = args;
                                //发送msg,刷新adapter显示图片
                                msg.sendToTarget();
                            }
                        } catch (XMPPException e) {
                            e.printStackTrace();
                        }
                    }

                    ;
                }.start();
            }
  }

​ 2)发送图片

​ ①添加一个点击方法,打开本地图库

Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, TUPIAN_RESULT);

​ ②选择完图片在onActivityResult拿到图片路径

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    //图片路径
    String picPath;
    if (data != null) {
        Uri uri = data.getData();
        if (!TextUtils.isEmpty(uri.getAuthority())) {
            Cursor cursor = getContentResolver().query(uri,
                    new String[]{MediaStore.Images.Media.DATA}, null, null, null);
            if (null == cursor) {
                Toast.makeText(this, "图片没找到", Toast.LENGTH_SHORT).show();
                return;
            }
            cursor.moveToFirst();
            //图片路径
            picPath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
            cursor.close();
        } else {
            //图片路径
            picPath = uri.getPath();
        }
    } else {
        Toast.makeText(this, "图片没找到", Toast.LENGTH_SHORT).show();
        return;
    }
    // 开始发送图片 参数(图片路径,接收者账户)
    new SendFileTask().execute(picPath, toUserID);
}

​ ③通过异步的方式AsyncTask去发送图片

class SendFileTask extends AsyncTask<String, Integer, Integer> {
    protected Integer doInBackground(String... params) {
        if (params.length < 2) {
            return Integer.valueOf(-1);
        }
        String img_path = params[0];
        //asmack的一个坑,如果接收者名称后缀不加上/Smack,则会报503错误
        String toId = params[1] + "/Smack";

        FileTransferManager fileTransferManager = XmppConnection.getFileTransferManager();
        File filetosend = new File(img_path);
        if (filetosend.exists() == false) {
            return -1;
        }
        OutgoingFileTransfer transfer = fileTransferManager
                .createOutgoingFileTransfer(toId);// 创建一个输出文件传输对象
        try {
            transfer.sendFile(filetosend, "recv img");
            while (!transfer.isDone()) {
                if (transfer.getStatus().equals(FileTransfer.Status.error)) {
                    System.out.println("ERROR!!! " + transfer.getError());
                } else {
                    System.out.println(transfer.getStatus());
                    System.out.println(transfer.getProgress());
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //文件发送完毕
            if (transfer.isDone()) {
                String[] args = new String[]{mAppGlobal.getName(), img_path};
                android.os.Message msg = handler.obtainMessage();
                msg.what = 3;
                msg.obj = args;
                //刷新adapter
                msg.sendToTarget();
            }
        } catch (XMPPException e1) {
            e1.printStackTrace();
        }
        return 0;
    }
}

这里写图片描述

10.获取群列表

​ 针对群聊功能,asmack提供了创建聊天室以及获取聊天群信息的一个类——MultiUserChat

​ MultiUserChat.getHostedRooms方法遍历获取到整个服务器上的所有群聊列表。

try {
    //遍历每个人所创建的群
    for (HostedRoom host :MultiUserChat.getHostedRooms(XmppConnection.getConnection(),          XmppConnection.getConnection().getServiceName())) {
        //遍历某个人所创建的群
        for (HostedRoom singleHost : MultiUserChat.getHostedRooms(XmppConnection.getConnection(), host.getJid())) {
            if (singleHost.getJid().indexOf("@") > 0) {
                Map<String, String> map = new HashMap<>();
                map.put("Gname", singleHost.getName());
                groupList.add(map);
            }
        }
    }
    mHandler.sendEmptyMessage(0);
} catch (XMPPException e) {
    e.printStackTrace();
}
9.创建群组
public static boolean createRoom(String user, String roomName,
                        String password) {
   if (getConnection() == null)
      return false;

   MultiUserChat muc = null;
   try {
      // 创建一个MultiUserChat 参数(XmppConnection,群全称)
      muc = new MultiUserChat(getConnection(), roomName + "@conference."
            + getConnection().getServiceName());
      // 创建聊天室
      muc.create(roomName);
      // 获得聊天室的配置表单
      Form form = muc.getConfigurationForm();
      // 根据原始表单创建一个要提交的新表单。
      Form submitForm = form.createAnswerForm();
      // 向要提交的表单添加默认答复
      for (Iterator<FormField> fields = form.getFields(); fields
            .hasNext();) {
         FormField field = (FormField) fields.next();
         if (!FormField.TYPE_HIDDEN.equals(field.getType())
               && field.getVariable() != null) {
            // 设置默认值作为答复
            submitForm.setDefaultAnswer(field.getVariable());
         }
      }
      // 设置聊天室的新拥有者
      List<String> owners = new ArrayList<>();
      owners.add(getConnection().getUser());// 用户JID
      submitForm.setAnswer("muc#roomconfig_roomowners", owners);
      // 设置聊天室是持久聊天室,即将要被保存下来
      submitForm.setAnswer("muc#roomconfig_persistentroom", true);
      // 房间仅对成员开放
      submitForm.setAnswer("muc#roomconfig_membersonly", false);
      // 允许占有者邀请其他人
      submitForm.setAnswer("muc#roomconfig_allowinvites", true);
      if (!password.equals("")) {
         // 进入是否需要密码
         submitForm.setAnswer("muc#roomconfig_passwordprotectedroom",
               true);
         // 设置进入密码
         submitForm.setAnswer("muc#roomconfig_roomsecret", password);
      }
      // 能够发现占有者真实 JID 的角色
      // submitForm.setAnswer("muc#roomconfig_whois", "anyone");
      // 登录房间对话
      submitForm.setAnswer("muc#roomconfig_enablelogging", true);
      // 仅允许注册的昵称登录
      submitForm.setAnswer("x-muc#roomconfig_reservednick", true);
      // 允许使用者修改昵称
      submitForm.setAnswer("x-muc#roomconfig_canchangenick", false);
      // 允许用户注册房间
      submitForm.setAnswer("x-muc#roomconfig_registration", false);
      // 发送已完成的表单(有默认值)到服务器来配置聊天室
      muc.sendConfigurationForm(submitForm);
   } catch (XMPPException e) {
      e.printStackTrace();
      return false;
   }
   return  true;
}

这里写图片描述

11.进入聊天室

​ 加入聊天室,得知道这个房间的名称,通过群列表那里可以获取到,然后创建MultiUserChat对象,调用join方法

public static void joinMultiUserChat(String user, String password, String roomsName) {
   try {
      // 使用XMPPConnection创建一个MultiUserChat窗口
      MultiUserChat muc = new MultiUserChat(connection, roomsName
            + "@conference." + connection.getServiceName());
      // 聊天室服务将会决定要接受的历史记录数量
      DiscussionHistory history = new DiscussionHistory();
      history.setMaxStanzas(0);
      // 用户加入聊天室 参数()
      muc.join(user, password, history, SmackConfiguration.getPacketReplyTimeout());
      //保存multiUserChat对象
      multiUserChat = muc;
      System.out.println("会议室加入成功........");
   } catch (XMPPException e) {
      e.printStackTrace();
      System.out.println("会议室加入失败........");
      muc = null;
   }
}
12.进行群聊天

​ 1)消息监听

MultiUserChat muc = XmppConnection.getMultiUserChat();
muc.addMessageListener(new PacketListener() {
    @Override
    public void processPacket(Packet packet) {
        Message message = (Message) packet;
        // 接收来自聊天室的聊天信息
        String groupName = message.getFrom();
        String[] nameOrGroup = groupName.split("/");
        //判断是否是本人发出的消息 不是则显示
        if (!nameOrGroup[1].equals(getUserName())) {
            String[] args = new String[]{nameOrGroup[1], message.getBody()};
            // 在handler里取出来显示消息
            android.os.Message msg = handler.obtainMessage();
            msg.what = 1;
            msg.obj = args;
            msg.sendToTarget();
        }
    }
});

​ 2)发送群消息

try {
    muc.sendMessage(content);
} catch (XMPPException e) {
    e.printStackTrace();
}           

这里写图片描述



Demo地址

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值