rokkonet

PC・Androidソフトウェア・アプリの開発・使い方に関するメモ

android開発 coroutineによる非同期スレッド実行

2021 Jul. 24.

参考ページ
雰囲気で利用しないためのAndroidにおけるKotlin-Coroutineメモ - Qiita
非同期処理のCoroutine(コルーチン)を始めてみよう!【Android・Kotlin】

ライブラリ導入

build.gradle(Module/App)に下記を記述するだけ
jarファイルの配置は不要

dependencies {
   implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1'
}


概要

メインスレッドクラスにCoroutineScopeをインプリメントする
launch{ }の中に非同期スレッド処理を記述する
launch{ }内では、withContext( ){ }でスレッドを使い分ける
 withContext(Dispatchers.Main) { }:メインスレッド処理
 withContext(Dispatchers.Default) { }:非同期スレッド処理
 withContext(Dispatchers.IO) { }:入出力関係非同期スレッド処理
launch{ }内では、withContext( )の指定がなければ、withContext(Dispatchers.Main)と同じ
async(Dispatchers.XX){ }.await( )では、ブロック内がDispatchers.XXな非同期スレッドとなり、また、その実行終了を待ってメインスレッドに戻る
async(Dispatchers.XX){ }ではブロック最終行の結果が返り値となる。"val VAL=async(Dispatchers.XX){ }.await( )"でVALをメインスレッドに渡すことができる
非同期スレッド中でのスレッドの中断はThread.sleep( )ではなくdelay( )を使う
非同期スレッド処理を関数化する場合は関数定義を "suspend fun" とする

メインクラスに書いておくもの

class MainActivity : AppCompatActivity(), CoroutineScope {

    // coroutineの準備
    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job
    val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
        // 新しいコルーチンの作成時や withContext の呼び出し時に Dispatchers をハードコードしない
        //     https://developer.android.com/kotlin/coroutines/coroutines-best-practices?hl=ja

    // 終了時のcoroutineのキャンセル設定
    override fun onDestroy() {
        job.cancel()
        super.onDestroy()
    }
}


launch{ }の書き方1

withContext(Dispatchers.Default) { } による非同期スレッド

launch {
    withContext(Dispatchers.Default) {
        // 非同期スレッド

    }

    // メインスレッド

}


launch{ }の書き方2

async( ){ }.await( ) による非同期スレッド

launch {
    async(Dispatchers.IO) {
        // Dispatchers.IO、Dispatchers.Defaultを使い分ける
        //     https://qiita.com/offwhite/items/94540e7cc3b330507c9c

        // 非同期スレッド

    }.await()

    withContext(Dispatchers.Main) {
        // メインスレッド

}


launch{ }の書き方3

async( ) { } による非同期スレッド

launch {
    val retAsync = async(Dispatchers.IO){
        // Dispatchers.IO、Dispatchers.Defaultを使い分ける
        //     https://qiita.com/offwhite/items/94540e7cc3b330507c9c

        // 非同期スレッド

    }
    withContext(Dispatchers.Main) {
        // メインスレッド
        //     async( )の結果を入れた変数を関数の引数にして利用する
        FUNCTION(retAsync.await())
}


非同期スレッド内でのHTTP通信例

MainActivity.kt

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
import PACKAGE.PROJECT.databinding.ActivityMainBinding
import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import kotlin.coroutines.CoroutineContext

class MainActivity : AppCompatActivity(), CoroutineScope {

    val targetUrl : String = "http://www.google.co.jp"

    // coroutine準備
    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

    // 終了時のcoroutineのキャンセル設定
    override fun onDestroy() {
        job.cancel()
        super.onDestroy()
    }

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

        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.btnStart.setOnClickListener {

            /*
             * launch{ }の中でメインスレッドと非同期スレッドを使い分ける
             * async( ){ }の中だけが非同期スレッド
             */
            launch {
                withContext(Dispatchers.Main) {
                    /*
                     * メインスレッド1
                     * ここのコードは非同期スレッド実行前に実行される
                     */
                    binding.txtVw1.setText("wait")
                    delay(5000)
                }

                /*
                 * 非同期スレッド
                 * await()により、このブロックの実行後にその下のメインスレッド2が実行される
                 * ブロック内最終行がasync( )の戻り値となる
                 */
                val resultSubThread = async(context = Dispatchers.IO) {
                    val TIMEOUT_MILLIS = 0 // 0は無限
                    val sb = StringBuffer("")
                    var httpConn: HttpURLConnection? = null
                    val br: BufferedReader? = null
                    val `is`: InputStream? = null
                    val isr: InputStreamReader? = null

                    val url = URL(targetUrl)
                    httpConn = url.openConnection() as HttpURLConnection
                    httpConn.connectTimeout = TIMEOUT_MILLIS // 接続にかかる時間
                    httpConn.readTimeout = TIMEOUT_MILLIS // データの読み込みにかかる時間
                    httpConn.requestMethod = "GET" // HTTPメソッド
                    httpConn.useCaches = false // キャッシュ利用
                    httpConn.doOutput = false // リクエストのボディの送信を許可(GETのときはfalse,POSTのときはtrueにする)
                    httpConn.doInput = false // レスポンスのボディの受信を許可
                    httpConn.connect()
                    val responseCode = httpConn.responseCode
                    Log.d("MyApp", responseCode.toString())
                    responseCode
                }.await()

                withContext(Dispatchers.Main) {
                    // メインスレッド2
                    binding.txtVw1.setText(resultSubThread.toString())
                }
            }
        }
    }
}