精华内容
参与话题
问答
  • SpringBoot2.0集成WebSocket,实现后台向前端推送信息

    万次阅读 多人点赞 2018-05-10 22:54:29
    什么是WebSocket? - 初次接触 WebSocket 的人,都会问同样的问题:我们...它能带来什么好处? - 答案很简单,因为 HTTP 协议有一个缺陷:***通信只能由客户端发起***,HTTP 协议做不到服务器主动向客户端推送信息。 ...

    什么是WebSocket?

    这里写图片描述
    WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。

    为什么需要 WebSocket?

    初次接触 WebSocket 的人,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?

    • 答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起,HTTP 协议做不到服务器主动向客户端推送信息。
      这里写图片描述
      举例来说,我们想要查询当前的排队情况,只能是页面轮询向服务器发出请求,服务器返回查询结果。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此WebSocket 就是这样发明的。

    前言

    2020-10-20 教程补充:

    • 补充关于@Component@ServerEndpoint关于是否单例模式等的解答,感谢大家热心提问和研究。
    • Vue版本的websocket连接方法

    2020-01-05 教程补充:

    感谢大家的支持和留言,14W访问量是满满的动力!接下来还会有websocket+redis集群优化篇针对多ws服务器做简单优化处理,敬请期待!

    话不多说,马上进入干货时刻。

    maven依赖

    SpringBoot2.0对WebSocket的支持简直太棒了,直接就有包可以引入

    		<dependency>  
               <groupId>org.springframework.boot</groupId>  
               <artifactId>spring-boot-starter-websocket</artifactId>  
           </dependency> 
    

    WebSocketConfig

    启用WebSocket的支持也是很简单,几句代码搞定

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;
    
    /**
     * 开启WebSocket支持
     * @author zhengkai.blog.csdn.net
     */
    @Configuration  
    public class WebSocketConfig {  
    	
        @Bean  
        public ServerEndpointExporter serverEndpointExporter() {  
            return new ServerEndpointExporter();  
        }  
      
    } 
    

    WebSocketServer

    这就是重点了,核心都在这里。

    1. 因为WebSocket是类似客户端服务端的形式(采用ws协议),那么这里的WebSocketServer其实就相当于一个ws协议的Controller

    2. 直接@ServerEndpoint("/imserver/{userId}")@Component启用即可,然后在里面实现@OnOpen开启连接,@onClose关闭连接,@onMessage接收消息等方法。

    3. 新建一个ConcurrentHashMap webSocketMap 用于接收当前userId的WebSocket,方便IM之间对userId进行推送消息。单机版实现到这里就可以。

    4. 集群版(多个ws节点)还需要借助mysql或者redis等进行处理,改造对应的sendMessage方法即可。

    package com.softdev.system.demo.config;
    
    import java.io.IOException;
    import java.util.concurrent.ConcurrentHashMap;
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.stereotype.Component;
    import cn.hutool.log.Log;
    import cn.hutool.log.LogFactory;
    
    
    /**
     * @author zhengkai.blog.csdn.net
     */
    @ServerEndpoint("/imserver/{userId}")
    @Component
    public class WebSocketServer {
    
        static Log log=LogFactory.get(WebSocketServer.class);
        /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
        private static int onlineCount = 0;
        /**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
        private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
        /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
        private Session session;
        /**接收userId*/
        private String userId="";
    
        /**
         * 连接建立成功调用的方法*/
        @OnOpen
        public void onOpen(Session session,@PathParam("userId") String userId) {
            this.session = session;
            this.userId=userId;
            if(webSocketMap.containsKey(userId)){
                webSocketMap.remove(userId);
                webSocketMap.put(userId,this);
                //加入set中
            }else{
                webSocketMap.put(userId,this);
                //加入set中
                addOnlineCount();
                //在线数加1
            }
    
            log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount());
    
            try {
                sendMessage("连接成功");
            } catch (IOException e) {
                log.error("用户:"+userId+",网络异常!!!!!!");
            }
        }
    
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose() {
            if(webSocketMap.containsKey(userId)){
                webSocketMap.remove(userId);
                //从set中删除
                subOnlineCount();
            }
            log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());
        }
    
        /**
         * 收到客户端消息后调用的方法
         *
         * @param message 客户端发送过来的消息*/
        @OnMessage
        public void onMessage(String message, Session session) {
            log.info("用户消息:"+userId+",报文:"+message);
            //可以群发消息
            //消息保存到数据库、redis
            if(StringUtils.isNotBlank(message)){
                try {
                    //解析发送的报文
                    JSONObject jsonObject = JSON.parseObject(message);
                    //追加发送人(防止串改)
                    jsonObject.put("fromUserId",this.userId);
                    String toUserId=jsonObject.getString("toUserId");
                    //传送给对应toUserId用户的websocket
                    if(StringUtils.isNotBlank(toUserId)&&webSocketMap.containsKey(toUserId)){
                        webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString());
                    }else{
                        log.error("请求的userId:"+toUserId+"不在该服务器上");
                        //否则不在这个服务器上,发送到mysql或者redis
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    
        /**
         *
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
            log.error("用户错误:"+this.userId+",原因:"+error.getMessage());
            error.printStackTrace();
        }
        /**
         * 实现服务器主动推送
         */
        public void sendMessage(String message) throws IOException {
            this.session.getBasicRemote().sendText(message);
        }
    
    
        /**
         * 发送自定义消息
         * */
        public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
            log.info("发送消息到:"+userId+",报文:"+message);
            if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
                webSocketMap.get(userId).sendMessage(message);
            }else{
                log.error("用户"+userId+",不在线!");
            }
        }
    
        public static synchronized int getOnlineCount() {
            return onlineCount;
        }
    
        public static synchronized void addOnlineCount() {
            WebSocketServer.onlineCount++;
        }
    
        public static synchronized void subOnlineCount() {
            WebSocketServer.onlineCount--;
        }
    }
    

    消息推送

    至于推送新信息,可以再自己的Controller写个方法调用WebSocketServer.sendInfo();即可

    
    import com.softdev.system.demo.config.WebSocketServer;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.servlet.ModelAndView;
    import java.io.IOException;
    
    /**
     * WebSocketController
     * @author zhengkai.blog.csdn.net
     */
    @RestController
    public class DemoController {
    
        @GetMapping("index")
        public ResponseEntity<String> index(){
            return ResponseEntity.ok("请求成功");
        }
    
        @GetMapping("page")
        public ModelAndView page(){
            return new ModelAndView("websocket");
        }
    
        @RequestMapping("/push/{toUserId}")
        public ResponseEntity<String> pushToWeb(String message, @PathVariable String toUserId) throws IOException {
            WebSocketServer.sendInfo(message,toUserId);
            return ResponseEntity.ok("MSG SEND SUCCESS");
        }
    }
    
    

    页面发起

    页面用js代码调用websocket,当然,太古老的浏览器是不行的,一般新的浏览器或者谷歌浏览器是没问题的。还有一点,记得协议是ws的,如果使用了一些路径类,可以replace(“http”,“ws”)来替换协议。

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>websocket通讯</title>
    </head>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script>
        var socket;
        function openSocket() {
            if(typeof(WebSocket) == "undefined") {
                console.log("您的浏览器不支持WebSocket");
            }else{
                console.log("您的浏览器支持WebSocket");
                //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
                //等同于socket = new WebSocket("ws://localhost:8888/xxxx/im/25");
                //var socketUrl="${request.contextPath}/im/"+$("#userId").val();
                var socketUrl="http://localhost:9999/demo/imserver/"+$("#userId").val();
                socketUrl=socketUrl.replace("https","ws").replace("http","ws");
                console.log(socketUrl);
                if(socket!=null){
                    socket.close();
                    socket=null;
                }
                socket = new WebSocket(socketUrl);
                //打开事件
                socket.onopen = function() {
                    console.log("websocket已打开");
                    //socket.send("这是来自客户端的消息" + location.href + new Date());
                };
                //获得消息事件
                socket.onmessage = function(msg) {
                    console.log(msg.data);
                    //发现消息进入    开始处理前端触发逻辑
                };
                //关闭事件
                socket.onclose = function() {
                    console.log("websocket已关闭");
                };
                //发生了错误事件
                socket.onerror = function() {
                    console.log("websocket发生了错误");
                }
            }
        }
        function sendMessage() {
            if(typeof(WebSocket) == "undefined") {
                console.log("您的浏览器不支持WebSocket");
            }else {
                console.log("您的浏览器支持WebSocket");
                console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
                socket.send('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
            }
        }
    </script>
    <body>
    <p>【userId】:<div><input id="userId" name="userId" type="text" value="10"></div>
    <p>【toUserId】:<div><input id="toUserId" name="toUserId" type="text" value="20"></div>
    <p>【toUserId】:<div><input id="contentText" name="contentText" type="text" value="hello websocket"></div>
    <p>【操作】:<div><a onclick="openSocket()">开启socket</a></div>
    <p>【操作】:<div><a onclick="sendMessage()">发送消息</a></div>
    </body>
    
    </html>
    

    运行效果

    • v20200105,加入开源项目spring-cloud-study-websocket,更新运行效果,更方便理解。
    • v1.1的效果,刚刚修复了日志,并且支持指定监听某个端口,代码已经全部更新,现在是这样的效果
    1. 打开两个页面,按F12调出控控制台查看测试效果:
    页面 参数
    http://localhost:9999/demo/page fromUserId=10,toUserId=20
    http://localhost:9999/demo/page fromUserId=20,toUserId=10

    分别开启socket,再发送消息
    在这里插入图片描述
    在这里插入图片描述
    2. 向前端推送数据:

    • http://localhost:9999/demo/push/10?message=123123

    在这里插入图片描述
    通过调用push api,可以向指定的userId推送信息,当然报文这里乱写,建议规定好格式。

    后续

    针对简单IM的业务场景,进行了一些优化,可以看后续的文章SpringBoot2+WebSocket之聊天应用实战(优化版本)(v20201005已整合)

    主要变动是CopyOnWriteArraySet改为ConcurrentHashMap,保证多线程安全同时方便利用map.get(userId)进行推送到指定端口。

    相比之前的Set,Set遍历是费事且麻烦的事情,而Map的get是简单便捷的,当WebSocket数量大的时候,这个小小的消耗就会聚少成多,影响体验,所以需要优化。在IM的场景下,指定userId进行推送消息更加方便。

    Websocker注入Bean问题

    关于这个问题,可以看最新发表的这篇文章,在参考和研究了网上一些攻略后,项目已经通过该方法注入成功,大家可以参考。
    关于controller调用controller/service调用service/util调用service/websocket中autowired的解决方法

    netty-websocket-spring-boot-starter

    Springboot2构建基于Netty的高性能Websocket服务器(netty-websocket-spring-boot-starter)
    只需要换个starter即可实现高性能websocket,赶紧使用吧

    Springboot2+Netty+Websocket

    Springboot2+Netty实现Websocket,使用官方的netty-all的包,比原生的websocket更加稳定更加高性能,同等配置情况下可以handle更多的连接。

    代码样式全部已经更正,也支持websocket连接url带参数功能,另外也感谢大家的阅读和评论,一起进步,谢谢!~~

    ServerEndpointExporter错误

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘serverEndpointExporter’ defined in class path resource [com/xxx/WebSocketConfig.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available

    感谢@来了老弟儿 的反馈:

    如果tomcat部署一直报这个错,请移除 WebSocketConfig@Bean ServerEndpointExporter 的注入 。

    ServerEndpointExporter 是由Spring官方提供的标准实现,用于扫描ServerEndpointConfig配置类和@ServerEndpoint注解实例。使用规则也很简单:

    1. 如果使用默认的嵌入式容器 比如Tomcat 则必须手工在上下文提供ServerEndpointExporter
    2. 如果使用外部容器部署war包,则不需要提供提供ServerEndpointExporter,因为此时SpringBoot默认将扫描服务端的行为交给外部容器处理,所以线上部署的时候要把WebSocketConfig中这段注入bean的代码注掉。

    正式项目的前端WebSocket框架 GoEasy

    感谢kkatrina的补充,正式的项目中,一般是用第三方websocket框架来做,稳定性、实时性有保证的多,也会包括一些心跳、重连机制。

    GoEasy专注于服务器与浏览器,浏览器与浏览器之间消息推送,完美兼容世界上的绝大多数浏览器,包括IE6, IE7之类的非常古老的浏览器。支持Uniapp,各种小程序,react,vue等所有主流Web前端技术。
    GoEasy采用 发布/订阅 的消息模式,帮助您非常轻松的实现一对一,一对多的通信。
    https://www.goeasy.io/cn/doc/

    @Component@ServerEndpoint关于是否单例模式,能否使用static Map等一些问题的解答

    看到大家都在热心的讨论关于是否单例模式这个问题,请大家相信自己的直接,如果websocket是单例模式,还怎么服务这么多session呢。

    1. websocket是原型模式@ServerEndpoint每次建立双向通信的时候都会创建一个实例,区别于spring的单例模式。
    2. Spring的@Component默认是单例模式,请注意,默认 而已,是可以被改变的。
    3. 这里的@Component仅仅为了支持@Autowired依赖注入使用,如果不加则不能注入任何东西,为了方便。
    4. 什么是prototype 原型模式? 基本就是你需要从A的实例得到一份与A内容相同,但是又互不干扰的实例B的话,就需要使用原型模式。
    5. 关于在原型模式下使用static 的webSocketMap,请注意这是ConcurrentHashMap ,也就是线程安全/线程同步的,而且已经是静态变量作为全局调用,这种情况下是ok的,或者大家如果有顾虑或者更好的想法的化,可以进行改进。 例如使用一个中间类来接收和存放session。
    6. 为什么每次都@OnOpen都要检查webSocketMap.containsKey(userId) ,首先了为了代码强壮性考虑,假设代码以及机制没有问题,那么肯定这个逻辑是废的对吧。但是实际使用的时候发现偶尔会出现重连失败或者其他原因导致之前的session还存在,这里就做了一个清除旧session,迎接新session的功能。

    Vue版本的websocket连接

    感谢**@GzrStudy**的贡献,供大家参考。

    <script>
    export default {
        data() {
            return {
                socket:null,
                userId:localStorage.getItem("ms_uuid"),
                toUserId:'2',
                content:'3'
            }
        },
      methods: {
        openSocket() {
          if (typeof WebSocket == "undefined") {
            console.log("您的浏览器不支持WebSocket");
          } else {
            console.log("您的浏览器支持WebSocket");
            //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
            //等同于socket = new WebSocket("ws://localhost:8888/xxxx/im/25");
            //var socketUrl="${request.contextPath}/im/"+$("#userId").val();
            var socketUrl =
              "http://localhost:8081/imserver/" + this.userId;
            socketUrl = socketUrl.replace("https", "ws").replace("http", "ws");
            console.log(socketUrl);
            if (this.socket != null) {
              this.socket.close();
              this.socket = null;
            }
            this.socket = new WebSocket(socketUrl);
            //打开事件
            this.socket = new WebSocket(socketUrl);
            //打开事件
            this.socket.onopen = function() {
              console.log("websocket已打开");
              //socket.send("这是来自客户端的消息" + location.href + new Date());
            };
            //获得消息事件
            this.socket.onmessage = function(msg) {
              console.log(msg.data);
              //发现消息进入    开始处理前端触发逻辑
            };
            //关闭事件
            this.socket.onclose = function() {
              console.log("websocket已关闭");
            };
            //发生了错误事件
            this.socket.onerror = function() {
              console.log("websocket发生了错误");
            };
          }
        },
        sendMessage() {
          if (typeof WebSocket == "undefined") {
            console.log("您的浏览器不支持WebSocket");
          } else {
            console.log("您的浏览器支持WebSocket");
            console.log(
              '{"toUserId":"' +
                 this.toUserId +
                '","contentText":"' +
                 this.content +
                '"}'
            );
            this.socket.send(
              '{"toUserId":"' +
                 this.toUserId +
                '","contentText":"' +
                 this.content +
                '"}'
             );
        
        }
    }
    
    展开全文
  • androidpn消息推送

    千次下载 热门讨论 2012-07-04 16:32:15
    androidpn消息推送 其中包含客户端 服务器端包含tomcat和jetty版本
  • 登陆自己的 阿里云控制台,找到容器镜像服务 根据提示创建命名空间,然后接着创建镜像仓库 创建完镜像仓库后,并且设置好来镜像提交用户的密码,... 下载镜像的时候加上域名 镜像名 版本号即可pull拉取镜像 ...

    登陆自己的 阿里云控制台,找到容器镜像服务
    在这里插入图片描述
    根据提示创建命名空间,然后接着创建镜像仓库
    在这里插入图片描述
    在这里插入图片描述
    创建完镜像仓库后,并且设置好来镜像提交用户的密码,点击该镜像仓库的管理,后面会有提示怎么提交镜像
    在这里插入图片描述
    在这里插入图片描述
    下载镜像的时候加上域名 镜像名 版本号即可pull拉取镜像

    展开全文
  • 前段时间公司需要 Android 端的手机群功能,我们就通过 MQTT 来实现了该功能。 MQTT 的官网如下http://mqtt.org/ 在 MQTT 官网中 http://mqtt.org/software 列举出了实现了该协议的开源客户端和服务器工程和库。...


    前段时间公司需要 Android 端的手机群推功能,我们就通过 MQTT 来实现了该功能。

    MQTT 的官网如下 http://mqtt.org/

    在 MQTT 官网中 http://mqtt.org/software 列举出了实现了该协议的开源客户端和服务器工程和库。

    我们是通过 ApacheActiveClient 开源项目的基础上来实现的。


    下面介绍客户端和服务器简单的搭建:

    客户端 :

    首先到 github(https://github.com/tokudu/AndroidPushNotificationsDemo) 网站上去下载手机客户端程序:

    AndroidPushNotificationsDemo.

    运行结果如下图所示 : 

    其中 Test_android1 是对手机的唯一标识,这个生成策略是可以自己来定的。很多时候我们需要话更多的时间去研究它的源码,而不是去问别人,只有当自己实在找不到解决方案的时候再问,我觉得这样更利于我们对项目的理解和继续扩展。

    服务器端

    至于服务器程序可以到 http://activemq.apache.org/ 去下载。需要注意的是,请读者仔细阅读该开源项目的文档。

    该项目非常强大,支持配置服务器的主从,分布式等。

    当你搭建成功服务器后可以使用客户端连接了。点击 Start Push service 按钮启动推送功能。

    连接成功后你会在控制台看到如下界面:

    在服务器后台你将看到:

    画的红线部分就是你的手机设备,然后单击进入如下界面:

    最后单击确定 ,在手机通知栏将会收到消息 :

    至此,这就是简单的服务器想客户端推送的消息了。

    当然这是最最简单的实现了。接下来就要看自己公司实际的需要进行扩展服务器或者客户端了。

    其实 MQTT 最显著的功能,就是群推。客户端可以注册多个 token(客户端的唯一标识)可以让所有客户端都注册该 token 即可,然后发送的时候,只需要针对一个 token 发消息,那么所有的手机都收到了。不用像其他的推送一样,对每个token都去发一遍。所以MQTT群发的效率的极高的。这样的话对手机端来说就很好了,也许我们需要对某个程序的所有手机发、向某个程序某个版本手机发,向某台手机发推送 等等用MQTT都可以很轻松的实现。

    接下来就是要靠自己去研究 MQTT 了。其他官网文档,和一些开源的功能已经提供了很多我们学习的资料。

    下面是关于 XMPP 协议推送 :Android 消息推送(一)基于 XMPP协议 AndroidPN 推送功能

    如果 github 上面的 Android 客户端的程序不能运行,可以下载我们用的(也是 Github 上下载的):http://download.csdn.net/detail/johnny901114/5820151

    如果还有问题可以留言。

    如果你觉得本文帮助到你,给我个关注和赞呗!

    另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图。

    如果有需要可以移步到我的 [GitHub -> AndroidAll](https://github.com/chiclaim/AndroidAll),里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 


     

    展开全文
  • 现在接别人的活,做的项目中,集成了激光推送服务,但是有些用户要不然不来推送,来就了就很多,而且只有在应用程序开启的时候,也就是系统进程运行这这个应用才有推送,如何做到应用没开启也能收到推送通知呢?...
  • 本来是想实现通知栏可以多行显示通知内容的,但是我实现了之后,收到了两条推送,下面是代码,求大神解答。 ![图片说明](https://img-ask.csdn.net/upload/201702/12/1486872060_223676.png) ``` public class ...
  • Android推送 百度云推送 入门篇

    万次阅读 多人点赞 2014-05-27 20:52:27
    现在app基本都有推送的功能,于是看了下百度云的推送,官方文档和Demo都很到位,记录下使用过程,目标是利用百度云推送最为服务器写个及时通讯的例子~当然了,这是第一篇入门~ 1、第一步就是在百度开发者服务管理中...

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/27231237

    现在app基本都有推送的功能,于是看了下百度云的推送,官方文档和Demo都很到位,记录下使用过程,目标是利用百度云推送最为服务器写个及时通讯的例子~当然了,这是第一篇入门~

    1、第一步就是在百度开发者服务管理中创建项目,然后拿到API key , Secret Key ;这个过程就不多说了,上官网直接申请就行,不复杂。


    2、下载云推送的客户端SDK,SDK的压缩文件中包含一个例子代码,一个用户手册,和所需的libs和资源等(其实直接看用户手册和Demo基本就没问题了)。



    3、准备工作结束,接下来,我们就直接开始新建项目测试

    a、新建一个项目,然后把SDK中的libs中的jar和so文件夹拷贝到新建的项目中去


    b、将manifest中的application的name设置为:com.baidu.frontia.FrontiaApplication

       <application
            android:name="com.baidu.frontia.FrontiaApplication"
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >

    如果你的项目需要自定义Application,请参考用户手册中的相关配置。

    c、添加权限

     <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
        <uses-permission android:name="android.permission.WRITE_SETTINGS" />
        <uses-permission android:name="android.permission.VIBRATE" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    d、添加两个receiver和一个Service(注释标明了用处)

     <!-- push service start -->
            <!-- 用于接收系统消息以保证PushService正常运行 -->
            <receiver
                android:name="com.baidu.android.pushservice.PushServiceReceiver"
                android:process=":bdservice_v1" >
                <intent-filter>
                    <action android:name="android.intent.action.BOOT_COMPLETED" />
                    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
                    <action android:name="com.baidu.android.pushservice.action.notification.SHOW" />
                    <action android:name="com.baidu.android.pushservice.action.media.CLICK" />
                </intent-filter>
            </receiver>
            <!-- Push服务接收客户端发送的各种请求 -->
            <!-- 注意:RegistrationReceiver 在2.1.1及之前版本有拼写失误,为RegistratonReceiver ,用新版本SDK时请更改为如下代码 -->
            <receiver
                android:name="com.baidu.android.pushservice.RegistrationReceiver"
                android:process=":bdservice_v1" >
                <intent-filter>
                    <action android:name="com.baidu.android.pushservice.action.METHOD" />
                    <action android:name="com.baidu.android.pushservice.action.BIND_SYNC" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.PACKAGE_REMOVED" />
    
                    <data android:scheme="package" />
                </intent-filter>
            </receiver>
            <!-- Push 服务 -->
            <!-- 注意:在4.0 (包含)之后的版本需加上如下所示的intent-filter action -->
            <service
                android:name="com.baidu.android.pushservice.PushService"
                android:exported="true"
                android:process=":bdservice_v1" >
                <intent-filter>
                    <action android:name="com.baidu.android.pushservice.action.PUSH_SERVICE" />
                </intent-filter>
            </service>
            <!-- push service end -->

    e、我们需要自己实现一个Receiver,来接收Push消息、接口调用回调以及通知点击事件。

     <receiver android:name="com.example.zhy_baiduyun_tuisong01.receiver.MyPushMessageReceiver" >
                <intent-filter>
                    <!-- 接收push消息 -->
                    <action android:name="com.baidu.android.pushservice.action.MESSAGE" />
                    <!-- 接收bind、setTags等method的返回结果 -->
                    <action android:name="com.baidu.android.pushservice.action.RECEIVE" />
                    <!-- 可选。接受通知点击事件,和通知自定义内容 -->
             		 <action android:name="com.baidu.android.pushservice.action.notification.CLICK" />
                </intent-filter>
            </receiver>

    代码:

    package com.example.zhy_baiduyun_tuisong01.receiver;
    
    import java.util.List;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import android.content.Context;
    import android.content.Intent;
    import android.text.TextUtils;
    import android.util.Log;
    
    import com.baidu.frontia.api.FrontiaPushMessageReceiver;
    import com.example.zhy_baiduyun_tuisong01.MainActivity;
    import com.example.zhy_baiduyun_tuisong01.util.PreUtils;
    
    /**
     * Push消息处理receiver。请编写您需要的回调函数, 一般来说: onBind是必须的,用来处理startWork返回值;
     * onMessage用来接收透传消息; onSetTags、onDelTags、onListTags是tag相关操作的回调;
     * onNotificationClicked在通知被点击时回调; onUnbind是stopWork接口的返回值回调
     * 
     * 返回值中的errorCode,解释如下: 
     *  0 - Success
     *  10001 - Network Problem
     *  30600 - Internal Server Error
     *  30601 - Method Not Allowed 
     *  30602 - Request Params Not Valid
     *  30603 - Authentication Failed 
     *  30604 - Quota Use Up Payment Required 
     *  30605 - Data Required Not Found 
     *  30606 - Request Time Expires Timeout 
     *  30607 - Channel Token Timeout 
     *  30608 - Bind Relation Not Found 
     *  30609 - Bind Number Too Many
     * 
     * 当您遇到以上返回错误时,如果解释不了您的问题,请用同一请求的返回值requestId和errorCode联系我们追查问题。
     * 
     */
    public class MyPushMessageReceiver extends FrontiaPushMessageReceiver {
        /** TAG to Log */
        public static final String TAG = MyPushMessageReceiver.class
                .getSimpleName();
    
        /**
         * 调用PushManager.startWork后,sdk将对push
         * server发起绑定请求,这个过程是异步的。绑定请求的结果通过onBind返回。 如果您需要用单播推送,需要把这里获取的channel
         * id和user id上传到应用server中,再调用server接口用channel id和user id给单个手机或者用户推送。
         * 
         * @param context
         *            BroadcastReceiver的执行Context
         * @param errorCode
         *            绑定接口返回值,0 - 成功
         * @param appid
         *            应用id。errorCode非0时为null
         * @param userId
         *            应用user id。errorCode非0时为null
         * @param channelId
         *            应用channel id。errorCode非0时为null
         * @param requestId
         *            向服务端发起的请求id。在追查问题时有用;
         * @return none
         */
        @Override
        public void onBind(Context context, int errorCode, String appid,
                String userId, String channelId, String requestId) {
            String responseString = "onBind errorCode=" + errorCode + " appid="
                    + appid + " userId=" + userId + " channelId=" + channelId
                    + " requestId=" + requestId;
            Log.e(TAG, responseString);
    
            // 绑定成功,设置已绑定flag,可以有效的减少不必要的绑定请求
            if (errorCode == 0) {
                PreUtils.bind(context);
            }
            // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑
            updateContent(context, responseString);
        }
    
        /**
         * 接收透传消息的函数。
         * 
         * @param context
         *            上下文
         * @param message
         *            推送的消息
         * @param customContentString
         *            自定义内容,为空或者json字符串
         */
        @Override
        public void onMessage(Context context, String message,
                String customContentString) {
            String messageString = "透传消息 message=\"" + message
                    + "\" customContentString=" + customContentString;
            Log.e(TAG, messageString);
    
            // 自定义内容获取方式,mykey和myvalue对应透传消息推送时自定义内容中设置的键和值
            if (!TextUtils.isEmpty(customContentString)) {
                JSONObject customJson = null;
                try {
                    customJson = new JSONObject(customContentString);
                    String myvalue = null;
                    if (customJson.isNull("mykey")) {
                        myvalue = customJson.getString("mykey");
                    }
                } catch (JSONException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
    
            // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑
            updateContent(context, messageString);
        }
    
        
        
        /**
         * 接收通知点击的函数。注:推送通知被用户点击前,应用无法通过接口获取通知的内容。
         * 
         * @param context
         *            上下文
         * @param title
         *            推送的通知的标题
         * @param description
         *            推送的通知的描述
         * @param customContentString
         *            自定义内容,为空或者json字符串
         */
        @Override
        public void onNotificationClicked(Context context, String title,
                String description, String customContentString) {
            
        	
        	String notifyString = "通知点击 title=\"" + title + "\" description=\""
                    + description + "\" customContent=" + customContentString;
            Log.e(TAG, notifyString);
    
            // 自定义内容获取方式,mykey和myvalue对应通知推送时自定义内容中设置的键和值
            if (!TextUtils.isEmpty(customContentString)) {
                JSONObject customJson = null;
                try {
                    customJson = new JSONObject(customContentString);
                    String myvalue = null;
                    if (customJson.isNull("mykey")) {
                        myvalue = customJson.getString("mykey");
                    }
                } catch (JSONException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
    
            // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑
            updateContent(context, notifyString);
        }
    
        /**
         * setTags() 的回调函数。
         * 
         * @param context
         *            上下文
         * @param errorCode
         *            错误码。0表示某些tag已经设置成功;非0表示所有tag的设置均失败。
         * @param successTags
         *            设置成功的tag
         * @param failTags
         *            设置失败的tag
         * @param requestId
         *            分配给对云推送的请求的id
         */
        @Override
        public void onSetTags(Context context, int errorCode,
                List<String> sucessTags, List<String> failTags, String requestId) {
            String responseString = "onSetTags errorCode=" + errorCode
                    + " sucessTags=" + sucessTags + " failTags=" + failTags
                    + " requestId=" + requestId;
            Log.e(TAG, responseString);
    
            // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑
            updateContent(context, responseString);
        }
    
        /**
         * delTags() 的回调函数。
         * 
         * @param context
         *            上下文
         * @param errorCode
         *            错误码。0表示某些tag已经删除成功;非0表示所有tag均删除失败。
         * @param successTags
         *            成功删除的tag
         * @param failTags
         *            删除失败的tag
         * @param requestId
         *            分配给对云推送的请求的id
         */
        @Override
        public void onDelTags(Context context, int errorCode,
                List<String> sucessTags, List<String> failTags, String requestId) {
            String responseString = "onDelTags errorCode=" + errorCode
                    + " sucessTags=" + sucessTags + " failTags=" + failTags
                    + " requestId=" + requestId;
            Log.e(TAG, responseString);
    
            // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑
            updateContent(context, responseString);
        }
    
        /**
         * listTags() 的回调函数。
         * 
         * @param context
         *            上下文
         * @param errorCode
         *            错误码。0表示列举tag成功;非0表示失败。
         * @param tags
         *            当前应用设置的所有tag。
         * @param requestId
         *            分配给对云推送的请求的id
         */
        @Override
        public void onListTags(Context context, int errorCode, List<String> tags,
                String requestId) {
            String responseString = "onListTags errorCode=" + errorCode + " tags="
                    + tags;
            Log.e(TAG, responseString);
    
            // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑
            updateContent(context, responseString);
        }
    
        /**
         * PushManager.stopWork() 的回调函数。
         * 
         * @param context
         *            上下文
         * @param errorCode
         *            错误码。0表示从云推送解绑定成功;非0表示失败。
         * @param requestId
         *            分配给对云推送的请求的id
         */
        @Override
        public void onUnbind(Context context, int errorCode, String requestId) {
            String responseString = "onUnbind errorCode=" + errorCode
                    + " requestId = " + requestId;
            Log.e(TAG, responseString);
    
            // 解绑定成功,设置未绑定flag,
            if (errorCode == 0) {
                PreUtils.unbind(context);
            }
            // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑
            updateContent(context, responseString);
        }
    
        private void updateContent(Context context, String content) {
            Log.e(TAG, "updateContent");
            //String logText = "" + Utils.logStringCache;
    
    //        if (!logText.equals("")) {
    //            logText += "\n";
    //        }
    
    //        SimpleDateFormat sDateFormat = new SimpleDateFormat("HH-mm-ss");
    //        logText += sDateFormat.format(new Date()) + ": ";
    //        logText += content;
    
            //Utils.logStringCache = logText;
    
            Intent intent = new Intent();
            intent.putExtra("result", content);
            intent.setClass(context.getApplicationContext(), MainActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.getApplicationContext().startActivity(intent);
        }
    
    }
    

    代码是官方Demo的代码,注释特别详细,做了一点修改,每次回调的结果,我会让显示到主界面上。主Actvity:

    package com.example.zhy_baiduyun_tuisong01;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.TextView;
    
    import com.baidu.android.pushservice.PushConstants;
    import com.baidu.android.pushservice.PushManager;
    import com.example.zhy_baiduyun_tuisong01.util.PreUtils;
    
    public class MainActivity extends Activity
    {
    	private TextView mTextView;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		initView();
    
    		autoBindBaiduYunTuiSong();
    
    	}
    
    	private void initView()
    	{
    		mTextView = (TextView) findViewById(R.id.id_textview);
    	}
    
    	@Override
    	protected void onNewIntent(Intent intent)
    	{
    		String result = intent.getStringExtra("result");
    		if (result != null)
    		{
    			mTextView.setText(result);
    
    		}
    		// super.onNewIntent(intent);
    	}
    
    	/**
    	 * 如果没有绑定百度云,则绑定,并记录在属性文件中
    	 */
    	private void autoBindBaiduYunTuiSong()
    	{
    		if (!PreUtils.isBind(getApplicationContext()))
    		{
    			PushManager.startWork(getApplicationContext(),
    					PushConstants.LOGIN_TYPE_API_KEY,
    					"TVkKGesssSDs5q7AamLGnNCs");
    		}
    	}
    
    }
    

    最终的测试:

    1、应用安装后,如果绑定成功,主界面:


    然后在管理控制台开始分别发送通知和消息:


    2、当发送通知并点击通知:


    3、当发送消息:




    好了,都是最基本的功能,没什么技术含量就是需要点耐心,下面贴上源码,使用源码请把MainActivity里面的KEY设置成自己申请的KEY。


    源码点击下载




    展开全文
  • 最近公司需要 Android 的消息推送,在网上找了很多消息推送的例子,都是无法运行. 经过一段时间的研究,终于把例子运行起来了。现在共享出来,共同学习。 在 androidpn 的官网下载...
  • 基于消息推送的聊天工具

    千次下载 热门讨论 2013-06-08 15:20:46
    本应用是基于百度云推送的一款轻量级聊天工具,包含多个开源项目库,同时本代码也已经开源,欢迎访问我的博客主页:http://blog.csdn.net/weidi1989,由于时间仓促,错误与疏忽之处在所难免,希望各位朋友们以邮件的...
  • 在Spring Boot框架下使用WebSocket实现消息推送

    万次阅读 多人点赞 2016-12-23 16:48:37
    Spring Boot的学习持续进行中。前面两篇博客我们介绍了如何使用Spring Boot容器搭建Web项目(使用Spring Boot开发Web项目)以及怎样为我们的Project添加HTTPS的支持(使用Spring Boot开发Web项目(二... 什么是WebSocket
  • Git 推送和删除远程标签

    万次阅读 2013-01-31 09:24:20
    事实上Git 的推送和删除远程标签命令是相同的,删除操作实际上就是推送空的源标签refs: git push origin 标签名 相当于 git push origin refs/tags/源标签名:refs/tags/目的标签名 git push 文档中有解释: ...
  • 极光推送的配置,在官方文档中已经很详细地描述了;有手动导入跟jcenter导入两种方式,推荐使用jcenter;//在gradle中添加 android { ...... defaultConfig { applicationId "com.xxx.xxx" //JPush上注册的包名. ...
  • 我想把极光推送官方定义在自己的App中的Xml中注册的那个推送服务名字改掉,但是那个服务是在什么地方调用的啊? 现在我改完推送服务的名字之后就接不到推送了,但是又找不到那个服务到底是在什么地方调用的,有大神...
  • Android推送之极光推送

    千次阅读 2013-10-29 16:23:04
    之前有写一个项目在推送这块想了也找了很多的办法,最终发现激光推送(Jpush)非常的不错,于是决定用激光推送,效果还非常的不错。 查看了极光的API文档,发现可以做server的远程调用 API。在这里分享哈怎样远程调用...
  • 转载自:... 推送本地分支local_branch到远程分支 remote_branch并建立关联关系 a.远程已有remote_branch分支并且已经关联本地分支local_branch且本地已经切换到local_branch git...
  • 极光推送中有指定Registration ID推送或根据唯一标识推送的方法吗?文档我看了没有针对这个问题的解释。同时也没有看到文档中提供的相关接口。
  • 在Android开发中,消息推送功能的使用非常常见。为了降低开发成本,使用第三方推送是现今较为流行的解决方案。 今天,我将手把手教大家如何在你的应用里集成小米推送 该文档基于小米推送官方Demo,并给出简易推送...
  • 最近需要开发微信和小程序的推送功能,需要用java后台实现推送,自己本身java和小程序都做,所以就自己动手实现下小程序的模版推送功能推送。 实现思路 1 小程序获取用户openid,收集formid传给java...
  • 个推推送 别名推送

    千次阅读 2018-06-28 17:32:32
    找了很久的问题。绑定别名需要在获取ClientId后才能去绑定。在Service回调clientId后在进行别名绑定
  • 想请教一下,怎么样可以让当前App从后台中清掉的时候,集成的极光推送推送服务不被 清掉?因为推送服务被清掉之后就接收不到推送的信息了。现在是只有在软件在被用户操作的时候才可能接收到推送的消息
  • 在使用git推送项目时候出现 "fatal: The remote end hung up unexpectedly " 原因是推送的文件太大。 解决方案: 在克隆/创建版本库生成的 .git目录下面修改生成的config文件增加如下: [http] postBuffer = ...
  • Android推送服务——百度云推送

    千次阅读 2013-12-06 11:44:09
    一、推送服务简介 消息推送,顾名思义,是由一方主动发起,而另一方与发起方以某一种方式建立连接并接收消息。在Android开发中,这里的发起方我们把它叫做推送服务器(Push Server),接收方叫做客户端(Client...
  • Web实时推送Demo by SignalR

    千次下载 热门讨论 2013-12-18 09:59:49
    因近期发现很多关于 Web实时推送的问题贴,所以写了一个简单的 基于 SignalR 的Web实时推送 Demo.
  • Android消息推送:第三方消息推送平台详细解析

    万次阅读 多人点赞 2016-10-19 19:33:50
    消息推送在Android开发中应用的场景是越来越多了,比如说电商产品进行活动宣传、资讯类产品进行新闻推送等等,如下图: 本文将介绍Android中实现消息推送的第三方推送的详细解决方案 阅读本文前,建议先阅读我的写...
  • 转载请以链接形式标明出处:  ...本文出自:【lxk_1993的博客】;  推送SDK 信鸽 个推 SDK占用空间 armeabi:1.76M 全部:2.31M (都包含android-support
  • 本地推送.极光推送.APNs推送

    千次阅读 2015-12-30 14:47:38
    1.本地推送的简单应用 2.APNs介绍 3.使用极光推送向你的APP推送一条信息 一.本地推送 本地通知推送也叫闹钟通知, 这是通知中比较简单的一部分. 如果将app比喻成一个人, 某个controller比喻成身体的某一部分(手臂), ...
  • JPush极光推送详解

    万次阅读 2016-10-19 22:26:52
    JPush是一种专业而免费的第三方云推送推送平台,又名极光推送,在同行中开发者对JPush较为青睐,哈哈,不仅仅是因为它是免费的,服务也是非常到位的。 2.JPush的集成步骤 1.新建一个工程,复制自己的应用包名。 2....
  • 1.本地推送 关于本地推送的作用及意义:【后期完善】 实现步骤 大致4步:1创建通知、2注册通知、3实现函数、4删除通知 注意! 下面的内容中没有提到:在使用通知的时候,为了避免重复注册:最好在生命周期的函数...

空空如也

1 2 3 4 5 ... 20
收藏数 84,947
精华内容 33,978
关键字:

推送