之前做了一个神秘的政府项目,此处保密。里面用的东西奇多,又是专网。抛弃了各种第三方。原本刚来,不知道是用专网(这里近似看作局域网吧),我很兴奋的用极光,环信还有网易云之类的,还互相比较,最后慢慢写,写了近一个星期,就只差群聊就做完了。。。然后被通知是专网。。全部删掉了。想哭。。。
这里还是建议考虑项目需求,再选择使用那种方案,用第三方的这些集成好的sdk也挺好的。
XMPP(Extensible Messageing and Presence Protocol:可扩展消息与存在协议)是目前主流的四种IM(IM:instant messaging,即时消息)协议之一,其他三种分别为:即时信息和空间协议(IMPP)、空间和即时信息协议(PRIM)、针对即时通讯和空间平衡扩充的进程开始协议SIP(SIMPLE)。
这些是相对成熟稳定的xmpp客户端软件:
spark
Psi
JWChat
xmpp服务器端:
openfire
tigase
iChatServer (通常用于IOS)
jabberd2
xmpp Library:
Smack (java)
Asmack (java)
gloox (C++)
这里需要注意的是,据说openfire最多就30w用户(还是修改源码优化的),tigase的商业版本支持到1400w。
现在smack(4.1以上版本)也可以直接运行Android平台上了,所以可以看到asmack也没有什么更新了。
openfire 下载地址:http://www.igniterealtime.org/downloads/index.jsp
对于安装,配置什么的,我就不多说了。
Android 端需要导入一个jar包,因为java是使用smack进行开发,而Asmack是针对android 改造的smack,所以我们采用Asmack即可。
网上有很多的jar包版本,我这里使用的是
这里也有简单的asmack对应的API中文文档供参考:http://download.csdn.net/download/arinasiyyj/7816239
那么接下来,咱们就先从登陆入手。
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp">
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="用户名"
android:paddingLeft="10dp"
android:gravity="center_vertical"
android:paddingRight="10dp"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp">
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="密码"
android:paddingLeft="10dp"
android:gravity="center_vertical"
android:paddingRight="10dp"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp">
<EditText
android:id="@+id/et_server"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint="服务器"
android:paddingLeft="10dp"
android:gravity="center_vertical"
android:paddingRight="10dp"/>
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/bt_login"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginRight="10dp"
android:text="登陆"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/ck_savepwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="记住密码"
/>
<CheckBox
android:id="@+id/ck_autologin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:text="自动登录"
/>
</RelativeLayout>
<Button
android:id="@+id/bt_register"
android:layout_width="90dp"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:layout_gravity="right"
android:text="注册"/>
</LinearLayout>
Login:
@OnClick({R.id.bt_login, R.id.bt_register})
public void onClick(View view) {
switch (view.getId()) {
case R.id.bt_login:
// 切记登陆连接服务器操作不能放在主线程操作
new Thread(new Runnable() {
@Override
public void run() {
if (login()){
startActivity(new Intent(LoginActivity.this,MainActivity.class));
finish();
}else {
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(LoginActivity.this,"登陆失败,请检查用户名,密码或者服务器IP是否输入正确!",Toast.LENGTH_SHORT).show();
}
});
}
}
}).start();
break;
case R.id.bt_register:
startActivity(new Intent(LoginActivity.this,RegisterActivity.class));
break;
}
}
private boolean login(){
try {
XMPPConnection connection = XMPPUtil.getXMPPConnection(myLoginData.LoginServer);
if (connection == null) {
Log.i("tag","failed");
throw new Exception("连接服务器失败!");
}
connection.login(myLoginData.username, myLoginData.password); //连接服务器
// Roster mRoster=connection.getRoster(); //获取花名册
// RosterGroup group = mRoster.createGroup("facehand"); //创建默认分组
MySave.setXMPPConnection(this,connection);
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
具体连接服务器操作:
package com.mychat.myxmpp.common;
import android.util.Log;
import com.mychat.myxmpp.XMPPUtilClient;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.provider.PrivacyProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smackx.GroupChatInvitation;
import org.jivesoftware.smackx.PrivateDataManager;
import org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider;
import org.jivesoftware.smackx.packet.ChatStateExtension;
import org.jivesoftware.smackx.packet.LastActivity;
import org.jivesoftware.smackx.packet.OfflineMessageInfo;
import org.jivesoftware.smackx.packet.OfflineMessageRequest;
import org.jivesoftware.smackx.packet.SharedGroupsInfo;
import org.jivesoftware.smackx.provider.AdHocCommandDataProvider;
import org.jivesoftware.smackx.provider.DataFormProvider;
import org.jivesoftware.smackx.provider.DelayInformationProvider;
import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
import org.jivesoftware.smackx.provider.MUCAdminProvider;
import org.jivesoftware.smackx.provider.MUCOwnerProvider;
import org.jivesoftware.smackx.provider.MUCUserProvider;
import org.jivesoftware.smackx.provider.MessageEventProvider;
import org.jivesoftware.smackx.provider.MultipleAddressesProvider;
import org.jivesoftware.smackx.provider.RosterExchangeProvider;
import org.jivesoftware.smackx.provider.StreamInitiationProvider;
import org.jivesoftware.smackx.provider.VCardProvider;
import org.jivesoftware.smackx.provider.XHTMLExtensionProvider;
import org.jivesoftware.smackx.search.UserSearch;
/**
* Created by 李硕 QQ:752231513 on 2016/7/22.
*/
public class XMPPUtil {
public static XMPPConnection getXMPPConnection(String server, int port) {
try {
//配置configuration
ConnectionConfiguration configuration = new ConnectionConfiguration(server, port);
configuration.setReconnectionAllowed(true); //重连许可
//设置安全类型
configuration.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
configuration.setSendPresence(true);
//设置不需要SAS验证
// configuration.setSASLAuthenticationEnabled(false);
configure(ProviderManager.getInstance());
SASLAuthentication.supportSASLMechanism("PLAIN", 0); // SASL 的认证方式
XMPPConnection connection = new XMPPConnection(configuration, null);
connection.connect();
return connection;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static XMPPConnection getXMPPConnection(String server){
return getXMPPConnection(server,5222); //openfire 默认端口号 5222 如果冲突会加1,依次类推
}
/*
* 下面这个是我在执行搜索查询用户时总是会报空指针 * smack.providers 的文件不能载入asmack中导致的。所以必须我们自己手动载入一个。 * 所以加入下面代码,在xmppConnection是实例化之前载入即可
* */
public static void configure(ProviderManager pm) {
// Private Data Storage
pm.addIQProvider("query","jabber:iq:private", new PrivateDataManager.PrivateDataIQProvider());
// Time
try {
pm.addIQProvider("query","jabber:iq:time", Class.forName("org.jivesoftware.smackx.packet.Time"));
} catch (ClassNotFoundException e) {
Log.w("TestClient", "Can't load class for org.jivesoftware.smackx.packet.Time");
}
// Roster Exchange
pm.addExtensionProvider("x","jabber:x:roster", new RosterExchangeProvider());
// Message Events
pm.addExtensionProvider("x","jabber:x:event", new MessageEventProvider());
// Chat State
pm.addExtensionProvider("active","http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("composing","http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("paused","http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("inactive","http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("gone","http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
// XHTML
pm.addExtensionProvider("html","http://jabber.org/protocol/xhtml-im", new XHTMLExtensionProvider());
// Group Chat Invitations
pm.addExtensionProvider("x","jabber:x:conference", new GroupChatInvitation.Provider());
// Service Discovery # Items
pm.addIQProvider("query","http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
// Service Discovery # Info
pm.addIQProvider("query","http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());
// Data Forms
pm.addExtensionProvider("x","jabber:x:data", new DataFormProvider());
// MUC User
pm.addExtensionProvider("x","http://jabber.org/protocol/muc#user", new MUCUserProvider());
// MUC Admin
pm.addIQProvider("query","http://jabber.org/protocol/muc#admin", new MUCAdminProvider());
// MUC Owner
pm.addIQProvider("query","http://jabber.org/protocol/muc#owner", new MUCOwnerProvider());
// Delayed Delivery
pm.addExtensionProvider("x","jabber:x:delay", new DelayInformationProvider());
// Version
try {
pm.addIQProvider("query","jabber:iq:version", Class.forName("org.jivesoftware.smackx.packet.Version"));
} catch (ClassNotFoundException e) {
// Not sure what's happening here.
}
// VCard
pm.addIQProvider("vCard","vcard-temp", new VCardProvider());
// Offline Message Requests
pm.addIQProvider("offline","http://jabber.org/protocol/offline", new OfflineMessageRequest.Provider());
// Offline Message Indicator
pm.addExtensionProvider("offline","http://jabber.org/protocol/offline", new OfflineMessageInfo.Provider());
// Last Activity
pm.addIQProvider("query","jabber:iq:last", new LastActivity.Provider());
// User Search
pm.addIQProvider("query","jabber:iq:search", new UserSearch.Provider());
// SharedGroupsInfo
pm.addIQProvider("sharedgroup","http://www.jivesoftware.org/protocol/sharedgroup", new SharedGroupsInfo.Provider());
// JEP-33: Extended Stanza Addressing
pm.addExtensionProvider("addresses","http://jabber.org/protocol/address", new MultipleAddressesProvider());
// FileTransfer
pm.addIQProvider("si","http://jabber.org/protocol/si", new StreamInitiationProvider());
pm.addIQProvider("query","http://jabber.org/protocol/bytestreams", new BytestreamsProvider());
// Privacy
pm.addIQProvider("query","jabber:iq:privacy", new PrivacyProvider());
pm.addIQProvider("command", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider());
pm.addExtensionProvider("malformed-action", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.MalformedActionError());
pm.addExtensionProvider("bad-locale", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadLocaleError());
pm.addExtensionProvider("bad-payload", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadPayloadError());
pm.addExtensionProvider("bad-sessionid", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadSessionIDError());
pm.addExtensionProvider("session-expired", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.SessionExpiredError());
}
}
之后自己记得做一些个人信息的缓存,登陆信息的储存就可以完成登陆了,我这个是从登陆入手,记得在服务器端手动注册一个账号就行
实在太忙,有时间继续接着写。。。又被催着写项目去了
本人个人原创,如有雷同,纯属巧合,或者与本人联系,做改动。请转载或者CV组合标明出处,谢谢!