Не получается связать Android приложение на Kotlin и сервер на Python с помощью сокета (Socket). Помогите ПОЖАЛУЙСТА

Я написал сервер на Python и клиент на Kotlin. Когда я запускаю в Android Studio приложение и нажимаю на кнопку, которая должна связаться с сервером, приложение просто вылетает!!! При этом когда я пишу тот же самый код не для андроид, а просто как Kotlin файл в Intellij Idea, всё прекрасно работает! Помогите пожалуйста, бошка уже раскалывается.

Код сервера на Python:

import socket

HOST = socket.gethostname()
print(socket.gethostbyname(HOST))

sock = socket.socket()
print('Socket created!')
sock.bind(('', 8000))
print('Socket bound!')
sock.listen(1)
print('Socket is now listening...')
conn, addr = sock.accept()
print('Connect with' + addr[0] + ': ' + str(addr[1]))

while True:
    data = conn.recv(1024)
    if not data:
        break

    print(data)

conn.close()

Вот клиент на Kotlin:

package com.example.instalog

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import java.io.DataOutputStream
import java.net.Socket

const val HOST = "192.168.3.5"
const val PORT = 8000

class MainActivity : AppCompatActivity() {

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

        submit_button.setOnClickListener { send() }

    }

    private fun send() {
        val socket = Socket(HOST, PORT)
        println("Socket created!")
        textView.text = "Socket created!"
        val writer = DataOutputStream(socket.getOutputStream())
        textView.text = "Writer created!"
        writer.writeUTF("Hello")
        textView.text = "Wrote!"
        writer.flush()
        textView.text = "Flushed!"
        writer.close()
        textView.text = "Writer closed!"
        socket.close()
        textView.text = "Socket closed!"
    }

}

Файл AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.instalog">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

И вот, если надо, клиент на Kotlin (не для Android), который РАБОТАЕТ!!!:

import java.io.DataOutputStream
import java.net.Socket

const val HOST = "localhost"
const val PORT = 8000

fun main() {

    val socket = Socket(HOST, PORT)
    val dout = DataOutputStream(socket.getOutputStream())

    dout.writeUTF("Hello")
    dout.flush()
    dout.close()
    socket.close()

}

Ответы (2 шт):

Автор решения: Sergei Buvaka

Как упомянулось в комментариях вероятнее всего вы ловите ошибку NetworkOnMainThreadException().

О чем говорит эта ошибка? О том что вы пытаетесь сделать сетевой запрос на UI потоке (в коде так же не видно, чтоб вы как-то выносили эту задачу на рабочий поток). Android система этого не разрешает потому как таким образом вы блокируете пользовательский интерфейс.

Что нужно сделать? Вам нужно вынести на рабочий поток ваш сетевой запрос, а результат вновь вывести на UI поток.

Для этого я вам рекомендую ознакомиться с такими штуками как Android Handler, RxJava - фреймворк или использовать Kotlin Coroutines . Так же рекомендую ознакомиться с библиотекой Retrofit.

Ну и в документации очень хорошо описано как работать с сетью в Android

→ Ссылка
Автор решения: Vlad Orlinskas

Создайте файл Coroutines.kt

val IO = object : CoroutineScope {
    override val coroutineContext = Dispatchers.IO
}

@Suppress("DeferredResultUnused")
fun io(block: suspend CoroutineScope.() -> (Unit)) {
    val catchingBlock: suspend CoroutineScope.() -> (Unit) = {
        try {
            block.invoke(this)
        } catch (throwable: Throwable) {
            Timber.e(throwable)
            Handler(Looper.getMainLooper())
                .post { throw throwable }
        }
    }

    IO.async(Dispatchers.IO, block = catchingBlock)
}

Если захотите разобраться https://kotlinlang.org/docs/reference/coroutines-overview.html

Добавьте зависимости

dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7"
}

Проверьте что бы у вас был kotlin 1.3.72

Теперь в любом месте приложения, вызывайте корутину следующим образом: (пример без коллбека).

fun doSomerthing(request: String) {
        io {
            // ваш код который выполнится в бэкграунде 
        }
}

Пример с коллбеком

fun sendSomerthing(request: String, callback: (Boolean) -> (Unit)) {
        io {
            val result = client.sendSomerthing(request)
            callback.invoke(result)
        }
}

А вот так вы можете получить результат выполнения этой функции по коллбеку:

repository.sendSomerthing(request) { result ->
   // теперь result это ваш ответ который вернет функция
}
→ Ссылка