Android Socket通讯 分离服务端和客户端发送表情消息

Posted 初学者-Study

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Socket通讯 分离服务端和客户端发送表情消息相关的知识,希望对你有一定的参考价值。

分离服务端和客户端、发送表情消息

前言

  在之前写的Socket通讯中,完成了基本的客户端和服务端的通讯,功能比较简单,本文将对功能进行一次升级。完成后效果图如下:

正文

  功能升级分为两个环节,页面优化,消息类型增加。首先来说就是页面的优化,之前全部写在一个页面里面,虽然可行,但是会显得很臃肿,不利于解读和维护。

一、页面优化

  页面优化内容还是比较多的,首先要做的就是分离页面。在com.llw.socket包下新建一个ui包。

① 分包

这个包下新建一个BaseActivity,代码如下:

open class BaseActivity: AppCompatActivity() 

    protected fun getIp() =
        intToIp((applicationContext.getSystemService(WIFI_SERVICE) as WifiManager).connectionInfo.ipAddress)

    private fun intToIp(ip: Int) =
        "$(ip and 0xFF).$(ip shr 8 and 0xFF).$(ip shr 16 and 0xFF).$(ip shr 24 and 0xFF)"

    protected fun showMsg(msg: String) = Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()

    protected open fun jumpActivity(clazz: Class<*>?) = startActivity(Intent(this, clazz))


这里是作为一个基类,后续我们写关于Activity的都放在这个下面。

在com.llw.socket包下新建一个adapter包,将MsgAdapter移到adapter包下。

在com.llw.socket包下新建一个bean包,将Message移到bean包下。

② 创建ServerActivity

  在创建之前我们需要改动一下样式,因为之前是使用自带的ActionBar,现在我们需要去掉,改成NoActionBar,如下图所示:

  然后在drawable文件夹下新建一个ic_back_black.xml,作为页面的返回图标,代码如下:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:autoMirrored="true"
    android:tint="#000000"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">

    <path
        android:fillColor="@android:color/white"
        android:pathData="M19,11H7.83l4.88,-4.88c0.39,-0.39 0.39,-1.03 0,-1.42l0,0c-0.39,-0.39 -1.02,-0.39 -1.41,0l-6.59,6.59c-0.39,0.39 -0.39,1.02 0,1.41l6.59,6.59c0.39,0.39 1.02,0.39 1.41,0l0,0c0.39,-0.39 0.39,-1.02 0,-1.41L7.83,13H19c0.55,0 1,-0.45 1,-1l0,0C20,11.45 19.55,11 19,11z" />

</vector>

  在ui包下新建一个ServerActivity,对应布局是activity_server.xml,布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".ui.ServerActivity">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/purple_500"
        app:navigationIcon="@drawable/ic_back_black"
        app:navigationIconTint="@color/white"
        app:subtitleTextColor="@color/white"
        app:title="服务端"
        app:titleTextColor="@color/white">

        <TextView
            android:id="@+id/tv_start_service"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:layout_marginEnd="16dp"
            android:text="开启服务"
            android:textColor="@color/white"
            android:textSize="14sp" />
    </com.google.android.material.appbar.MaterialToolbar>

    <LinearLayout
        android:id="@+id/lay_client"
        android:layout_width="match_parent"
        android:layout_height="110dp"
        android:orientation="vertical"
        android:visibility="gone">

        <com.google.android.material.textfield.TextInputLayout
            android:id="@+id/op_code_layout"
            style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/et_ip_address"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:digits="0123456789."
                android:hint="连接Ip地址"
                android:inputType="number"
                android:lines="1"
                android:singleLine="true"
                android:text="192.168.0.120" />
        </com.google.android.material.textfield.TextInputLayout>

        <Button
            android:id="@+id/btn_connect_service"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp"
            android:text="连接服务"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/btn_start_service" />
    </LinearLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_msg"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical"
        android:paddingStart="16dp"
        android:paddingEnd="16dp">

        <androidx.appcompat.widget.AppCompatEditText
            android:id="@+id/et_msg"
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:layout_weight="1"
            android:background="@drawable/shape_et_bg"
            android:gravity="center_vertical"
            android:hint="发送给客户端"
            android:padding="10dp"
            android:textSize="14sp" />

        <com.google.android.material.button.MaterialButton
            android:id="@+id/btn_send_msg"
            android:layout_width="80dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:text="发送"
            app:cornerRadius="8dp" />
    </LinearLayout>
</LinearLayout>

然后我们再修改一下ServerActivity中的代码,如下所示:

class ServerActivity : BaseActivity(), ServerCallback 

    private val TAG = ServerActivity::class.java.simpleName
    private lateinit var binding: ActivityServerBinding

    //Socket服务是否打开
    private var openSocket = false

    //消息列表
    private val messages = ArrayList<Message>()

    //消息适配器
    private lateinit var msgAdapter: MsgAdapter

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        binding = ActivityServerBinding.inflate(layoutInflater)
        setContentView(binding.root)

        initView()
    

    private fun initView() 
        binding.toolbar.apply 
            subtitle = "IP:$getIp()"
            setNavigationOnClickListener  onBackPressed() 
        
        //开启服务/关闭服务 服务端处理
        binding.tvStartService.setOnClickListener 
            openSocket = if (openSocket) 
                SocketServer.stopServer();false
             else SocketServer.startServer(this)
            //显示日志
            showMsg(if (openSocket) "开启服务" else "关闭服务")
            //改变按钮文字
            binding.tvStartService.text = if (openSocket) "关闭服务" else "开启服务"
        

        //发送消息给客户端
        binding.btnSendMsg.setOnClickListener 
            val msg = binding.etMsg.text.toString().trim()
            if (msg.isEmpty()) 
                showMsg("请输入要发送的信息");return@setOnClickListener
            
            //检查是否能发送消息
            val isSend = if (openSocket) openSocket else false
            if (!isSend) 
                showMsg("当前未开启服务或连接服务");return@setOnClickListener
            
            SocketServer.sendToClient(msg)
            binding.etMsg.setText("")
            updateList(1, msg)
        
        //初始化列表
        msgAdapter = MsgAdapter(messages)
        binding.rvMsg.apply 
            layoutManager = LinearLayoutManager(this@ServerActivity)
            adapter = msgAdapter
        
    

    override fun onOptionsItemSelected(item: MenuItem): Boolean 
        when(item.itemId) 
            android.R.id.home -> onBackPressed()
        
        return super.onOptionsItemSelected(item)
    

    /**
     * 接收到客户端发的消息
     */
    override fun receiveClientMsg(success: Boolean, msg: String) = updateList(2, msg)

    override fun otherMsg(msg: String) 
        Log.d(TAG, msg)
    

    /**
     * 更新列表
     */
    private fun updateList(type: Int, msg: String) 
        messages.add(Message(type, msg))
        runOnUiThread 
            (if (messages.size == 0) 0 else messages.size - 1).apply 
                msgAdapter.notifyItemChanged(this)
                binding.rvMsg.smoothScrollToPosition(this)
            
        
    

  在这里我首先继承自BaseActivity,这样可以使用父类的方法,然后实现ServerCallback,就可以收到客户端发送过来的消息。同时我们不用再去判断当前是客户端还是服务端,因为当我们进入这个页面那就是服务端了。

③ 创建ClientActivity

  在ui包下新建一个ClientActivity,对应布局是activity_client.xml,布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".ui.ClientActivity">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/purple_500"
        app:navigationIcon="@drawable/ic_back_black"
        app:navigationIconTint="@color/white"
        app:title="客户端"
        app:titleTextColor="@color/white">

        <TextView
            android:id="@+id/tv_connect_service"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:layout_marginEnd="16dp"
            android:text="连接服务"
            android:textColor="@color/white"
            android:textSize="14sp" />
    </com.google.android.material.appbar.MaterialToolbar>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_msg"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical"
        android:paddingStart="16dp"
        android:paddingEnd="16dp">

        <androidx.appcompat.widget.AppCompatEditText
            android:id="@+id/et_msg"
            androi

以上是关于Android Socket通讯 分离服务端和客户端发送表情消息的主要内容,如果未能解决你的问题,请参考以下文章

Android Socket通讯 分离服务端和客户端发送表情消息

Android Socket通讯

Android Socket通讯

Android Socket通讯

51. Socket服务端和客户端使用TCP协议通讯 | 厚土Go学习笔记

java中TCP服务端和客服端通讯