之前写过一篇关于 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
如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!