KotlinでTCP/IP送信


はじめに

AndroidでTCP/IP通信をしようとするとうまくいかないので、その対応方法の記録です。
2018年ごろに作られているTCP/IPのサンプルがまったく動作しないのでその原因を調査しました。

サンプル1

MainActivityのボタンクリックイベント内に

sample1.kt
        var socket : Socket? = null
        try {
            val socket = Socket("localhost", 12345)
            val writer : PrintWriter  = PrintWriter(socket.getOutputStream(), true)
            writer.println("serial test")
        }
        if (socket != null) {
            socket.close()
        }

と代表的なサンプルを書いて動作させてみましょう。
コメントの方にすばらしく簡潔なサンプルがありますのでそちらも参考にして下さい。
これは動作しません。動作しないと言うより接続に行きません。
エラー内容を見るとパーミッションを指定して下さいと表示されるのでAndroidManifest.xmlを修正します。

AndroidManifest.xml
    <application
        中略
        android:usesCleartextTraffic="true"
        中略
    </application>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

INTERNETの指定がインターネットへ接続可能にする設定
ACCESS_NETWORK_STATEはネットワークの様々なやりとりを有効にする設定
android:usesCleartextTraffic="true"は暗号化無しの通信を許可する設定です。

INTERNET以外の設定は不要と思いますが今後のバージョンアップでまた悩むのが嫌なのであらかじめ付けてます。

そして実行すると今度はアプリが落ちます。
エラーメッセージに若干のヒントはあるのですが要は「ボタンクリックイベントから呼ぶな」と言っているようです。

サンプル2

じゃあ簡単なスレッドを作ってその中で動作させてみます。
コメントの方にすばらしく簡潔なサンプルがありますのでそちらも参考にして下さい。

sample.kt
        val runnable = object : Runnable {
            override fun run() {
                var socket : Socket? = null
                try {
                    socket = Socket("localhost", 12345)
                    val writer : PrintWriter  = PrintWriter(socket.getOutputStream(), true)
                    writer.println("serial test")
                } finally {
                    if (socket != null) {
                        socket.close()
                    }
                }
            }
        }
        val thread = Thread(runnable)
        thread.start()

今度は動作します。

まとめ

元々接続処理を行うと指定しても一定時間処理が返ってこないのでメインで処理する物ではありませんが、だからといってアプリごと落とさなくても・・・