android端
第一步 添加依赖,我使用的是Java-WebSocket
implementation "org.java-websocket:Java-WebSocket:1.4.0"
第二步 定义JWebSocketClient,实现WebSocketClient接口,实现其中onOpen()
,onMessage()
,onClose()
,onError()
方法。
package com.cngs.lcgh.service.ws;
import android.util.Log;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
/**
* WebSocket客户端
*
* @author 刘鹏
* @date 2021-04-20
*/
public class JWebSocketClient extends WebSocketClient {
public JWebSocketClient(URI serverUri) {
super(serverUri, new Draft_6455());
}
@Override
public void onOpen(ServerHandshake handshakedata) {
Log.e("JWebSocketClient", "onOpen()");
}
@Override
public void onMessage(String message) {
Log.e("JWebSocketClient", "onMessage()");
}
@Override
public void onClose(int code, String reason, boolean remote) {
Log.e("JWebSocketClient", "onClose()");
}
@Override
public void onError(Exception ex) {
Log.e("JWebSocketClient", "onError()");
}
}
第三步 定义一个自己的NotifyService,继承Service而来。实现一个方法,initSocketClient(String id)
初始化WebSocket连接。
/**
* 初始化websocket连接
*/
private void initSocketClient(String id) {
URI uri = URI.create(WebSocketConfig.ws);
client = new JWebSocketClient(uri) {
@Override
public void onMessage(String message) {
Log.e("JWebSocketClientService", "收到的消息:" + message);
sendMessage(message);
}
@Override
public void onOpen(ServerHandshake handshakedata) {
super.onOpen(handshakedata);
Log.e("JWebSocketClientService", "websocket连接成功");
sendMsg(id);
}
};
connect();
}
第四步 发送通知notifycation
/**
* 发送通知消息
*/
private void sendMessage(String content) {
Intent intent;
NotificationChannel notificationChannel = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationChannel = new NotificationChannel("important", "Important", NotificationManager.IMPORTANCE_LOW);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
assert notificationManager != null;
notificationManager.createNotificationChannel(notificationChannel);
}
Notification notification = new NotificationCompat.Builder(this, "important")
.setContentTitle("道路拥堵信息")
.setAutoCancel(false)
.setStyle(new NotificationCompat.BigTextStyle().bigText(content))
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.icon)
.build();
startForeground(16, notification);
}
第五步 在对应的activity
中启动当前NotifyService
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getPersimmions();
userId= PreferencesUtils.getUserId(MainActivity.this);
Intent myIntent = new Intent(this, NotifyService.class);
myIntent.putExtra("userId",userId);
startService(myIntent);
}
第六步 在AndroidManifest.xml
中注册NotifyService
服务
<service
android:name=".service.NotifyService"
android:enabled="true"
android:exported="true" />
代码
JWebSocketClient
:
package com.cngs.lcgh.service.ws;
import android.util.Log;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
/**
* WebSocket客户端
*
* @author 刘鹏
* @date 2021-04-20
*/
public class JWebSocketClient extends WebSocketClient {
public JWebSocketClient(URI serverUri) {
super(serverUri, new Draft_6455());
}
@Override
public void onOpen(ServerHandshake handshakedata) {
Log.e("JWebSocketClient", "onOpen()");
}
@Override
public void onMessage(String message) {
Log.e("JWebSocketClient", "onMessage()");
}
@Override
public void onClose(int code, String reason, boolean remote) {
Log.e("JWebSocketClient", "onClose()");
}
@Override
public void onError(Exception ex) {
Log.e("JWebSocketClient", "onError()");
}
}
WebSocketConfig
:
package com.cngs.lcgh.service.ws;
import android.content.Context;
import android.widget.Toast;
/**
* WebSocket配置类
*
* @author 刘鹏
* @date 2021-04-20
*/
public class WebSocketConfig {
public static final String ws = "ws://11.11.12.139:9002/ws";//websocket测试地址
}
}
NotifyService
package com.cngs.lcgh.service;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.cngs.lcgh.MainActivity;
import com.cngs.lcgh.R;
import com.cngs.lcgh.service.ws.JWebSocketClient;
import com.cngs.lcgh.service.ws.WebSocketConfig;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
public class NotifyService extends Service {
public JWebSocketClient client;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d("MyService", "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("MyService", "onStartCommand");
super.onStartCommand(intent, flags, startId);
String userId = intent.getStringExtra("userId");
initSocketClient(userId);
return START_STICKY;
}
/**
* 发送通知消息
*/
private void sendMessage(String content) {
Intent intent;
NotificationChannel notificationChannel = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationChannel = new NotificationChannel("important", "Important", NotificationManager.IMPORTANCE_LOW);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
assert notificationManager != null;
notificationManager.createNotificationChannel(notificationChannel);
}
Notification notification = new NotificationCompat.Builder(this, "important")
.setContentTitle("道路拥堵信息")
.setAutoCancel(false)
.setStyle(new NotificationCompat.BigTextStyle().bigText(content))
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.icon)
.build();
startForeground(16, notification);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyService", "onDestroy");
}
/**
* 初始化websocket连接
*/
private void initSocketClient(String id) {
URI uri = URI.create(WebSocketConfig.ws);
client = new JWebSocketClient(uri) {
@Override
public void onMessage(String message) {
Log.e("JWebSocketClientService", "收到的消息:" + message);
sendMessage(message);
}
@Override
public void onOpen(ServerHandshake handshakedata) {
super.onOpen(handshakedata);
Log.e("JWebSocketClientService", "websocket连接成功");
sendMsg(id);
}
};
connect();
}
/**
* 连接websocket
*/
private void connect() {
new Thread() {
@Override
public void run() {
try {
//connectBlocking多出一个等待操作,会先连接再发送,否则未连接发送会报错
client.connectBlocking();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
/**
* 发送消息
*
* @param msg
*/
public void sendMsg(String msg) {
if (null != client) {
Log.e("JWebSocketClientService", "发送的消息:" + msg);
client.send(msg);
}
}
}
java后端
使用javax.websocket-api来实现,基于tomcat,如果是spring-boot项目,可以用spring-websocket。因为我们原来项目不是springboot项目,所以只能用javax.websocket-api。使用百度地图api去获取道路实况信息。
百度地图实时路况API:http://lbsyun.baidu.com/index.php?title=webapi/traffic
第一步 添加依赖
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
第二步 实现一个WebSocketServer,来实现一个websocket连接。用@ServerEndpoint("/ws")
注解。
package com.hyty.cnlcgh.maps.websocket;
import com.hyty.cnlcgh.maps.service.IRoadCongestionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ContextLoader;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* websocket
*
* @author 刘鹏
* @date 2021/4/21 14:52
*/
@ServerEndpoint("/ws")
@Component
public class WebSocketServer {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
private String uid;
@Autowired
public IRoadCongestionService roadCongestionService;
/**
* 连接建立成功调用的方法
*
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
addOnlineCount();
logger.info("有新连接加入!当前在线人数为" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
if (webSocketSet.contains(this)){
webSocketSet.remove(this);
}
subOnlineCount(); //在线数减1
logger.info("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
logger.info("================来自客户端的消息:" + message);
roadCongestionService = ContextLoader.getCurrentWebApplicationContext().getBean(IRoadCongestionService.class);
// 当当前id判断不是对应组织的人的时候就不推送消息
String org = "38";
if (null != message){
if (roadCongestionService.judgeOrgById(message, org)) {
this.uid = message;
webSocketSet.add(this);
logger.info("当前在线有效人数为" + webSocketSet.size());
}
}
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) {
logger.error("发生错误");
error.printStackTrace();
}
/**
* 发送消息
*
* @throws IOException
*/
public void sendMessage(String context) throws IOException {
logger.info("================推送的消息:" + context);
this.session.getBasicRemote().sendText(context);
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {
return webSocketSet;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public static void setWebSocketSet(CopyOnWriteArraySet<WebSocketServer> webSocketSet) {
WebSocketServer.webSocketSet = webSocketSet;
}
}
这个类中我在onMessage()方法中实现了自己的逻辑,android端建立连接的时候发送一个当前用户id过来,这边接收到这个id,判断当前id是否是我们待会儿准备推送消息的人,是的话将当前示例添加到set中,声明了一个私有变量uid,是为了后面推送消息的时候使用。
第三步 实现定时任务发送消息到对应id的用户
package com.hyty.cnlcgh.maps.websocket;
import com.hyty.cnlcgh.maps.service.IRoadCongestionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ContextLoader;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* 定时推送消息
*
* @author 刘鹏
* @date 2021/4/22 14:23
*/
@Component
public class MessageTask {
@Autowired
public IRoadCongestionService roadCongestionService;
private static final Logger logger = LoggerFactory.getLogger(MessageTask.class);
@Scheduled(cron = "0 0/3 * * * ?") // 每隔3分钟向android推送一次
public void sendMessageTask(){
logger.info("********* 定时任务:推送消息到android端 执行 **************");
CopyOnWriteArraySet<WebSocketServer> webSocketSet = WebSocketServer.getWebSocketSet();
roadCongestionService = ContextLoader.getCurrentWebApplicationContext().getBean(IRoadCongestionService.class);
for (WebSocketServer item:webSocketSet) {
try {
String content = null;
// A大队
if (roadCongestionService.judgeOrgById(item.getUid(), "54")){
content = buildMessageCongestion(1);
}
// B大队
if (roadCongestionService.judgeOrgById(item.getUid(), "55")){
content = buildMessageCongestion(2);
}
// C大队
if (roadCongestionService.judgeOrgById(item.getUid(), "56")){
content = buildMessageCongestion(3);
}
if (null != content){
item.sendMessage(content);
}
} catch (IOException e) {
e.printStackTrace();
}
}
logger.info("/n 定时任务:推送消息到android端 完成.......");
}
/**
* 构建返回消息
*
* @param type 判断属于是哪个大队 1 A 2 B 3 C
* @return String
*/
public String buildMessageCongestion(int type) {
roadCongestionService = ContextLoader.getCurrentWebApplicationContext().getBean(IRoadCongestionService.class);
return roadCongestionService.getCongestion(type);
}
}
CopyOnWriteArraySet<WebSocketServer> webSocketSet = WebSocketServer.getWebSocketSet();
从websocket中取出当时建立连接时候的server。通过遍历set判断给不同的人推送不同的消息。
item.sendMessage(content);
调用websocket的发送消息方法将消息推送到android端
代码
IRoadCongestionService
:
package com.hyty.cnlcgh.maps.service;
import com.hyty.cnlcgh.maps.entity.Congestion;
import java.util.List;
/**
* 道路拥堵情况接口
*
* @author 刘鹏
* @date 2021/3/24 11:38
*/
public interface IRoadCongestionService {
/**
* 获取拥堵信息路段,组成String类型,供websocket调用返回给前端
*
* @param type 判断属于是哪个大队
* @return String
*/
String getCongestion(int type);
/**
* 判断是否是该组织的人
*
* @param uid 用户id
* @param orgId 组织id
* @return 是否
*/
boolean judgeOrgById(String uid, String orgId);
}
RoadCongestionServiceImpl
:
package com.hyty.cnlcgh.maps.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.hyty.cnlcgh.maps.dao.IRoadMapper;
import com.hyty.cnlcgh.maps.entity.Congestion;
import com.hyty.cnlcgh.maps.service.IRoadCongestionService;
import org.apache.http.client.fluent.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 道路拥堵情况实现类
*
* @author 刘鹏
* @date 2021/3/24 11:45
*/
@Service
public class RoadCongestionServiceImpl implements IRoadCongestionService {
@Autowired
public IRoadMapper roadMapper;
private static final Logger logger = LoggerFactory.getLogger(RoadCongestionServiceImpl.class);
/**
* 定时任务从百度地图API获取对应道路拥堵情况并存放数据库
*/
@Scheduled(cron = "0 0/10 * * * ?") // 每隔10分钟请求一次百度地图API
public void congestionTask() {
logger.info("----------------定时任务(访问百度API获取道路拥堵信息) 开始:----------------");
String ak = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";//百度地图API控制台应用的AK
String BaseUrl = "http://api.map.baidu.com/traffic/v1/road?";
List<Congestion> list = roadMapper.selectAll();
for (Congestion item : list) {
String url = BaseUrl + "road_name=" + item.getRoadName() + "&city=" + item.getCityName() + "&ak=" + ak;
try {
String result = Request.Get(url)
.connectTimeout(1000).socketTimeout(1000)
.execute().returnContent().asString();
JSONObject jsonResult = JSONObject.parseObject(result);
Congestion congestion = new Congestion();
congestion.setId(item.getId());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分");
congestion.setCurTime(sdf.format(new Date()));
congestion.setCongestion(jsonResult.getString("description"));
if (roadMapper.updateById(congestion) <= 0) {
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
logger.info("----------------定时任务(访问百度API获取道路拥堵信息) 结束----------------");
}
/**
* 获取道路拥堵情况生成String
*
* @param type 判断属于哪个大队
* @return string
*/
@Override
public String getCongestion(int type) {
StringBuilder sb = new StringBuilder();
String filterName1 = "拥堵";
String filterName2 = "缓慢";
List<Integer> idList = new ArrayList<>();
switch (type) {
case 1:
idList.add(1);
idList.add(3);
break;
case 2:
idList.add(2);
idList.add(4);
idList.add(6);
break;
case 3:
idList.add(5);
break;
}
List<Congestion> congestionList = roadMapper.selectAllCongestion(filterName1, filterName2, idList);
if (congestionList.size() > 0) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分");
sb.append(sdf.format(new Date())).append(",");
for (Congestion cong : congestionList) {
String city = cong.getCityName().replaceAll("市", "段");
String Domingo = cong.getCongestion();
sb.append(Domingo);
}
return sb.toString();
}
return null;
}
/**
* 判断是否是该组织的人
*
* @param userId 用户id
* @param orgId 组织id
* @return 是否
*/
@Override
public boolean judgeOrgById(String userId, String orgId) {
return roadMapper.countNumChengNanById(userId, orgId) > 0;
}
}