Android Netty使用(Kotlin+RxKotlin版)

之前写过一篇关于 Android Socket连接的博客,
当时是采用service的方式,自己在Service中创建Socket并且自己维护线程,线程切换起来很是麻烦,使得service中的代码看起来很臃肿。
后来炒菜机需要进行更新,正好当时已经开始用kotlin开发了,并且了解了一下比较出名的Netty,就把代码重构了。关于Netty的优点网上有很多介绍,这里就不赘述了。
新版本的socket客户端直接通过一个单例方式来实现,通过Rxjava的方式解决回调和线程切换的问题,使用起来代码简洁很多。

话不多说,先上图

在这里插入图片描述

大概就是这样,关于乱码的自己转一下就好了。这里只是个示例。
下面我们先来把NettyClient实现一下

首先下载 Netty的jar包(demo中也有jar文件,可以直接用,文章末尾会放出demo),下载地址,导入到项目中,并且添加RxKotlin和RxAndroid相关依赖。

如下
在这里插入图片描述

导入jar包和依赖后我们就可以编写我们的NettyClient类了,代码如下,我们一般要有连接,发送,断开,重连这几个方法,如果还需要其他方法,自己再加。


/**
 * @description: Netty客户端
 * @author : yzq
 * @date   : 2018/11/23
 * @time   : 14:24
 *
 */

class NettyClient {


    /*是否连接*/
    private var isConnect = false

    /*伴生对象*/
    companion object {
        var instance = NettyClient()
    }


    private lateinit var group: NioEventLoopGroup

    private lateinit var bootstrap: Bootstrap

    private var channel: Channel? = null


    /*连接*/
    fun connect(ip: String, port: String): Observable<Boolean> {

        return Observable.create<Boolean>({
            group = NioEventLoopGroup()
            bootstrap = Bootstrap()
                .remoteAddress(ip, port.toInt())
                .group(group)
                .channel(NioSocketChannel::class.java)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
                .handler(object : ChannelInitializer<SocketChannel>() {
                    override fun initChannel(sc: SocketChannel) {
                        var pipeline = sc.pipeline()
                        pipeline.addLast(LoggingHandler(LogLevel.INFO))

                        /*byte类型编解码*/
//                        pipeline.addLast(ByteArrayDecoder())
//                        pipeline.addLast(ByteArrayEncoder())

                        /*string类型编解码*/
                        pipeline.addLast(StringDecoder())
                        pipeline.addLast(StringEncoder())
                    }
                })


            try {
                channel = bootstrap.connect().sync().channel()
                it.onNext(channel!!.isActive)
                isConnect = channel!!.isActive
            } catch (e: Exception) {
                it.onNext(false)
                isConnect = false
            }
        }).subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())


    }


    /*发送命令*/
    fun sendOrder(order: String): Observable<Boolean> {


        return Observable.create<Boolean>({ emitter ->

            if (isConnect) {
                channel?.writeAndFlush(order)?.addListener {
                    emitter.onNext(it.isSuccess)
                }
            } else {
                emitter.onNext(false)
            }


        }).subscribeOn(Schedulers.io())//这里注意要在工作线程发送
            .observeOn(AndroidSchedulers.mainThread())


    }

    /*是否连接*/
    fun isConnect(): Boolean {
        return isConnect
    }

    /*重连*/
    fun reConnect(ip: String, port: String): Observable<Boolean> {
        disConnect()
        return connect(ip, port)

    }


    /*关闭连接*/
    fun disConnect() {

        isConnect = false
        group.shutdownGracefully()


    }


}

好了,NettyClient这样就写完了。我们来使用一下,布局文件很简单,我们直接来看Activity中的代码。

package com.yzq.nettydemo

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.text.TextUtils
import android.widget.Toast
import com.yzq.nettydemo.netty.NettyClient
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        /*连接按钮*/
        connectBtn.setOnClickListener {
            connectSocket()

        }

        /*发送按钮*/
        sendBtn.setOnClickListener {

            sendMsg()
        }

    }


    /*处理连接*/
    fun connectSocket() {

        /*拿到输入的ip和port*/

        var ip = ipEt.text.toString().trim()
        var port = portEt.text.toString().trim()


        /*这里可以对ip地址和端口号进行一下正则校验  这里我就不校验了*/

        if (TextUtils.isEmpty(ip) or TextUtils.isEmpty(port)) {
            showToast("ip和端口号不能为空,请检查")

            return
        }



        NettyClient.instance.connect(ip, port)
            .subscribe {

                if (it) {
                    showToast("已连接")
                } else {
                    showToast("连接失败")
                }
            }

    }


    /*处理发送*/
    private fun sendMsg() {

        var msg = contentEt.text.toString().trim()
        if (TextUtils.isEmpty(msg)) {
            showToast("您没有输入内容,请检查")

            return
        }

        NettyClient.instance.sendOrder(msg)
            .subscribe {

                if (it) {

                    /*发送成功*/
                    showToast("发送成功")
                } else {

                    showToast("发送失败")

                }
            }


    }

    fun showToast(msg: String) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
    }


    override fun onDestroy() {
        super.onDestroy()

        /*关闭连接  这里根据自己需求使用*/
        NettyClient.instance.disConnect()
    }
}

之前的版本我们是通过EventBus来进行通信的,新版本我们直接通过观察者模式来实现异步操作,使用起来方便很多。

下面是Demo
Netty Demo


如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!

©️2020 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值