(转)OpenFire源码学习之十八:IOS离线推送

转:http://blog.csdn.net/huwenfeng_2011/article/details/43458213

IOS离线推送

场景:

如果您有iOS端的APP,在会话聊天的时候,用户登陆了但可能会退出了界面。这时候其他终端给目标端发送消息时候,消息可以发送到ios的推送服务器。用过QQ的都知道,你会有哦一条消息在您的主屏上展示。这个就是利用了IOS的推送服务器呢。那么openfire只需要判断用户不在线的时候将消息推送给IOS端。

苹果服务器的消息推送都需要手机的唯一标志,也就是唯一的终端设备号。那么IOS端在登陆的时候需要将该手机的设备号传递给OF服务器。这个传递很简单,您可以自定义发送IQ消息。也可以在登陆后绑定资料的时候添加JID属性来绑定设备。(建议用绑定资源的形式,这样在服务端判断的时候可以很方便的根据JID的属性值来决定是都推送)

 服务端实现ios消息推送所需2个证书(附件):测试推送证书.p12、正式推送正式.p12,密码都为123456.

 2个证书的区别在于一个是用于开发测试的推送证书,一个是用于产品正式上线的推送证书。2个证书获取到的终端token是不一样的。这2个证书用于Java后台连接APNS的服务器地址也是不同的,测试推送证书对应服务器地址是:gateway.sandbox.push.apple.com , 正式推送证书对应的服务器地址是:gateway.push.apple.com .

具体怎么做呢:

1、安装IOS推送服务需要的证书到本地,这个在网上有很多中方法

2、IOS终端登陆发送设备消息给服务器,或者以绑定资源的形式。

3、OF服务端接收该设备ID,并保存起来。

4、当有消息推送时候,根据JID属性push消息。

接下来具体看看源码了。

源码

OfflinePushPlugin

[java] view plain copy
 
  1. public class OfflinePushPlugin implements Component, Plugin, PropertyEventListener, PacketInterceptor{  
  2.   
  3.       
  4.     private static final Logger Log = LoggerFactory.getLogger(OfflinePushPlugin.class);  
  5.       
  6.     public static final String NAMESPACE_JABBER_IQ_TOKEN_BIND= "jabber:iq:token:bind";  
  7.     public static final String NAMESPACE_JABBER_IQ_TOKEN_UNBUND= "jabber:iq:token:unbund";  
  8.       
  9.     public static final String SERVICENAME = "plugin.offlinepush.serviceName";  
  10.     public static final String SERVICEENABLED = "plugin.offlinepush.serviceEnabled";  
  11.       
  12.     private ComponentManager componentManager;  
  13.     private PluginManager pluginManager;  
  14.   
  15.     private String serviceName;  
  16. private boolean serviceEnabled;  
  17. //证书安装的目录  
  18.     private static String dcpath = System.getProperty("openfireHome") + "\conf\";  
  19.     private String dcName;  
  20.     private String dcPassword;  
  21.     private boolean enabled;  
  22.       
  23.     private static Map<String, String> map = new ConcurrentHashMap<String, String>(20);  
  24.     private static Map<String, Integer> count = new ConcurrentHashMap<String, Integer>(20);  
  25.       
  26.     private static AppleNotificationServer appleServer = null;  
  27.     private static List<PayloadPerDevice> list ;  
  28.       
  29.     public String getDcName() {  
  30.         return dcName;  
  31.     }  
  32.   
  33.     public void setDcName(String dcName) {  
  34.         JiveGlobals.setProperty("plugin.offlinepush.dcName", dcName);  
  35.         this.dcName = dcName;  
  36.     }  
  37.   
  38.     public String getDcPassword() {  
  39.         return dcPassword;  
  40.     }  
  41.   
  42.     public void setDcPassword(String dcPassword) {  
  43.         JiveGlobals.setProperty("plugin.offlinepush.password", dcPassword);  
  44.         this.dcPassword = dcPassword;  
  45.     }  
  46.   
  47.     public boolean getEnabled() {  
  48.         return enabled;  
  49.     }  
  50.   
  51.     public void setEnabled(boolean enabled) {  
  52.         this.enabled = enabled;  
  53.         JiveGlobals.setProperty("plugin.offlinepush.enabled",  enabled ? "true" : "false");  
  54.     }  
  55.   
  56.     public OfflinePushPlugin () {  
  57.         serviceName = JiveGlobals.getProperty(SERVICENAME, "offlinepush");  
  58.         serviceEnabled = JiveGlobals.getBooleanProperty(SERVICEENABLED, true);  
  59.     }  
  60.   
  61.     @Override  
  62.     public void xmlPropertySet(String property, Map<String, Object> params) {  
  63.           
  64.     }  
  65.   
  66.     @Override  
  67.     public void xmlPropertyDeleted(String property, Map<String, Object> params) {  
  68.     }  
  69.   
  70.     @Override  
  71.     public void initializePlugin(PluginManager manager, File pluginDirectory) {  
  72.           
  73.         dcName = JiveGlobals.getProperty("plugin.offlinepush.dcName", "");  
  74.         // If no secret key has been assigned to the user service yet, assign a random one.  
  75.         if (dcName.equals("")){  
  76.             dcName = "delementtest.p12";  
  77.             setDcName(dcName);  
  78.         }  
  79.         dcpath += dcName;  
  80.         dcPassword = JiveGlobals.getProperty("plugin.offlinepush.password", "");  
  81.         if (dcPassword.equals("")){  
  82.             dcPassword = "123456";  
  83.             setDcPassword(dcPassword);  
  84.         }  
  85.           
  86.         enabled = JiveGlobals.getBooleanProperty("plugin.offlinepush.enabled");  
  87.         setEnabled(enabled);  
  88.           
  89.         Log.info("dcpath: " + dcpath);  
  90.         Log.info("dcPassword: " + dcPassword);  
  91.         Log.info("enabled: " + enabled);  
  92.           
  93.         try {  
  94.             appleServer = new AppleNotificationServerBasicImpl(dcpath, dcPassword, enabled );  
  95.             if (list == null ) {  
  96.                 list = new ArrayList<PayloadPerDevice>();  
  97.             }  
  98.               
  99.         } catch (KeystoreException e1) {  
  100.             Log.error("KeystoreException: " + e1.getMessage());  
  101.         }   
  102.           
  103.         pluginManager = manager;  
  104.           
  105.         componentManager = ComponentManagerFactory.getComponentManager();  
  106.         try {  
  107.             componentManager.addComponent(serviceName, this);  
  108.         }  
  109.         catch (ComponentException e) {  
  110.             Log.error(e.getMessage(), e);  
  111.         }  
  112.           
  113.         InterceptorManager.getInstance().addInterceptor(this);  
  114.         PropertyEventDispatcher.addListener(this);  
  115.           
  116.     }  
  117.   
  118.     @Override  
  119.     public void destroyPlugin() {  
  120.         InterceptorManager.getInstance().removeInterceptor(this);  
  121.         PropertyEventDispatcher.removeListener(this);  
  122.         pluginManager = null;  
  123.         try {  
  124.             componentManager.removeComponent(serviceName);  
  125.             componentManager = null;  
  126.         }  
  127.         catch (Exception e) {  
  128.             if (componentManager != null) {  
  129.                 Log.error(e.getMessage(), e);  
  130.             }  
  131.         }  
  132.         serviceName = null;  
  133.     }  
  134.   
  135.     @Override  
  136.     public String getName() {  
  137.         return pluginManager.getName(this);  
  138.     }  
  139.   
  140.     @Override  
  141.     public String getDescription() {  
  142.         return pluginManager.getDescription(this);  
  143.     }  
  144.   
  145.     @Override  
  146.     public void processPacket(Packet p) {  
  147.         if (!(p instanceof IQ)) {  
  148.             return;  
  149.         }  
  150.         final IQ packet = (IQ) p;  
  151.   
  152.         if (packet.getType().equals(IQ.Type.error)  
  153.                 || packet.getType().equals(IQ.Type.result)) {  
  154.             return;  
  155.         }  
  156.         final IQ replyPacket = handleIQRequest(packet);  
  157.   
  158.         try {  
  159.             componentManager.sendPacket(this, replyPacket);  
  160.         } catch (ComponentException e) {  
  161.             Log.error(e.getMessage(), e);  
  162.         }  
  163.     }  
  164.   
  165.     private IQ handleIQRequest(IQ iq) {  
  166.         final IQ replyPacket; // 'final' to ensure that it is set.  
  167.   
  168.         if (iq == null) {  
  169.             throw new IllegalArgumentException("Argument 'iq' cannot be null.");  
  170.         }  
  171.   
  172.         final IQ.Type type = iq.getType();  
  173.         if (type != IQ.Type.get && type != IQ.Type.set) {  
  174.             throw new IllegalArgumentException(  
  175.                     "Argument 'iq' must be of type 'get' or 'set'");  
  176.         }  
  177.   
  178.         final Element childElement = iq.getChildElement();  
  179.         if (childElement == null) {  
  180.             replyPacket = IQ.createResultIQ(iq);  
  181.             replyPacket  
  182.                     .setError(new PacketError(  
  183.                             Condition.bad_request,  
  184.                             org.xmpp.packet.PacketError.Type.modify,  
  185.                             "IQ stanzas of type 'get' and 'set' MUST contain one and only one child element (RFC 3920 section 9.2.3)."));  
  186.             return replyPacket;  
  187.         }  
  188.   
  189.         final String namespace = childElement.getNamespaceURI();  
  190.         if (namespace == null) {  
  191.             replyPacket = IQ.createResultIQ(iq);  
  192.             replyPacket.setError(Condition.feature_not_implemented);  
  193.             return replyPacket;  
  194.         }  
  195.   
  196.         if (namespace.equals(NAMESPACE_JABBER_IQ_TOKEN_BIND)) {  
  197.             replyPacket = processSetUUID(iq, true);  
  198.         }   
  199.         else if (namespace.equals(NAMESPACE_JABBER_IQ_TOKEN_UNBUND)) {  
  200.             replyPacket = processSetUUID(iq, false);  
  201.         }   
  202.         else if (namespace.equals(IQDiscoInfoHandler.NAMESPACE_DISCO_INFO)) {  
  203.             replyPacket = handleDiscoInfo(iq);  
  204.         }   
  205.         else {  
  206.             // don't known what to do with this.  
  207.             replyPacket = IQ.createResultIQ(iq);  
  208.             replyPacket.setError(Condition.feature_not_implemented);  
  209.         }  
  210.   
  211.         return replyPacket;  
  212.     }  
  213.       
  214.     private static IQ handleDiscoInfo(IQ iq) {  
  215.         if (iq == null) {  
  216.             throw new IllegalArgumentException("Argument 'iq' cannot be null.");  
  217.         }  
  218.   
  219.         if (!iq.getChildElement().getNamespaceURI().equals(  
  220.                 IQDiscoInfoHandler.NAMESPACE_DISCO_INFO)  
  221.                 || iq.getType() != Type.get) {  
  222.             throw new IllegalArgumentException(  
  223.                     "This is not a valid disco#info request.");  
  224.         }  
  225.   
  226.         final IQ replyPacket = IQ.createResultIQ(iq);  
  227.   
  228.         final Element responseElement = replyPacket.setChildElement("query",  
  229.                 IQDiscoInfoHandler.NAMESPACE_DISCO_INFO);  
  230.         responseElement.addElement("identity").addAttribute("category",  
  231.                 "directory").addAttribute("type", "user").addAttribute("name",  
  232.                 "Offline Push");  
  233.         responseElement.addElement("feature").addAttribute("var",  
  234.                 NAMESPACE_JABBER_IQ_TOKEN_BIND);  
  235.         responseElement.addElement("feature").addAttribute("var",  
  236.                 IQDiscoInfoHandler.NAMESPACE_DISCO_INFO);  
  237.         responseElement.addElement("feature").addAttribute("var",  
  238.                 ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT);  
  239.   
  240.         return replyPacket;  
  241.     }  
  242.       
  243.     private IQ processSetUUID(IQ packet, boolean isSet) {  
  244.         Element rsmElement = null;  
  245.         if (!packet.getType().equals(IQ.Type.set)) {  
  246.             throw new IllegalArgumentException(  
  247.                     "This method only accepts 'set' typed IQ stanzas as an argument.");  
  248.         }  
  249.   
  250.         final IQ resultIQ;  
  251.   
  252.         final Element incomingForm = packet.getChildElement();  
  253.   
  254.         rsmElement = incomingForm.element(QName.get("info",  
  255.                 NAMESPACE_JABBER_IQ_TOKEN_UNBUND));  
  256.           
  257.         if(rsmElement == null) {  
  258.             rsmElement = incomingForm.element(QName.get("info",  
  259.                     NAMESPACE_JABBER_IQ_TOKEN_BIND));  
  260.         }  
  261.   
  262.         resultIQ = IQ.createResultIQ(packet);  
  263.         if (rsmElement != null) {  
  264.             String osElement = rsmElement.attributeValue("os");  
  265.             String jidElement = rsmElement.attributeValue("jid");  
  266.               
  267.             String username = new JID(jidElement).getNode();  
  268.            
  269.             if (osElement == null || jidElement == null) {  
  270.                 resultIQ.setError(Condition.bad_request);  
  271.                 return resultIQ;  
  272.             }  
  273.             if (isSet) {  
  274.                 String tokenElement = rsmElement.attributeValue("token");  
  275.                 map.put(username, tokenElement);  
  276.                 count.put(username, 0);  
  277.                 Log.info("set token,username:" + username + " ,token:" + tokenElement);  
  278.             }  
  279.             else {  
  280.                 map.remove(username);  
  281.                 count.remove(username);  
  282.                 Log.info("remove token,username:" + username );  
  283.             }  
  284.         }   
  285.         else{  
  286.             resultIQ.setError(Condition.bad_request);  
  287.         }  
  288.   
  289.         return resultIQ;  
  290.     }  
  291.       
  292.     public String getServiceName() {  
  293.         return serviceName;  
  294.     }  
  295.       
  296.     public void setServiceName(String name) {  
  297.         JiveGlobals.setProperty(SERVICENAME, name);  
  298.     }  
  299.       
  300.     public boolean getServiceEnabled() {  
  301.         return serviceEnabled;  
  302.     }  
  303.       
  304.     public void setServiceEnabled(boolean enabled) {  
  305.         serviceEnabled = enabled;  
  306.         JiveGlobals.setProperty(SERVICEENABLED, enabled ? "true" : "false");  
  307.     }  
  308.       
  309.     public void propertySet(String property, Map<String, Object> params) {  
  310.         if (property.equals(SERVICEENABLED)) {  
  311.             this.serviceEnabled = Boolean.parseBoolean((String)params.get("value"));  
  312.         }  
  313.         if (property.equals("plugin.offlinepush.dcName")) {  
  314.             this.dcName = (String)params.get("value");  
  315.         }  
  316.         else if (property.equals("plugin.offlinepush.enabled")) {  
  317.             this.enabled = Boolean.parseBoolean((String)params.get("value"));  
  318.         }  
  319.         else if (property.equals("plugin.offlinepush.password")) {  
  320.             this.dcPassword = (String)params.get("value");  
  321.         }  
  322.     }  
  323.   
  324.     /* 
  325.      * (non-Javadoc) 
  326.      *  
  327.      * @see org.jivesoftware.util.PropertyEventListener#propertyDeleted(java.lang.String, 
  328.      *      java.util.Map) 
  329.      */  
  330.     public void propertyDeleted(String property, Map<String, Object> params) {  
  331.         if (property.equals(SERVICEENABLED)) {  
  332.             this.serviceEnabled = true;  
  333.         }  
  334.         if (property.equals("plugin.offlinepush.dcName")) {  
  335.             this.dcName = "delementtest.p12";  
  336.         }  
  337.         else if (property.equals("plugin.offlinepush.enabled")) {  
  338.             this.enabled = false;  
  339.         }  
  340.         else if (property.equals("plugin.offlinepush.password")) {  
  341.             this.dcPassword = "123456";  
  342.         }  
  343.     }  
  344.       
  345.     @Override  
  346.     public void initialize(JID jid, ComponentManager componentManager)  
  347.             throws ComponentException {  
  348.         // TODO Auto-generated method stub  
  349.           
  350.     }  
  351.   
  352.     @Override  
  353.     public void start() {  
  354.         // TODO Auto-generated method stub  
  355.           
  356.     }  
  357.   
  358.     @Override  
  359.     public void shutdown() {  
  360.         // TODO Auto-generated method stub  
  361.           
  362.     }  
  363.   
  364.     @Override  
  365.     public void interceptPacket(Packet packet, Session session,  
  366.             boolean incoming, boolean processed) throws PacketRejectedException {  
  367.         if (processed && incoming) {  
  368.             if (packet instanceof Message) {  
  369.                 if (((Message) packet).getBody() == null) {  
  370.                     return;  
  371.                 }  
  372.                 JID jid = packet.getTo();  
  373.                 //获取用户的设备标志id  
  374.                 String uuid = map.get(jid.getNode());  
  375.                 if (uuid != null && !"".equals(uuid)) {  
  376.                     User user = null;  
  377.                     try {  
  378.                         user = XMPPServer.getInstance().getUserManager().getUser(jid.getNode());  
  379.                     } catch (UserNotFoundException e2) {  
  380.                         e2.printStackTrace();  
  381.                     }  
  382.                     PresenceManager presenceManager = XMPPServer.getInstance().getPresenceManager();  
  383.                     org.xmpp.packet.Presence presence = presenceManager.getPresence(user);  
  384.                     if (presence == null) {  
  385.                           
  386.                         String body = ((Message) packet).getBody();  
  387.                         JSONObject jb = null;  
  388.                         String msgType = "10015";  
  389.                         try {  
  390.    
  391.                             jb = new JSONObject(body);  
  392.                             msgType = jb.getString("msgType");  
  393.                             if ("10012".equals(msgType) || "10001".equals(msgType) || "10002".equals(msgType)) {  
  394.                                 return;  
  395.                             }  
  396.                         } catch (JSONException e) {  
  397.                             try {  
  398.                               //根据不同的消息类型,发送不通的提示语  
  399.                                 msgType = jb.getInt("msgType")+"";  
  400.                                 if ("10012".equals(msgType) || "10001".equals(msgType) || "10002".equals(msgType)) {  
  401.                                     return;  
  402.                                 }  
  403.                             } catch (JSONException e1) {  
  404.                                 msgType = "10015";  
  405.                             }  
  406.                         }  
  407.                         if (msgType != null) {  
  408.                             //msgType = "offlinepush." + msgType;  
  409.                             String pushCont = LocaleUtils.getLocalizedString("offlinepush.10015", "offlinepush");  
  410.                             if (!"10000".equals(msgType)) {  
  411.                                 msgType = "offlinepush." + msgType;  
  412.                                 pushCont = LocaleUtils.getLocalizedString(msgType, "offlinepush");  
  413.                             }  
  414.                             else {  
  415.                                 pushCont = LocaleUtils.getLocalizedString("offlinepush.10000", "offlinepush");  
  416.                                 String cont = LocaleUtils.getLocalizedString("offlinepush.other", "offlinepush");;  
  417.                                 String mtype  = "";  
  418.                                 try {  
  419.                                     mtype = jb.getString("mtype");  
  420.                                 } catch (JSONException e) {  
  421.                                     try {  
  422.                                         mtype = jb.getInt("mtype") + "";  
  423.                                     } catch (JSONException e1) {  
  424.                                         msgType = "10015";  
  425.                                     }  
  426.                                 }  
  427.                                 if ("0".equals(mtype)) {  
  428.                                     try {  
  429.                                         cont = jb.getString("Cnt");  
  430.                                         if (cont.length() > 20) {  
  431.                                             cont = cont.substring(0, 20);  
  432.                                             cont += "...";  
  433.                                         }  
  434.                                           
  435.                                     } catch (JSONException e) {  
  436.                                     }  
  437.                                       
  438.                                               
  439.                                 }  
  440.                                 else if ("1".equals(mtype)) {  
  441.                                     cont = LocaleUtils.getLocalizedString("offlinepush.image", "offlinepush");   
  442.                                 }  
  443.                                 else if ("2".equals(mtype)) {  
  444.                                     cont = LocaleUtils.getLocalizedString("offlinepush.audio", "offlinepush");   
  445.                                 }  
  446.                                 else if ("4".equals(mtype)) {  
  447.                                     cont = LocaleUtils.getLocalizedString("offlinepush.file", "offlinepush");   
  448.                                 }  
  449.                                 else if ("3".equals(mtype)) {  
  450.                                     cont = LocaleUtils.getLocalizedString("offlinepush.location", "offlinepush");   
  451.                                 }  
  452.                                 else if ("6".equals(mtype)) {  
  453.                                     cont = LocaleUtils.getLocalizedString("offlinepush.video", "offlinepush");  
  454.                                 }  
  455.                                 pushCont += cont;  
  456.                             }  
  457.                             pushOfflineMsg(uuid, pushCont, jid);  
  458.                         }  
  459.                     }  
  460.                 }  
  461.             }  
  462.         }  
  463.           
  464.     }  
  465.   
  466.     private void pushOfflineMsg(String token, String pushCont, JID jid) {  
  467.             NotificationThreads work = null;  
  468.         try {  
  469.               
  470.             Integer size = count.get(jid.getNode()) + 1;   
  471.             if (size <= 1000)  
  472.                 count.put(jid.getNode(), size);  
  473.             List<PayloadPerDevice> list = new ArrayList<PayloadPerDevice>();  
  474.             PushNotificationPayload payload = new PushNotificationPayload();   
  475.             payload.addAlert(pushCont);  
  476.             payload.addSound("default");   
  477.             payload.addBadge(size);  
  478.             payload.addCustomDictionary("jid", jid.toString());  
  479.             PayloadPerDevice pay = new PayloadPerDevice(payload, token);  
  480.             list.add(pay);   
  481.             work = new NotificationThreads(appleServer,list,1);  
  482.             work.setListener(DEBUGGING_PROGRESS_LISTENER);  
  483.             work.start();   
  484.         } catch (JSONException e) {  
  485.             Log.error("JSONException:" + e.getMessage());  
  486.         } catch (InvalidDeviceTokenFormatException e) {  
  487.             Log.error("InvalidDeviceTokenFormatException:" + e.getMessage());  
  488.         }finally{  
  489.             work.destroy();  
  490.             Log.info("push to apple: username: " + jid.getNode() + " ,context" + pushCont);  
  491.         }  
  492.     }  
  493.       
  494.     public Runnable createTask(final String token, final String msgType, final JID jid) {  
  495.         return new Runnable() {  
  496.             @Override  
  497.             public void run() {  
  498.                 PushNotificationPayload payload = new PushNotificationPayload();    
  499.                 try {  
  500.                     String pushCont = LocaleUtils.getLocalizedString(msgType, "offlinepush");  
  501.                     List<PayloadPerDevice> list = new ArrayList<PayloadPerDevice>();  
  502.                     payload.addAlert(pushCont);  
  503.                     payload.addSound("default");   
  504.                     payload.addBadge(1);  
  505.                     payload.addCustomDictionary("jid", jid.toString());  
  506.                     PayloadPerDevice pay = new PayloadPerDevice(payload, token);  
  507.                     list.add(pay);   
  508.                     NotificationThreads work = new NotificationThreads(appleServer,list,1);  
  509.                     work.setListener(DEBUGGING_PROGRESS_LISTENER);  
  510.                     work.start();   
  511.                 } catch (JSONException e) {  
  512.                     Log.error("JSONException:" + e.getMessage());  
  513.                 } catch (InvalidDeviceTokenFormatException e) {  
  514.                     Log.error("InvalidDeviceTokenFormatException:" + e.getMessage());  
  515.                 }   
  516.             }  
  517.         };  
  518.           
  519.     }  
  520.       
  521.     public static final NotificationProgressListener DEBUGGING_PROGRESS_LISTENER = new NotificationProgressListener() {    
  522.         public void eventThreadStarted(NotificationThread notificationThread) {    
  523.             System.out.println("   [EVENT]: thread #" + notificationThread.getThreadNumber() + " started with " + " devices beginning at message id #" + notificationThread.getFirstMessageIdentifier());    
  524.         }    
  525.         public void eventThreadFinished(NotificationThread thread) {    
  526.             System.out.println("   [EVENT]: thread #" + thread.getThreadNumber() + " finished: pushed messages #" + thread.getFirstMessageIdentifier() + " to " + thread.getLastMessageIdentifier() + " toward "+ " devices");    
  527.         }    
  528.         public void eventConnectionRestarted(NotificationThread thread) {    
  529.             System.out.println("   [EVENT]: connection restarted in thread #" + thread.getThreadNumber() + " because it reached " + thread.getMaxNotificationsPerConnection() + " notifications per connection");    
  530.         }    
  531.         public void eventAllThreadsStarted(NotificationThreads notificationThreads) {    
  532.             System.out.println("   [EVENT]: all threads started: " + notificationThreads.getThreads().size());    
  533.         }    
  534.         public void eventAllThreadsFinished(NotificationThreads notificationThreads) {    
  535.             System.out.println("   [EVENT]: all threads finished: " + notificationThreads.getThreads().size());    
  536.         }    
  537.         public void eventCriticalException(NotificationThread notificationThread, Exception exception) {    
  538.             System.out.println("   [EVENT]: critical exception occurred: " + exception);    
  539.         }    
  540.      };   
  541. }  

Plugin.xml

[html] view plain copy
 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2.   
  3. <plugin>  
  4.     <class>com.....offlinepush.plugin.OfflinePushPlugin</class>  
  5.     <name>offlinepush</name>  
  6.     <description>.......</description>  
  7.     <author>huwenfeng</author>  
  8.     <version>1.5.1</version>  
  9.     <date>1/2/2014</date>  
  10.     <minServerVersion>3.7.0</minServerVersion>  
  11. </plugin>  

资源文件:offlinepush_i18n_zh_CN.properties

[html] view plain copy
 
  1. offlinepush.10000=u65B0u6D88u606FuFF1A  
  2. offlinepush.10001=u7528u6237u64CDu4F5C  
  3. offlinepush.image=[u56FEu7247]  
  4. offlinepush.audio=[u8BEDu97F3]  
  5. offlinepush.file=[u6587u4EF6]  
  6. offlinepush.other=[u5176u4ED6]  
  7. offlinepush.location=[u4F4Du7F6E]  
  8. offlinepush.video=[u89C6u9891]  
  9. ......   

需要的jar包。

OK啦。

注意:IOS的推送服务器有两种模式都是免费,一种是测试的还一种是正式使用的。

所以这里最好将推送服务的使用模式在OF的管理台做配置。

本人在控制台配置了三个属性值:

原文地址:https://www.cnblogs.com/wangle1001986/p/7249473.html