openfire+asmock实现消息推送

本文详细介绍了如何搭建Openfire服务器环境,并实现基于Java和Android客户端的消息推送功能,包括群发消息和广播插件的使用。


这里要感谢下袭烽和Ares1201两位大大,我就是按照他俩的文章做的。当然,他俩的文章讲的内容要比我这里的多。同时,也参考了很多其他人的文章,这里就不一一叙述了。


参考资料:

http://blog.youkuaiyun.com/ares1201/article/details/7737872

http://blog.youkuaiyun.com/shimiso/article/details/11225873


1、环境

Openfire3.8.2

Smack3.3.1

Asmack (不知道,在github上下载的打包不了,可能是公司网络的事,所以直接在网上找了个现成的)

Spark 2.6.3(不是必须的,下下来可以进行简单发发消息测试)

2、搭建openfire环境:

1)使用eclipse新建java工程,工程名字一定要是openfire_src,否则后续相关配置也要更改;

2)把下载的openfire源码拷贝到工程目录下;

3)把下载的3jarcoherence.jarcoherence-work.jartangosol.jar放到/openfire_src/build/lib下,并添加到build_path中;

4)使用eclipse的自动修复,把报的3个错误修复了,再把多出来的hazelcast包删掉(一下就能找到,全是错)。否则会跟另一个包有冲突;

5)打开ant窗口,把/openfire_src/build/build.xml拖入窗口,运行,系统会自动生成2个目录:targetwork

6)把ojdbc14.jar拷到/openfire_src/target/openfire/lib下(如果不使用oracle当数据库的不用这步)

7)配置启动设置:debug/run->configuration->javaapplication->new,如下配置:


 


配置classpath,把i18nresource资源加入


勾选rundebug


之后直接debug/run就可以直接运行了

8)使用浏览器打开localhost:9090,进入控制台,配置openfire

标准数据库->配置数据库连接->初始设置->管理员账号密码,之后就可以使用管理员账号密码登陆了。

9)这时,如果之前下载了spark,就可以连接上进行测试了,这里就不细写了。

10)加载broadcast插件:

进入控制台->插件->有效的插件->broadcast

这个插件的主要目的是进行群发(或者叫广播?反正我觉得是看客户端怎么实现的了)。

11)注册测试用户。我这里注册了个chrismartin,密码都是1,下面用作测试。

 

3、搭建java端测试工程,直接上代码了。主要是为了进行群发(广播)。

 

importjava.util.Collection;

importjava.util.Scanner;

 

importorg.jivesoftware.smack.Chat;

importorg.jivesoftware.smack.ChatManager;

importorg.jivesoftware.smack.Connection;

importorg.jivesoftware.smack.ConnectionConfiguration;

importorg.jivesoftware.smack.MessageListener;

importorg.jivesoftware.smack.Roster;

importorg.jivesoftware.smack.RosterEntry;

importorg.jivesoftware.smack.XMPPConnection;

importorg.jivesoftware.smack.XMPPException;

importorg.jivesoftware.smack.packet.Message;

 

 

public class IMTest{

 

/**

 * @param args

 */

publicstatic void main(String[] args) {

//TODO Auto-generated method stub

XMPPConnection.DEBUG_ENABLED= true;

Connectionconn = null;

try{

IMTestim = new IMTest();

conn= im.connect();

im.doLogin(conn);

im.getRosters(conn);

//                        im.sendMsg(conn);

im.sendPacket(conn);

}catch (XMPPException e) {

//TODO Auto-generated catch block

e.printStackTrace();

}finally{

conn.disconnect();

}

}

 

/**

 * 创建与服务器之间的链接

 * @return

 * @throws XMPPException

 */

privateConnection connect() throws XMPPException{

 

Connectionconn;

ConnectionConfigurationconnConf = new ConnectionConfiguration("192.168.175.227", 5222);

connConf.setCompressionEnabled(true);

connConf.setSASLAuthenticationEnabled(true);

conn= new XMPPConnection(connConf);

conn.connect();

returnconn;

}

 

/**

 * 登陆

 * @param conn

 * @throws XMPPException

 */

privatevoid doLogin(Connection conn) throws XMPPException{

conn.login("admin","1");

System.out.println(conn.getUser()+"haslogined");

}

 

/**

 * 获得联系人列表(只有添加好友的才能看到)

 * @param conn

 * @return

 */

privateCollection<RosterEntry> getRosters(Connection conn){

Collection<RosterEntry>rosters = conn.getRoster().getEntries();

for(RosterEntryentity : rosters){

   System.out.print("name: "+entity.getName()+ ",jid: " +entity.getUser());     //此处可获取用户JID

   System.out.println("" ); 

}

returnrosters;

}

 

/**

 * 发送消息

 * @param conn

 * @throws XMPPException

 */

privatevoid sendMsg(Connection conn) throws XMPPException{

ChatManagerchatManager = conn.getChatManager();

Chatchat = chatManager.createChat("chris@xueyi-pc", new MessageListener(){

 

/**

 * 获取对方发送的消息

 */

@Override

publicvoid processMessage(Chat arg0, Message msg) {

//TODO Auto-generated method stub

System.out.println("recievemessage is:"+msg.getBody());

}

});

//发送消息给对方

Scannerinput = new Scanner(System.in);

while(true) {

Stringmessage = input.nextLine();

System.out.println("mysend message is :"+message);

chat.sendMessage(message);

}

 

}

/**

 * 发送广播

 * @param conn

 */

privatevoid sendPacket(Connection conn){

Messagenewmsg = new Message();  

newmsg.setTo("all@broadcast.xueyi-pc");  //这句很重要,是使用broadcast插件向xueyi-pc域下的所有用户发送

newmsg.setSubject("重要通知"); 

newmsg.setBody("今天下午2点60分有会!"); 

newmsg.setType(Message.Type.headline);//normal支持离线  

conn.sendPacket(newmsg); 

conn.disconnect(); 

}

 

}

 

如果这时候您有spark,并且连接上之前搭建的openfire服务器了的话,您就可以接到上面程序发送的消息。

注意:all@broadcast.xueyi-pc这句是关键,如果spark收不到信息,您可以看看spark登陆的用户是否是在xueyi-pc域下。

具体可以通过getRosters()方法,获得用户列表,之后可以查看。

 

4、搭建android端。

以上步骤都OK了,可以搭建android的客户端了。因为公司网络各种墙,github上下载的asmack源码编译不了,我只弄到了asmack包。

这里为了测试,我只做了3个类:MainActivityXMPPServiceXMPPThread(由于代码比较多,省的看着乱,我把servicethread类分开写了)。需要的权限如下:

 

    <!-- 访问Internet-->        

    <uses-permissionandroid:name="android.permission.INTERNET" />

    <!--- 访问网络状态 -->

    <uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- 往SDCard写入数据权限 -->

    <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <!-- 在SDCard中创建与删除文件权限 -->

    <uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

    <!-- 往SDCard写入数据权限 -->

    <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

 

这里记得要注册service,我当时就犯了这么个低级错误。说写的也对啊,debug怎么不往servicecreate里面进呢。

1MainActivity类是程序入口,因为消息提示在当前程序未执行的情况下也要提示,所以通过start()方式启动service,代码如下:

packagecom.neusoft.activity;

 

importandroid.app.Activity;

importandroid.content.Intent;

importandroid.os.Bundle;

importandroid.util.Log;

importandroid.view.Menu;

 

public classMainActivity extends Activity {

@Override

protectedvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Log.i("MainActivity","thread id is "+Thread.currentThread().getName());

Intentintent = new Intent(this,XMPPService.class);

startService(intent);

 

}

 

@Override

publicboolean onCreateOptionsMenu(Menu menu) {

//Inflate the menu; this adds items to the action bar if it is present.

getMenuInflater().inflate(R.menu.main,menu);

returntrue;

}

 

}

2XMPPService 类主要是为了一个新的进程,保证程序的进程未执行的情况下也能正常收到消息。现在的handleMessage方法只实现了通知的功能。后续可以根据不同的消息类型,增加操作。比如说:聊天啊,图片啊等等可以有不同的表现方式,都可以通过这一个service实现。

 

packagecom.neusoft.activity;

 

importandroid.app.Notification;

importandroid.app.NotificationManager;

importandroid.app.PendingIntent;

importandroid.app.Service;

importandroid.content.Context;

importandroid.content.Intent;

importandroid.net.Uri;

importandroid.os.Handler;

importandroid.os.IBinder;

importandroid.os.Message;

importandroid.util.Log;

 

public classXMPPService extends Service {

 

privateContext context = null;

privatestatic final String TAG = "XMPPService";

privatestatic final boolean RECIEVE_FLAG = true;

    private NotificationManager manager;

privateint i=0;

ThreadxmppThread = null;

 

@Override

publicvoid onCreate() {

Log.i(TAG,"service is created");

super.onCreate();

this.context= getApplicationContext();

manager= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

xmppThread= new Thread(new XMPPThread(mHandler));

xmppThread.start();

}

 

@Override

publicint onStartCommand(Intent intent, int flag,int startId) {

Log.i(TAG,"service is started. thread id is"+Thread.currentThread().getName());

returnsuper.onStartCommand(intent,flag, startId);

}

 

@Override

publicvoid onDestroy() {

Log.i(TAG,"service is destroy");

super.onDestroy();

}

 

@Override

publicIBinder onBind(Intent intent) {

returnnull;

}

 

 

privateHandler mHandler = new Handler(){ 

    @Override 

    public void handleMessage(Message msg){ 

        switch(msg.what){ 

        case 0: 

        { 

            if(RECIEVE_FLAG) {

  final org.jivesoftware.smack.packet.Messagemes = (org.jivesoftware.smack.packet.Message)msg.obj;

 System.out.println("来自:"+mes.getFrom()+"  消息内容:" + mes.getBody());

 

  //构建一个通知对象,指定了图标,标题,和时间

  Notification notification = newNotification(R.drawable.notification, "通知",System.currentTimeMillis());

  //TODO 处理消息

  //界面跳转

  Intent intent = newIntent(context,MainActivity.class);

  //消息重复接收关键

 intent.setData(Uri.parse("custom://"+System.currentTimeMillis()));

 

 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 

  intent.putExtra("Body",mes.getBody());                                

  intent.putExtra("From",mes.getFrom());

 

  //触发界面跳转

  PendingIntent    mPendingIntent =PendingIntent.getActivity(context, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT| PendingIntent.FLAG_ONE_SHOT);

  //消息栏

  notification.setLatestEventInfo(context,"您有新的消息", mes.getBody(),mPendingIntent);

  notification.flags =Notification.FLAG_AUTO_CANCEL;//点击后自动消失

  notification.defaults =Notification.DEFAULT_SOUND;//声音默认

  //消息叠加

  manager.notify(i, notification);

  i++;

  }

            break; 

        } 

        default: 

            break; 

        } 

    } 

}; 

}

 

3XMPPThread是线程类。因为请求后台,所以要单独起一个线程。Android serviceactivity一样,也是有主线程的,并且从android4.x开始吧,好像是,就不能在主线程做提交操作了,否则会报错。代码如下:

packagecom.neusoft.activity;

 

importorg.jivesoftware.smack.Connection;

importorg.jivesoftware.smack.ConnectionConfiguration;

importorg.jivesoftware.smack.PacketListener;

importorg.jivesoftware.smack.XMPPConnection;

importorg.jivesoftware.smack.XMPPException;

importorg.jivesoftware.smack.filter.PacketFilter;

importorg.jivesoftware.smack.filter.PacketTypeFilter;

importorg.jivesoftware.smack.packet.Packet;

 

importandroid.app.Notification;

importandroid.app.PendingIntent;

importandroid.content.Intent;

importandroid.net.Uri;

importandroid.os.Handler;

importandroid.os.Message;

importandroid.util.Log;

 

public classXMPPThread implements Runnable {

 

privateHandler handler = null;

privateConnection conn = null;

privatestatic final String TAG = "XMPPThread";

privatestatic final boolean RECIEVE_FLAG = true;

 

publicXMPPThread(Handler handler){

this.handler= handler;

}

 

@Override

publicvoid run() {

try{

this.conn= connect();

login(conn);

handleMessage();

}catch (XMPPException e) {

e.printStackTrace();

Log.e(TAG,e.getMessage(),e);

}

 

}

 

/**

 * 创建链接

 * @return

 * @throws XMPPException

 */

privateConnection connect() throws XMPPException{

ConnectionConfigurationconfig = new ConnectionConfiguration("192.168.175.227",5222);

 

config.setReconnectionAllowed(true);

config.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);

config.setSendPresence(true);

config.setSASLAuthenticationEnabled(false);

config.setRosterLoadedAtLogin(false);

 

//这块需要注掉,缺少xsmack的包

//                config.setCompressionEnabled(true);

//                config.setSASLAuthenticationEnabled(true);

Connectionconn = new XMPPConnection(config);

conn.connect();

returnconn;

}

 

/**

 * 登陆

 * @param conn

 * @return

 * @throws XMPPException

 */

privateboolean login(Connection conn) throws XMPPException{

conn.login("martin","1");

returntrue;

}

 

publicvoid handleMessage()

{

//消息接受包

PacketFilterfilter=new PacketTypeFilter(org.jivesoftware.smack.packet.Message.class);

//消息接受连接

PacketListenermyListener=new PacketListener(){

@SuppressWarnings("deprecation")

publicvoid processPacket(final Packet packet) {

  if(RECIEVE_FLAG) {

  Log.d(TAG, "接收到的消息体是:" +packet.toXML());

  final org.jivesoftware.smack.packet.Messagemes = (org.jivesoftware.smack.packet.Message)packet;

  Log.d(TAG,"来自:"+mes.getFrom()+" 消息内容:" + mes.getBody());

  Message msg = handler.obtainMessage(0,packet);

  handler.sendMessage(msg);

  }

}

};

conn.addPacketListener(myListener,filter);        

}

 

}

 

以上的完成后,进行测试,就会发现,在设备上,可以在通知栏看到如下图,点击可进入程序:


 

以上基本上就算是helloworld级的推送了,当然有很多可以完善的地方,比如说插件、用户管理,服务器重启后,客户端如何重新登录等等。


代码及openfire搭建缺少的jar包


http://download.youkuaiyun.com/detail/jy02196708/6763529


最让我气愤的是,做了如上的功课后,发现有现成的推送模型:androidpn,明天研究这货。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值