android - 在服务器端使用 Spring 框架在 android 中设置 Stomp 客户端

标签 android spring websocket stomp

我正在开发一个 android 应用程序,它与在 Spring 中配置的码头服务器交换数据。为了获得更动态的 android 应用程序,我正在尝试将 WebSocket 协议(protocol)与 Stomp 消息一起使用。

为了实现这些东西,我在 spring 中配置了一个 web 套接字消息代理:

@Configuration
//@EnableScheduling
@ComponentScan(
        basePackages="project.web",
        excludeFilters = @ComponentScan.Filter(type= FilterType.ANNOTATION, value = Configuration.class)
)
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/message");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/client");
    }
}

和 Spring Controller 中的 SimpMessageSendingOperations 将消息从服​​务器发送到客户端:

@Controller
public class MessageAddController {
    private final Log log = LogFactory.getLog(MessageAddController.class);

    private SimpMessageSendingOperations messagingTemplate;

    private UserManager userManager;

    private MessageManager messageManager;

    @Autowired
    public MessageAddController(SimpMessageSendingOperations messagingTemplate, 
            UserManager userManager, MessageManager messageManager){
        this.messagingTemplate = messagingTemplate;
        this.userManager = userManager;
        this.messageManager = messageManager;
    }

    @RequestMapping("/Message/Add")
    @ResponseBody
    public SimpleMessage addFriendship(
            @RequestParam String content,
            @RequestParam Long otherUser_id
    ){
        if(log.isInfoEnabled())
            log.info("Execute MessageAdd action");
        SimpleMessage simpleMessage;

        try{
            User curentUser = userManager.getCurrentUser();
            User otherUser = userManager.findUser(otherUser_id);

            Message message = new Message();
            message.setContent(content);
            message.setUserSender(curentUser);
            message.setUserReceiver(otherUser);

            messageManager.createMessage(message);          
            Message newMessage = messageManager.findLastMessageCreated();

            messagingTemplate.convertAndSend( 
                    "/message/add", newMessage);//send message through websocket

            simpleMessage = new SimpleMessage(null, newMessage);
        } catch (Exception e) {
            if(log.isErrorEnabled())
                log.error("A problem of type : " + e.getClass() 
                        + " has occured, with message : " + e.getMessage());
            simpleMessage = new SimpleMessage(
                            new SimpleException(e.getClass(), e.getMessage()), null);
        }
        return simpleMessage;
    }
}

当我使用 stomp.js 在 Web 浏览器中测试此配置时,我没有任何问题:消息在 Web 浏览器和 Jetty 服务器之间完美交换。用于网络浏览器测试的 JavaScript 代码:

    var stompClient = null;

    function setConnected(connected) {
        document.getElementById('connect').disabled = connected;
        document.getElementById('disconnect').disabled = !connected;
        document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
        document.getElementById('response').innerHTML = '';
    }

    function connect() {
        stompClient = Stomp.client("ws://YOUR_IP/client");         
        stompClient.connect({}, function(frame) {
            setConnected(true);
            stompClient.subscribe('/message/add', function(message){
                showMessage(JSON.parse(message.body).content);
            });
        });
    }

    function disconnect() {
        stompClient.disconnect();
        setConnected(false);
        console.log("Disconnected");
    }


    function showMessage(message) {
        var response = document.getElementById('response');
        var p = document.createElement('p');
        p.style.wordWrap = 'break-word';
        p.appendChild(document.createTextNode(message));
        response.appendChild(p);
    }

当我尝试在 Android 中使用带有 gozirra、activemq-stomp 或其他库的 stomp 时出现问题:大多数时候,与服务器的连接不起作用。我的应用程序停止运行,几分钟后,我在 logcat 中收到以下消息:java.net.UnknownHostException: Unable to resolve host "ws://192.168.1.39/client": No address associated with hostname ,我不明白为什么。使用 Gozzira 库的代码,它管理我的 android Activity 中的 stomp 吸引力:

private void stomp_test() {
    String ip = "ws://192.172.6.39/client";
    int port = 8080;

    String channel = "/message/add";
    Client c;

    try {
        c = new Client( ip, port, "", "" );
        Log.i("Stomp", "Connection established");
        c.subscribe( channel, new Listener() {
            public void message( Map header, String message ) {
                Log.i("Stomp", "Message received!!!");
              }
        });

    } catch (IOException ex) {
        Log.e("Stomp", ex.getMessage());
        ex.printStackTrace();

    } catch (LoginException ex) {
        Log.e("Stomp", ex.getMessage());
        ex.printStackTrace();
    } catch (Exception ex) {
        Log.e("Stomp", ex.getMessage());
        ex.printStackTrace();
    }

}

经过一些研究,我发现大多数想通过 Java 客户端使用 stomp over websocket 的人都使用 ActiveMQ 服务器,就像这个 site .但是 spring 工具使用起来非常简单,如果我能像现在这样保持我的服务器层,那就太棒了。有人会知道如何在服务器端使用 Spring 配置在客户端使用 stomp java (Android) 吗?

最佳答案

我使用 RxJava 为 android(或纯 java)实现 STOMP 协议(protocol) https://github.com/NaikSoftware/StompProtocolAndroid .使用 SpringBoot 在 STOMP 服务器上测试。 简单示例(使用 retrolambda ):

private StompClient mStompClient;

 // ...

 mStompClient = Stomp.over(WebSocket.class, "ws://localhost:8080/app/hello/websocket");
 mStompClient.connect();

 mStompClient.topic("/topic/greetings").subscribe(topicMessage -> {
     Log.d(TAG, topicMessage.getPayload());
 });

 mStompClient.send("/app/hello", "My first STOMP message!");

 // ...

 mStompClient.disconnect();

在项目中添加以下类路径:

  classpath 'me.tatarka:gradle-retrolambda:3.2.0'

在您的应用程序 build.gradle 中添加以下内容:

apply plugin: 'me.tatarka.retrolambda'

android {
       .............
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}


  dependencies {
      ............................
    compile 'org.java-websocket:Java-WebSocket:1.3.0'
    compile 'com.github.NaikSoftware:StompProtocolAndroid:1.1.5'
}

所有异步工作!您可以在subscribe()send() 之后调用connect(),消息将被推送到队列。

附加功能:

  • 用于握手查询的额外 HTTP header (用于传递身份验证 token 或其他)
  • 您可以为库实现自己的传输,只需实现接口(interface) ConnectionProvider
  • 订阅连接生命周期事件(连接、关闭、错误)

例如:

public class MainActivity extends AppCompatActivity {
    private StompClient mStompClient;
    public static  final String TAG="StompClient";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button view = (Button) findViewById(R.id.button);
        view.setOnClickListener(e->  new LongOperation().execute(""));


    }


        private class LongOperation extends AsyncTask<String, Void, String> {
        private StompClient mStompClient;
        String TAG="LongOperation";
        @Override
        protected String doInBackground(String... params) {

            mStompClient = Stomp.over(WebSocket.class, "ws://localhost:8080/app/hello/websocket");
            mStompClient.connect();

            mStompClient.topic("/topic/greetings").subscribe(topicMessage -> {
                Log.d(TAG, topicMessage.getPayload());
            });

            mStompClient.send("/app/hello", "My first STOMP message!").subscribe();


            mStompClient.lifecycle().subscribe(lifecycleEvent -> {
                switch (lifecycleEvent.getType()) {

                    case OPENED:
                        Log.d(TAG, "Stomp connection opened");
                        break;

                    case ERROR:
                        Log.e(TAG, "Error", lifecycleEvent.getException());
                        break;

                    case CLOSED:
                        Log.d(TAG, "Stomp connection closed");
                        break;
                }
            });
            return "Executed";
        }

        @Override
        protected void onPostExecute(String result) {

        }

    }
}

在manifest.xml中添加上网权限

<uses-permission android:name="android.permission.INTERNET" />

关于android - 在服务器端使用 Spring 框架在 android 中设置 Stomp 客户端,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24346068/

相关文章:

Android - 重新膨胀 fragment 会导致 "Error inflating class fragment"

java - 如何在android中复制文件?

Django、socket.io、node.js - 管理私有(private)消息和群组对话

Java Jetty WebSocket 服务器 : Handle broadcasts with asynchronously disconnecting clients

javascript - NodeJS 和 Web 套接字 : Check if socket origin is the same as the web socket server

java - 在 TextView 中显示 json 值

java - Android .java 文件正在获取的完整格式链列表是什么?

java.lang.IllegalArgumentException : ResourceLoader must not be null in Spring

java - Spring 的 @EntityListeners 在使用 @Postpersist 时抛出异常

spring - @Autowired - 没有为依赖至少 1 个 bean 找到符合条件的 bean