**

Android实现基于webSocket的事件回传达到同屏操控效果

在某些需求开发中,我们也许会遇到这样的需求:实现一个App内部的同屏控制,一台设备控制多台设备进行同步点击。达到同屏的效果。同屏的实现有很多种方法,可以采用rtmp手机录屏推流进行实时直播、也可采用截屏的方式对手机屏幕进行实时socket等,但是这些都是基于屏幕分享的形式。
这里我们不采用屏分享,博主的解决方案是对app内部的点击事件进行实时消息推送(适用于小型软件),我点那里,其他设备跟着一起点哪里。要实现这样的效果传统的http协议当然是不行的,因此我们采用可以长连接的webSocket,采用webSocket建立一个长连接平台。具体实现步骤如下:
(以下为Android端的实现步骤,服务器端的实现请看这里[])

一、 gradle引入netty依赖

//netty
implementation group: 'io.netty', name: 'netty-all', version: '4.1.50.Final'

二、 新建一个ChartClient类用于连接服务器

发起连接的方法

public void connect(){
    group = new NioEventLoopGroup();
    Bootstrap bootstrap = new Bootstrap();
    try {
        bootstrap.group(group);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                pipeline.addLast("decoder",new StringDecoder());
                pipeline.addLast("encoder",new StringEncoder());
                pipeline.addLast(new ChartClientHandler());
            }
        });

        ChannelFuture cf = bootstrap.connect(host, port).sync();
        mChannel = cf.channel();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

发送消息的方法(当发送消息的时候,我们使用Handler发送消息到主线程去控制提示信息的显示,告诉用户现在的连接状态)

public void sendData(String msg){

    if (mChannel == null){
        Message message = new Message();
        message.what = 400;
        App.getMsgHandler().sendMessage(message);
        return;
    }
    if (mChannel.isOpen()){
        try{
            mChannel.writeAndFlush(msg+"\n");
        }catch (Exception e){
            e.printStackTrace();
            Message message = new Message();
            message.what = 446;
            App.getMsgHandler().sendMessage(message);
        }

    }else {
        Message message = new Message();
        message.what = 400;
        App.getMsgHandler().sendMessage(message);
    }

}

断开连接的方法

public void disConnect(){
    if (mChannel == null){
        Message message = new Message();
        message.what = 400;
        App.getMsgHandler().sendMessage(message);
        return;
    }
    if (mChannel.isOpen()){
        mChannel.close();
    }else {
        Message message = new Message();
        message.what = 600;
        App.getMsgHandler().sendMessage(message);
    }
    if (!group.isShutdown()){
        group.shutdownGracefully();
    }
}

与服务器端一样,我们通过Handler进行消息处理,所以我们需要创建一个Handler继承一下SimpleChannelInboundHandler

重写channelRead0方法用于读取从服务器发过来的消息

@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {

    Message message = new Message();
    message.what=0x01;
    Bundle mBUndle = new Bundle();
    mBUndle.putString("msg",msg);
    message.setData(mBUndle);
    App.getMsgHandler().sendMessage(message);
}

这里我们拿到消息以后,使用Handler将消息发送到主线程进行处理。
因为我们要控制的是事件的同步点击,因此我们要在主程序的Application里面接收消息。
这里最好新建一个App类继承自我们的Application,在app里面写一个Handler用于处理我们的消息回传

android 多应用同屏 android同屏怎么用_android


android 多应用同屏 android同屏怎么用_服务器_02

到这里我们已经实现了服务器的连接和消息的接收,接下来,我们要处理消息的发送和事件的自动触发

实现消息的发送首先我们要先连接服务器,我们在程序启动的时候,就向服务器发起连接

android 多应用同屏 android同屏怎么用_android 多应用同屏_03

在mainActivity中的onCreate方法中创建一个连接对象、再创建一个子线程去发起连接,并捕获连接异常。

android 多应用同屏 android同屏怎么用_android 多应用同屏_04

Screen是我封装的一个对象,用于存放事件类型和主机地址、连接状态、是否主控、是否被控、端口号等等的,大家可以根据自己的需要也去封装一个。

设备接入以后我们需要检查一下是否处于连接状态,如果有主控设备正在控制中,设备将自动同步到主控屏幕的屏幕状态(这里是采用字符串的形式进行传参,后端根据需要截取字符串,并判断事件类型,再对事件进行分发处理)

android 多应用同屏 android同屏怎么用_App_05

所以我们发送事件是通过调用chartClient的sendData()方法直接将事件参数拼接成字符串发送到服务器端,服务器端再通过对消息的处理,回传回来对应的时间信息,如果用户的设备是非主控设备,那么设备就会自动执行事件。

客服端收到消息后,在App中的handler里面进行处理,我的处理方式是根据事件类型去判断触发了申明事件,从而让我们的app自动去点击

android 多应用同屏 android同屏怎么用_websocket_06


消息都是发到对应的主线程去执行。最终实现效果如下:

android 多应用同屏 android同屏怎么用_App_07


注:演示应用是适配平板的,所以ui看起来有些奇怪

**