android 集成websocket,并发送通知

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;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容