您的位置:首页 > 运维架构

openfire+asmock实现消息推送

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

参考资料:
http://blog.csdn.net/ares1201/article/details/7737872
http://blog.csdn.net/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)把下载的3个jar包coherence.jar、coherence-work.jar、tangosol.jar放到/openfire_src/build/lib下,并添加到build_path中;
4)使用eclipse的自动修复,把报的3个错误修复了,再把多出来的hazelcast包删掉(一下就能找到,全是错)。否则会跟另一个包有冲突;
5)打开ant窗口,把/openfire_src/build/build.xml拖入窗口,运行,系统会自动生成2个目录:target和work;
6)把ojdbc14.jar拷到/openfire_src/target/openfire/lib下(如果不使用oracle当数据库的不用这步)
7)配置启动设置:debug/run->configuration->javaapplication->new,如下配置:
4000

 

配置classpath,把i18n和resource资源加入

勾选run和debug

之后直接debug/run就可以直接运行了
8)使用浏览器打开localhost:9090,进入控制台,配置openfire
标准数据库->配置数据库连接->初始设置->管理员账号密码,之后就可以使用管理员账号密码登陆了。
9)这时,如果之前下载了spark,就可以连接上进行测试了,这里就不细写了。
10)加载broadcast插件:
进入控制台->插件->有效的插件->broadcast
这个插件的主要目的是进行群发(或者叫广播?反正我觉得是看客户端怎么实现的了)。
11)注册测试用户。我这里注册了个chris和martin,密码都是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个类:MainActivity、XMPPService和XMPPThread(由于代码比较多,省的看着乱,我把service和thread类分开写了)。需要的权限如下:
 
    <!-- 访问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怎么不往service的create里面进呢。
1)MainActivity类是程序入口,因为消息提示在当前程序未执行的情况下也要提示,所以通过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;

}

 
}
2)XMPPService
类主要是为了一个新的进程,保证程序的进程未执行的情况下也能正常收到消息。现在的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; 

        } 

    } 

}; 
}
 
3)XMPPThread是线程类。因为请求后台,所以要单独起一个线程。Android
的service好activity一样,也是有主线程的,并且从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.csdn.net/detail/jy02196708/6763529

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

参考资料:
http://blog.csdn.net/ares1201/article/details/7737872
http://blog.csdn.net/shimiso/article/details/11225873
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  openfire helloworld