示例图片二

Websocket实现后端主动向Android推送任务

< id> alimaven </ id>

*/

< artifactId> spring-boot-maven-plugin </ artifactId>

content!!.setText(text)

启动服务器

* 用来存放管理员Session

< groupId> com.push </ groupId>

/**

WebSocketUtils.removeUser(session);

<?xml version="1.0" encoding="UTF-8"?>

< dependency>

}

构造一个messagebean,这个类主要用于接受前端管理后台传过来的发送参数,比如推送标题,推送内容,推送渠道等。

xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"

< scope> provided </ scope>

}

< artifactId> spring-boot-starter-parent </ artifactId>

overridefunonClosing(webSocket: WebSocket?, code: Int, reason: String?) {

e.printStackTrace;

< artifactId> spring-boot-starter-websocket </ artifactId>

WebSocketUtils.addUser(session);

.addInterceptors( newHttpSessionHandshakeInterceptor)

});

<!-- gson比较好用,json处理时方便些,也引入了库-->

//这些都可以作为后台赛选发送对象的依据,如果需要扩充,可以在这里加入其他参数,后台赛选时处理一下即可

/**

}

/**

//应为客户端是普通用户,所以使用user请求,channel:渠道,id:设备id,age:年龄。

< artifactId> spring-boot-starter-web </ artifactId>

</ properties>

System. out.println( "发送消息给管理员失败:" e.getLocalizedMessage);

-->

} catch(IOException e) {

System. out.println( "消息发送失败");

adminSessionSet. add(socketSession);

< java.version> 1.8 </ java.version>

}

刚开始时我直接用intellij idea打包成jar包,结果是打出了jar包,但是这个包实际上没法运行,运行时会提示“缺少清单文件”这类提示,大致就是缺少了META-INF这个文件。于是上网找了一下,发现使用mvn命令可以打包,于是又用mvn clean和mvn install命令打了一次包,结果没打成功,出现一个错误:

}

</ mirror>

privateString title; //推送标题

/**

if(Integer.valueOf(map. get( "channel")) == messageBean.getChannel){

</ dependency>

< mirror>

前言

< mirror>

}

* @param socketSession

}

}

}

.writeTimeout( 3, TimeUnit.SECONDS)

} catch(IOException e) {

< plugin>

< version> 2.2.1.RELEASE </ version>

/**

privateintminYear; //推送限定的最小接收年龄

*连接成功时调用此方法

xsi:schemaLocation= "http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

Log.e( "日志", "链接关闭中")

//连接成功

try{

< groupId> org.springframework.boot </ groupId>

if(messageBean != null){

publicvoidafterConnectionClosed(WebSocketSession session, CloseStatus closeStatus)throwsException {

}

* @param socketSession

if(Integer.valueOf(map. get( "channel")) == messageBean.getChannel && Integer.valueOf(map. get( "age"))>messageBean.getMinYear && Integer.valueOf(map. get( "age"))<messageBean.getMaxYear){

}

publicstaticsynchronized intgetUserOnlineCount( ) {

String[] para= path[i].split( "=");

});

/**

< projectxmlns= "http://maven.apache.org/POM/4.0.0"xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"

|

//连接异常

privateintmaxYear; //推送限定的最大接收年龄

@ResponseBody

publicstaticsynchronized voidremoveAdmin( WebSocketSession webSocketSession) {

@Override

System. out.println( "发送消息给用户失败:" e.getLocalizedMessage);

到上面就已经完全结束了,效果就是文章开头的gif图,当然,这里我忽略前端管理后台的搭建,应为这个不是文章重点,所以忽略掉了。前端管理后台实际上我使用的是AdminLTE-3.0.0,让后写一些js和后端交互传递一下数据就行了。

}

| this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used

<mirror>

implementation'com.squareup.okhttp3:okhttp:3.8.1'

}

}

privateintchannel = -1; //推送渠道

*/

try{

* @return

< artifactId> spring-boot-starter-thymeleaf </ artifactId>

/**

text!!.setText( "连接状态:连接成功")

usersSessionSet.sendMessage( newTextMessage(msg));

< id> uk </ id>

< artifactId> jstl </ artifactId>

* 删除普通用户

WebSocketUtils.sendMessageToAdmin;

super.onClosed(webSocket, code, reason)

e.printStackTrace;

| for inheritance and direct lookup purposes, and must be unique across the set of mirrors.

实现服务器后端向不同渠道,不同用户群体发送不同类型的通知(比如状态了通知,启动弹窗等)。

// 在这里注册一下user,用于拦截普通用户的连接

@Override

@RequestMapping(value = "/sendAll",method = RequestMethod.POST , produces = "application/json;charset=UTF-8")

< dependency>

以前很多推送都是通过前端通过设定一定的时间间隔不断的向服务器获取推送消息,不过这样的缺点是浪费了很多服务器资源,而且也有可能被人滥用,导致服务器异常。于是乎出现了websocket协议。websocket协议的好处是可以实现持久性连接,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。本篇文章主要讲的是利用springboot websocket实现后端向前端推送消息的功能。

}

map.put(para[ 0],para[ 1]);

webSocketSession.sendMessage( newTextMessage(msg));

}

*/

returnmv;

publicvoidhandleMessage(WebSocketSession session, WebSocketMessage<?> message)throwsException {

} catch(IOException e) {

//读取前端连接时的地址,从地址中获取条件参数,并保存到hashmap中去

@RequestMapping(value= "/")

* 连接是默认跳转到starter.html这个网页下,这个网页就是管理后台的主界面

< plugins>

WebSocketUtils.sendMessageToUser(backInfo);

return"{}";

text!!.setText( "连接状态:连接失败")

*/

map.put(para[ 0],para[ 1]);

*/

super.onFailure(webSocket, t, response)

*关闭连接时调用此方法

/**

mv.setViewName( "starter");

});

//向管理员汇报当前在线人数

usersSessionSet.forEach(webSocketSession -> {

publicstaticvoidsendMessageToUserForCondition( String msg) {

Gson gson = new Gson;

try{

}

*/

privateString targetId; //推送目标id

* 发送消息给管理员

publicvoidregisterWebSocketHandlers(WebSocketHandlerRegistry registry){

xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

@EnableWebSocket

< name> OSChina Central </ name>

varmOkHttpClient = OkHttpClient.Builder

* 发送给某个用户

/**

< name> Human Readable Name for this Mirror. </ name>

publicString sendAll( @RequestBodyMessageBean messageBean){

</ mirror>

*/

publicModelAndView index(ModelAndView mv){

< artifactId> javax.servlet-api </ artifactId>

< mirrorOf> central </ mirrorOf>

*/

registry.addHandler( newUserHandler, "/user")

}

*/

}

打包代码成jar格式并部署到服务器打包

/**

</ parent>

try{

Android代码的实现

< dependency>

到这里后端的代码基本完成了,adminhandle的代码与userhandle雷同,当然如果不需要动态统计人数到管理界面展示,adminhanle也没必要编写了。

}

</ dependency>

map.put(para[ 0],para[ 1]);

上传包

*连接异常时调用此方法

}

privateString content; //推送内容

</ mirror>

*/

< version> 2.8.6 </ version>

资源

< optional> true </ optional>

< scope> provided </ scope>

< groupId> org.springframework.boot </ groupId>

* 删除管理员

usersSessionSet.forEach(webSocketSession -> {

< parent>

System. out.println( "发送消息给管理员失败:" e.getLocalizedMessage);

returnnewServerEndpointExporter;

< name> push </ name>

< version> 0.0.1-SNAPSHOT </ version>

//设置读取超时时间

*/

/**

*/

e.printStackTrace;

System. out.println( "发送消息给用户失败:" e.getLocalizedMessage);

* @return

//判断渠道是否符合条件参数,符合则发送消息

overridefunonClosed(webSocket: WebSocket?, code: Int, reason: String?) {

//mOkHttpClient.dispatcher.executorService.shutdown

privatestaticCopyOnWriteArraySet<WebSocketSession> adminSessionSet = newCopyOnWriteArraySet<>;

原标题:Websocket实现后端主动向Android推送任务

*/

< dependency>

</mirror>

try{

下面的代码只贴出连接部分的代码,其他非核心的内容就不贴出来了。

privateinttype; //推送类型

publicstaticvoidsendMessageToUserForSingle( String msg) {

</ exclusion>

里面比较重要的依赖都标有注释了

这里我们使用scp命令上传jar包到服务器(我的编程环境是linux系统的),使用的命令是:scp 文件名 用户名@服务器ip地址:服务器目标文件夹,比如scp pushAdmin.jar root@xxx.xxx.xxx.xxx:/home

});

try{

}

for( inti= 0;i<path.length;i ){

< build>

setting.xml中的内容如下:

}

.readTimeout( 3, TimeUnit.SECONDS)

< mirror>

String backInfo = gson.toJson(messageBean);

//设置连接超时时间

//向管理员汇报当前人数

< url> http://uk.maven.org/maven2/ </ url>

</ mirror>

<!-- 魔板,前端页面时需要此依赖,不过不用也可以使用前端界面,不过麻烦些,引入这个库后,前端界面放到resource里的templates文件夹下,静态内容放到resource里的static文件夹下,比如要引入的css或js这些-->

< groupId> com.google.code.gson </ groupId>

@Override

e.printStackTrace;

privateString imageUrl; //推送图片地址

Gson gson = newGson;

/**

< mirrors>

publicstaticvoidsendMessageToAdmin( ) {

< dependency>

@Override

粉丝福利:送五本《移动开发架构设计实战》,点击查看

linux服务器一台(用于项目的部署) intellij idea (用于后端代码编写软件) android studio (android代码编写软件) 群推:所有在线设备都将收到推送 个推:只有输入的设备id号才可以收到推送 渠推:针对某一个市场渠道进行推送 条件推送:多个条件组合起来的推送,比如用户年龄区间,市场渠道等针对性比较强的推送 展开全文 bean:存放基本的bean对象 config:配置类,该项目中是存放websocket的配置类 controller:控制器,里面可以通过不同的路由地址放回不同的json数据或界面 utils:存放工具类 static:存放静态文件,比如css,js之类的 templates:存放html模板,用户前端界面展示 可扩展性强,我们需要什么推送条件,补齐一下就好了,如果使用友盟推送等第三方推送,往往会发现他们提供给我们的推送条件并不完全是我们需要的。 方便获取用分布信息。因为在连接websocket时会有传递的参数,这些参数够多的话可以方便的知道用户构成是怎样的 第一:需要对推送系统进行开发维护,二第三方的直接接入即可使用。 第二:当在线用户过多时可能影响服务器性能,毕竟每个在线用户就是一个实体,如果有上千万个在线用户,也就意味着服务器必须创建成千上完个实体,服务器吃不吃得消还真不好说。这里我还没有做过压力测试,也不好知道究竟会对服务器有多大的影响。 第三:推送不一定可以准确命中目标。如果目标设备要收到推送,则设备必须处于在线状态。对于安卓手机而言,进程保活的手段是挺多的,但是不可能100%有效,除非像阿里,腾讯那样是独角兽公司,可以被手机系统列入白名单。

< groupId> org.springframework.boot </ groupId>

因为我们要用到maven,maven在国内很难连接上或者根本有时连接不上,这是需要修改一下setting.xml中的内容,如果没有就创建一下,步骤如下。存在setting.xml时显示的是open "setting.xml",不存在则显示 create "setting.xml"

@Override

< dependency>

}

HashMap<String,String> map = newHashMap<>;

publicstaticsynchronized voidaddAdmin( WebSocketSession socketSession) {

< dependencies>

</ plugins>

publicstaticvoidsendMessageToAdmin( String msg) {

}

< properties>

.build

改配置后就很容易连接上了。下面的图片是项目的目录结构图

* @parammessageBean 记录后台管理页面传过来的内容

});

</ exclusions>

publicclassMessageBean{

<!-- servlet依赖. -->

Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.2:test (default-test) on project push: There are test failures.。感觉无解然后又找了一下其他命令,最后看到网上有一位大神说这种情况可以使用另外一个命令打包。mvn clean package -Dmaven.test.skip=true,后来的确也试了一下这个命令,的确是可行的

< groupId> org.junit.vintage </ groupId>

/**

总结优点:

<url>http://my.repository.com/repo/path</url>

<!-- 我们需要toncat,这是打开对tomcat的支持.-->

< exclusion>

String[] para= path[i].split( "=");

valrequest = Request.Builder.url( "ws://xxx.xxx.xxx.xxx:8080/user?channel=0&id=0&age=22").build

WebSocketUtils.sendMessageToAdmin;

/**

super.onOpen(webSocket, response)

}

< name> internal nexus repository </ name>

}

returnadminSessionSet.size;

super.onMessage(webSocket, text)

e.printStackTrace;

< artifactId> gson </ artifactId>

< name> aliyun maven </ name>

publicbooleansupportsPartialMessages{

//设置写的超时时间

*/

* @return

if(map. get( "id"). equals(messageBean.getTargetId)){

< mirror>

HashMap<String,String> map = newHashMap<>;

publicclassPushMainEnterController{

}

mOkHttpClient!!.newWebSocket(request, object:WebSocketCallback{

}

//收到消息

* 发送消息给管理员

<!-- 这个需要为 true 热部署才有效 -->

@Configuration

valsocketListener = WebSocketCallback

overridefunonOpen(webSocket: WebSocket?, response: Response?) {

publicstaticsynchronized voidremoveUser( WebSocketSession webSocketSession) {

编写controller和websocket的一个配置类,controller主要用于对各种连接进行拦截并做出相应的处理,websockt配置类里面项目中主要是对websocket地址进行拦截。

<id>mirrorId</id>

webSocketSession.sendMessage( newTextMessage(msg));

* 群发消息

}

})

returnfalse;

/**

< artifactId> junit-vintage-engine </ artifactId>

< url> http://repo.maven.apache.org/maven2 </ url>

< deion> Demo project for Spring Boot </ deion>

publicclassUserHandlerimplementsWebSocketHandler{

usersSessionSet. add(socketSession);

* 通过条件发送信息

</ dependency>

websocket代码如下:

</ dependency>

}

returnusersSessionSet.size;

implementation'com.squareup.okhttp3:mockwebserver:3.8.1'

//其他的个推,条件推送,渠道推送和群发消息的代码基本一模一样,只是WebSocketUtils中调用的方法不同而已

System.out.println( "用户连接失败");

}

*/

/**

< exclusions>

} catch(IOException e) {

String[] path = webSocketSession.getUri.getQuery.split( "&");

.addInterceptors( newHttpSessionHandshakeInterceptor)

publicvoidafterConnectionEstablished(WebSocketSession session)throwsException {

Gson gson = newGson;

overridefunonMessage(webSocket: WebSocket?, text: String?) {

< mirrorOf> central </ mirrorOf>

HashMap<String,String> map = newHashMap<>;

</ dependency>

使用ssh命令连接服务器。比如ssh root@xxx.xxx.xxx.xxx,连接上后找到刚才上传的jar包,让后使用nohup java -jar 包名.jar &启动并挂起后台。到这里就部署完了,这时外网就可以正常访问后台管理了。

* 添加普通用户

* 获取管理员在线人数

android连接websocket我们就用okhttp3去实现,既然要使用okhttp3,就必须引入依赖,依赖如下

} else{

}

Gson gson = newGson;

< dependency>

}

publicstaticvoidsendMessageToUserForChannel( String msg) {

MessageBean messageBean = gson.fromJson(msg,MessageBean.class);

< mirrorOf> central </ mirrorOf>

controller的代码如下:

//关闭连接服务

//关闭时

webSocketSession.sendMessage( newTextMessage(msg));

String para = session.getUri.getQuery;

</ dependency>

adminSessionSet.forEach(webSocketSession -> {

< groupId> org.springframework.boot </ groupId>

.setAllowedOrigins( "*");

publicstaticvoidsendMessageToUser( String msg) {

@ResponseBody

< artifactId> push </ artifactId>

//发送消息前端设备

usersSessionSet.forEach(usersSessionSet->{

< groupId> org.springframework.boot </ groupId>

* @return

publicvoidhandleTransportError(WebSocketSession session, Throwable exception)throwsException {

//这里注册一下admin,用于拦截管理员的连接,其实管理员在此项目中中需要获取在线的普通用户数量而已

| Specifies a repository mirror site to use instead of a given repository. The repository that

*/

*/

< artifactId> tomcat-embed-jasper </ artifactId>

< id> CN </ id>

}

< url> http://maven.oschina.net/content/groups/public/ </ url>

@Override

步骤四

/**

/**

/**

registry.addHandler( newAdminHandler, "/admin")

</ dependency>

}

System.out.println( "用户退出连接");

</ settings>

}

</ mirrors>

adminSessionSet. remove(webSocketSession);

步骤二:

< artifactId> spring-boot-devtools </ artifactId>

<name>Human Readable Name for this Mirror.</name>

String[] path = webSocketSession.getUri.getQuery.split( "&");

*/

}

< dependency>

usersSessionSet.forEach(webSocketSession -> {

privatestaticCopyOnWriteArraySet<WebSocketSession> usersSessionSet = newCopyOnWriteArraySet<>;

<!-- mirror

配置一下依赖,下面是我的poem.xml文件内容

//告知管理员消息发送失败

publicstaticsynchronized intgetAdminOnlineCount( ) {

}

adminSessionSet.forEach(webSocketSession -> {

</ dependencies>

*收到消息时调用此方法

< scope> test </ scope>

< groupId> javax.servlet </ groupId>

< artifactId> spring-boot-starter-test </ artifactId>

}

publicclassWebSocketUtils{

* 获取普通用户在线人数

.setAllowedOrigins( "*");

@Controller

* @parammv

overridefunonFailure(webSocket: WebSocket?, t: Throwable?, response: Response?) {

super.onClosing(webSocket, code, reason)

String[] para= path[i].split( "=");

} catch(IOException e) {

*/

publicServerEndpointExporter serverEndpointExporter{

</ build>

* @param webSocketSession

* 添加管理员

} catch(IOException e) {

< groupId> javax.servlet </ groupId>

< groupId> org.springframework.boot </ groupId>

</ dependency>

//关闭后

/**

< id> nexus </ id>

< mirrorOf> central </ mirrorOf>

</ project>

<!-- 引入websocket-->

<?xml version="1.0" encoding="UTF-8"?>

< dependency>

usersSessionSet. remove(webSocketSession);

.connectTimeout( 3, TimeUnit.SECONDS)

* 用来存放普通用户Session

* 发送给某个渠道

</ plugin>

< modelVersion> 4.0.0 </ modelVersion>

WebSocketUtils辅助类的编写,主要方便发送消息,添加用户等操作。这里原本我是想通过使用hashmap保存的,这样方便直接通过key获取内容,不过考虑到hashmap是线程不安全的,在多线程读写时可能存在问题,所以改用CopyOnWriteArraySet了。

MessageBean messageBean = gson.fromJson(msg,MessageBean.class);

publicclassWebSocketConfigimplementsWebSocketConfigurer{

}

* @param msg

@Bean

});

for( inti= 0;i<path.length;i ){

}

定义两个handle,用来处理用户连接和管理员连接时产生的动作。当用户连接成功或断开连接时,会向管理员推送消息,以便管理员获取在线用户,这里只贴出用户的handle

< groupId> org.springframework.boot </ groupId>

publicstaticsynchronized voidaddUser( WebSocketSession socketSession) {

* @param webSocketSession

< url> http://maven.aliyun.com/nexus/content/groups/public/ </ url>

MessageBean messageBean = gson.fromJson(msg,MessageBean.class);

*/

//setting和getting就不列出来了

* 发送消息给所有用户

< relativePath/> <!-- lookup parent from repository -->

<mirrorOf>repositoryId</mirrorOf>

String[] path = webSocketSession.getUri.getQuery.split( "&");

webSocketSession.sendMessage( newTextMessage( "在线人数为:" getUserOnlineCount));

< groupId> org.apache.tomcat.embed </ groupId>

webSocketSession.sendMessage( newTextMessage(msg));

</ dependency>

WebSocketUtils.sendMessageToAdmin( "发送失败");

<!-- 使用mvn命令打包成jar包时需要用到-->

for( inti= 0;i<path.length;i ){

e.printStackTrace;

< settingsxmlns= "http://maven.apache.org/SETTINGS/1.0.0"

System. out.println( "发送消息给用户失败:" e.getLocalizedMessage);

//在这里可以启动弹窗启动notification,后端传送的数据最好是json,这样容易解析

}

}