Kotlinでのhttp通信のテストでのエラー
解決済
回答 1
投稿
- 評価
- クリップ 0
- VIEW 1,536
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ページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
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つについては、警告を発しないようにアノテーションを付与しておけばいいかと思います。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.22%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
質問への追記・修正、ベストアンサー選択の依頼
yukke_
2019/10/08 21:32
androidのバージョンとエラーを書いてください
luke04
2019/10/09 15:44
Android9です。
エラーと言うより、ビルドはできるのですが実行してアプリ内のリストをタップするとアプリが強制終了してしまいます。Android studioが出す警告はコード内にコメントで書きました。