質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.22%

Kotlinでのhttp通信のテストでのエラー

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,536

luke04

score 7

kotlinでandroid開発を勉強している初心者です。
書籍を参考にhttp通信を用いてlivedoorから天気情報を取得して表示させたいのですが、実行してみると繰り返し停止しますというエラーが出て強制終了してしまいます。以下がエラーが出るMainActivity.ktのコードです。実行は実機で、Galaxy S9です。
エラー内容はListItemClickLister内に二つ、WeatherInfoReceiver内に一つで、コメントとして書きました。ご回答よろしくお願いします。

package com.example.asynksample

import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ListView
import android.widget.SimpleAdapter
import android.widget.TextView
import org.json.JSONObject
import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL

class MainActivity : AppCompatActivity() {

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

        //画面部品ListViewを取得
        val lvCityList = findViewById<ListView>(R.id.lvCityList)
        //SimpleAdapterで使用するMutableListオブジェクトを用意
        val cityList: MutableList<MutableMap<String, String>> = mutableListOf()
        //都市データを格納するMutableMapオブジェクトの用意とcityListへのデータ登録
        val city = mutableMapOf("name" to "東京","id" to "130010")
        cityList.add(city)
        //SimpleAdapterで使用するfrom-to用変数の用意
        val from = arrayOf("name")
        val to = intArrayOf(android.R.id.text1)
        //SimpleAdapterを生成
        val adapter = SimpleAdapter(applicationContext, cityList, android.R.layout.simple_expandable_list_item_1, from, to)
        //ListViewにSimpleAdapterを設定
        lvCityList.adapter = adapter
        //リストタップのリスナクラス登録
        lvCityList.onItemClickListener = ListItemClickListener()
    }

    //リストがタップされたときの処理が記述されたメンバクラス
    private inner class ListItemClickListener : AdapterView.OnItemClickListener {
        override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) {
            //ListViewでタップされた行の都市名と都市IDを取得
            val item = parent.getItemAtPosition(position) as Map<String,String> //(警告)Unchecked cast: Any! to Map<String, String>
            val cityName = item["name"]
            val cityId = item["id"]
            //取得した都市名をtvCityNameに設定
            val tvCityName = findViewById<TextView>(R.id.tvCityName)
            tvCityName.setText(cityName + "の天気:") //エラー: Use of setter method insted of property access syntax
            //WeatherInfoReceiverインスタンスを生成
            val receiver = WeatherInfoReceiver()
            //WeatherInfoReceiverを実行
            receiver.execute(cityId)
        }
    }

    private inner class WeatherInfoReceiver : AsyncTask<String, String, String>() { //(警告)This AsyncTask class should be static or leaks might occur
        override fun doInBackground(vararg params: String): String {
            //可変長引数の1個目(インデックス0)を取得。これが都市ID
            val id = params[0]
            //都市IDを使って接続URL文字列を作成
            val urlStr = "http://weather.livedoor.com/forecast/webservise/json/v1?city=${id}"

            //ここに上記URLに接続してJSON文字列を取得する処理を記述
            //URLオブジェクトを生成
            val url = URL(urlStr)
            //URLオブジェクトからHttpURLConnectionオブジェクトを取得
            val con = url.openConnection() as HttpURLConnection
            //http接続メソッドを設定
            con.requestMethod = "GET"
            //接続
            con.connect()
            //HttpURLConnectionオブジェクトからレスポンスデータを取得。天気情報が格納されている
            val stream = con.inputStream
            //レスポンスデータであるInputStreamオブジェクトを文字列(JSON文字列)に変換
            val result = is2String(stream)
            //HttpURLConnectionオブジェクトを解放
            con.disconnect()
            //InputStreamオブジェクトを開放
            stream.close()

            //JSON文字列を返す
            return result
        }

        override fun onPostExecute(result: String) {

            //ここに天気情報JSON文字列を解析する処理を記述
            //JSON文字列からJSONObjectオブジェクトを生成。これをルートJSONオブジェクトとする
            val rootJSON = JSONObject(result)
            //ルートJSON直下の「description」JSONオブジェクトを取得
            val descriptionJSON = rootJSON.getJSONObject("description")
            //「description」プロパティ直下の「text」文字列(天気概況文)を取得
            val desc = descriptionJSON.getString("text")
            //ルートJSON直下の「forecasts」JSON配列を取得
            val forecasts = rootJSON.getJSONArray("forecasts")
            //「forecasts」JSON配列の一つ目(インデックス0)のJSONオブジェクトを取得
            val forecastNow = forecasts.getJSONObject(0)
            //「forecasts」一つ目のJSONオブジェクトから「telop」文字列(天気)を取得
            val telop = forecastNow.getString("telop")

            //天気情報用文字列をTextViewにセット
            val tvWeatherTelop = findViewById<TextView>(R.id.tvWeatherTelop)
            val tvWeatherDesc = findViewById<TextView>(R.id.tvWeatherDesc)
            tvWeatherTelop.text = telop
            tvWeatherDesc.text = desc
        }

        private fun is2String(stream: InputStream): String {
            val sb = StringBuilder()
            val reader = BufferedReader(InputStreamReader(stream, "UTF-8"))
            var line = reader.readLine()
            while(line !=null) {
                sb.append(line)
                line = reader.readLine()
            }
            reader.close()
            return sb.toString()
        }
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • yukke_

    2019/10/08 21:32

    androidのバージョンとエラーを書いてください

    キャンセル

  • luke04

    2019/10/09 15:44

    Android9です。
    エラーと言うより、ビルドはできるのですが実行してアプリ内のリストをタップするとアプリが強制終了してしまいます。Android studioが出す警告はコード内にコメントで書きました。

    キャンセル

回答 1

checkベストアンサー

0

アプリが落ちたときのLogCatを参照すると、多分

     Caused by: java.io.IOException: Cleartext HTTP traffic to weather.livedoor.com not permitted
        at com.android.okhttp.HttpHandler$CleartextURLFilter.checkURLPermitted(HttpHandler.java:115)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:458)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:127)
        at jp.hrs.keicha.myapplication.MainActivity$WeatherInfoReceiver.doInBackground(MainActivity.kt:73)
        at jp.hrs.keicha.myapplication.MainActivity$WeatherInfoReceiver.doInBackground(MainActivity.kt:58)
        at android.os.AsyncTask$2.call(AsyncTask.java:333)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:764) 

みたいなエラーログが出ていることと思います。ここで、Cleartext HTTP traffic toをキーワードにして検索すると、一例として次のような情報が見つかります。

Android 9(Pie)でHTTP通信を有効にする | Qiita

これを参考にして修正することで解決できないでしょうか。


(コメントに対する追記)

他のところの警告は次のようにすれば回避できるのでは。

            @Suppress("UNCHECKED_CAST")
            val item = parent.getItemAtPosition(position) as Map<String, String>


            //tvCityName.setText(cityName + "の天気:") //エラー: Use of setter method insted of property access syntax
            tvCityName.text = "${cityName}の天気:" // ${}で変数を文字列中に展開できる

    (中略)

    @SuppressLint("StaticFieldLeak")
    private inner class WeatherInfoReceiver : AsyncTask<String, String, String>() { 

setText()やgetText()のようなsetter/getterが用意されたJavaメソッドは、Kotlinでは上記のようにイコールで代入する記述ができるようになっており、そちらが推奨されています。ご提示のコードでも、

        lvCityList.adapter = adapter

ではその手法を使われていますよね?むしろ、こちらではsetAdapter()を使わずにKotlin的な記述をされているのに、setText()の方はなぜそのままにしてしまったのかなと思います。

他の2つについては、警告を発しないようにアノテーションを付与しておけばいいかと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/09 19:01

    × webservise
    ○ webservice
    のような気がします。スペルミスではないでしょうか。

    キャンセル

  • 2019/10/10 08:06

    ありがとうございます、スペルミスでした。ただ正常に実行はできますがコードに警告は残ります。これらはほっといてもいいものですか?

    キャンセル

  • 2019/10/12 17:34

    解決しました、ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る