Kotlin+Couroutine[KotlinでAndroid]

Kotlinでアンドロイドアプリを作成しています。

その中でこの記事でまとめた、APIを使いたいと思い実装してました。

僕はアプリ開発の素人ですので、本来の実装方法は分かりませんが

とりあえず、ネットワークに繋いで処理する系は非同期処理でしょ!ということで

AsyncTaskで実装してました。

ですが、文字列が渡せない。

流れとしてはsetOnclicklistnerでボタンを押したときのEditTextの文字列をAsyncTaskに渡してたんですが、上手く挙動しません。

KotlinでAsyncTaskは非推奨ということもあり、諦めて別の方法を利用します。

それがタイトルの通りCouroutine(コルーチン)です。

こちらの記事を参考にさせていただき、実装しました。

APIの使い方は今回はパスします!

retrofit2でそこらへんは実装しているので、感じ取って読んでください。

import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.*
import android.view.inputmethod.InputMethodManager
import android.widget.*
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.list_item.view.*
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.Retrofit
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import kotlinx.coroutines.experimental.Deferred
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async
class MainActivity : Activity() {

    var job: Deferred<Unit>? = null
    var foodText: EditText? = null//入力テキスト
    var input_food:String? = null//foodTextの文字列
    var result_count:Int? = 0//APIの検索結果のリスト長
    var eat_foodLists :MutableList<CharSequence> = mutableListOf()//食事リスト
    var foodLists :MutableList<Items> = mutableListOf()//APIの検索結果を表示するリスト

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

        foodText = findViewById(R.id.edit_text) as EditText
        inputButton.setOnClickListener {View ->
            //まずソフトキーボードを非表示にする
            val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.hideSoftInputFromWindow(View.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS)
            if (foodText!!.text != null) {
                // 取得したテキストを TextView に張り付ける
                input_food = foodText!!.text.toString(
                // async関数の戻り(Deferred型)を受け取る
                job = async {
                    // myInputメソッドの呼び出し
                    MyInput(input_food!!)
                }
            }
        }
        foodListView.setOnItemClickListener { adapterView, view, position, id ->//検索結果リストのタップイベント
            val food = view.findViewById<TextView>(R.id.foodTextView).text
            eat_foodLists.add(food)
            Log.d("TAG",eat_foodLists.toString())
        }

    }

    data class FoodData(val food_name: String, val calory: String)//listviewのカスタムのためのデータクラス
    data class ViewHolder(val foodTextView: TextView, val caloryTextView: TextView)
    var foods :MutableList<FoodData> = mutableListOf()//
    class FoodListAdapter(context: Context, foods: List<FoodData>) : ArrayAdapter<FoodData>(context, 0, foods) {//独自アダプタのクラス
        private val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
            var view = convertView
            var holder: ViewHolder

            if (view == null) {
                view = layoutInflater.inflate(R.layout.list_item, parent, false)
                holder = ViewHolder(
                        view?.foodTextView!!,
                        view.caloryTextView
                )
                view.tag = holder
            } else {
                holder = view.tag as ViewHolder
            }

            val foods = getItem(position) as FoodData
            holder.foodTextView.text = foods.food_name
            holder.caloryTextView.text = foods.calory
            return view
        }
    }
        private fun MyInput(input:String) {
            var retro = Retrofit.Builder()
                    .baseUrl("https://apex.oracle.com/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
            var service = retro.create(API_interface::class.java)
            var nutrition = service.API(input)
            nutrition.enqueue(object : Callback<OracleAPIData> {
                @SuppressLint("SetTextI18n")
                override fun onResponse(call: Call<OracleAPIData?>, response: Response<OracleAPIData?>) {
                    val  api_body = response?.body()
                    if(api_body != null) {
                        result_count = api_body!!.items.count()
                        if(result_count == 0){Toast.makeText(applicationContext, "検索した食事名は見つかりませんでした", Toast.LENGTH_LONG).show()}
                        for (i in 0..(result_count!!-1)){ foodLists.add(api_body!!.items[i]) }
                        for (i in 0..(result_count!!-1)){
                            val tmp = FoodData(api_body!!.items[i].food_name, api_body!!.items[i].calories.toString())
                            foods.add(tmp)
                        }
                        //onProgressと同じ処理(UIの変更が可能)
                        async(UI) {
                            val foodListView = findViewById(R.id.foodListView) as ListView
                            //var adapter = ArrayAdapter<Items>(this@MainActivity, android.R.layout.simple_list_item_1, foodLists)
                            val adapter = FoodListAdapter(this@MainActivity, foods)
                            foodListView.adapter = adapter
                        }
                        Thread.sleep(500)
                    }else{Toast.makeText(applicationContext, "食事名を入力してください", Toast.LENGTH_LONG).show()}
                }
                override fun onFailure(call: Call<OracleAPIData>, t: Throwable) {
                    Log.d("Tag","failed!!")
                }
            })
            // onPostExecuteメソッドと同等の処理
            async() {
                foodLists.clear()
                foods.clear()
            }

        }
}

今回の注目点は

var job: Deferred<Unit>? = null
// async関数の戻り(Deferred型)を受け取る
                job = async {
                    // myInputメソッドの呼び出し
                    MyInput(input_food!!)
                }
private fun MyInput(input:String) {
         //onProgressと同じ処理(UIの変更が可能)
                        async(UI) {
                                ・・・   
                           }
        // onPostExecuteメソッドと同等の処理
                        async() {
               ・・・
            }
}

まずjob変数を作って、非同期処理を呼び出します。

その中のメソッドでは非同期処理が行われます。

非同期処理中にUIを変更したい場合はasync(UI){}

したくないときはasync(){}を使いましょう。

おおまかな流れはこれだけです。

非常に分かりやすくて、可読性も高いですね。

みなさんもぜひ使ってみてください。

Sponsored Link

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です