kotlinでandroid開発を勉強している初心者です。
書籍を参考にhttp通信を用いてlivedoorから天気情報を取得して表示させたいのですが、実行してみると繰り返し停止しますというエラーが出て強制終了してしまいます。以下がエラーが出るMainActivity.ktのコードです。実行は実機で、Galaxy S9です。
エラー内容はListItemClickLister内に二つ、WeatherInfoReceiver内に一つで、コメントとして書きました。ご回答よろしくお願いします。
Kotlin
1package com.example.asynksample 3import android.os.AsyncTask 4import androidx.appcompat.app.AppCompatActivity 5import android. os.Bundle 6import android.view.View 7import android.widget.AdapterView 8import android.widget.ListView 9import android.widget.SimpleAdapter 10import android.widget.TextView 11import org.json.JSONObject 12import java.io.BufferedReader 13import java.io.InputStream 14import java.io.InputStreamReader 15import java.net.HttpURLConnection 16import java.net.URL 18class MainActivity : AppCompatActivity() { 20 override fun onCreate(savedInstanceState: Bundle?) { 21 super.onCreate(savedInstanceState) 22 setContentView(R.layout.activity_main) 24 //画面部品ListViewを取得 25 val lvCityList = findViewById<ListView>(R.id.lvCityList) 26 //SimpleAdapterで使用するMutableListオブジェクトを用意 27 val cityList: MutableList<MutableMap<String, String>> = mutableListOf() 28 //都市データを格納するMutableMapオブジェクトの用意とcityListへのデータ登録 29 val city = mutableMapOf("name" to "東京","id" to "130010") 30 cityList.add(city) 31 //SimpleAdapterで使用するfrom-to用変数の用意 32 val from = arrayOf("name") 33 val to = intArrayOf(android.R.id.text1) 34 //SimpleAdapterを生成 35 val adapter = SimpleAdapter(applicationContext, cityList, android.R.layout.simple_expandable_list_item_1, from, to) 36 //ListViewにSimpleAdapterを設定 37 lvCityList.adapter = adapter 38 //リストタップのリスナクラス登録 39 lvCityList.onItemClickListener = ListItemClickListener() 40 } 42 //リストがタップされたときの処理が記述されたメンバクラス 43 private inner class ListItemClickListener : AdapterView.OnItemClickListener { 44 override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) { 45 //ListViewでタップされた行の都市名と都市IDを取得 46 val item = parent.getItemAtPosition(position) as Map<String,String> //(警告)Unchecked cast: Any! to Map<String, String> 47 val cityName = item["name"] 48 val cityId = item["id"] 49 //取得した都市名をtvCityNameに設定 50 val tvCityName = findViewById<TextView>(R.id.tvCityName) 51 tvCityName.setText(cityName + "の天気:") //エラー: Use of setter method insted of property access syntax 52 //WeatherInfoReceiverインスタンスを生成 53 val receiver = WeatherInfoReceiver() 54 //WeatherInfoReceiverを実行 55 receiver.execute(cityId) 56 } 57 } 59 private inner class WeatherInfoReceiver : AsyncTask<String, String, String>() { //(警告)This AsyncTask class should be static or leaks might occur 60 override fun doInBackground(vararg params: String): String { 61 //可変長引数の1個目(インデックス0)を取得。これが都市ID 62 val id = params[0] 63 //都市IDを使って接続URL文字列を作成 64 val urlStr = "http://weather.livedoor.com/forecast/webservise/json/v1?city=${id}" 66 //ここに上記URLに接続してJSON文字列を取得する処理を記述 67 //URLオブジェクトを生成 68 val url = URL(urlStr) 69 //URLオブジェクトからHttpURLConnectionオブジェクトを取得 70 val con = url.openConnection() as HttpURLConnection 71 //http接続メソッドを設定 72 con.requestMethod = "GET" 73 //接続 74 con.connect() 75 //HttpURLConnectionオブジェクトからレスポンスデータを取得。天気情報が格納されている 76 val stream = con.inputStream 77 //レスポンスデータであるInputStreamオブジェクトを文字列(JSON文字列)に変換 78 val result = is2String(stream) 79 //HttpURLConnectionオブジェクトを解放 80 con.disconnect() 81 //InputStreamオブジェクトを開放 82 stream.close() 84 //JSON文字列を返す 85 return result 86 } 88 override fun onPostExecute(result: String) { 90 //ここに天気情報JSON文字列を解析する処理を記述 91 //JSON文字列からJSONObjectオブジェクトを生成。これをルートJSONオブジェクトとする 92 val rootJSON = JSONObject(result) 93 //ルートJSON直下の「description」JSONオブジェクトを取得 94 val descriptionJSON = rootJSON.getJSONObject("description") 95 //「description」プロパティ直下の「text」文字列(天気概況文)を取得 96 val desc = descriptionJSON.getString("text") 97 //ルートJSON直下の「forecasts」JSON配列を取得 98 val forecasts = rootJSON.getJSONArray("forecasts") 99 //「forecasts」JSON配列の一つ目(インデックス0)のJSONオブジェクトを取得 100 val forecastNow = forecasts.getJSONObject(0) 101 //「forecasts」一つ目のJSONオブジェクトから「telop」文字列(天気)を取得 102 val telop = forecastNow.getString("telop") 104 //天気情報用文字列をTextViewにセット 105 val tvWeatherTelop = findViewById<TextView>(R.id.tvWeatherTelop) 106 val tvWeatherDesc = findViewById<TextView>(R.id.tvWeatherDesc) 107 tvWeatherTelop.text = telop 108 tvWeatherDesc.text = desc 109 } 111 private fun is2String(stream: InputStream): String { 112 val sb = StringBuilder() 113 val reader = BufferedReader(InputStreamReader(stream, "UTF-8")) 114 var line = reader.readLine() 115 while(line !=null) { 116 sb.append(line) 117 line = reader.readLine() 118 } 119 reader.close() 120 return sb.toString() 121 } 122 } 123}
回答 1 件
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
これを参考にして修正することで解決できないでしょうか。
(コメントに対する追記)
他のところの警告は次のようにすれば回避できるのでは。
kotlin
1 @Suppress("UNCHECKED_CAST") 2 val item = parent.getItemAtPosition(position) as Map<String, String> 5 //tvCityName.setText(cityName + "の天気:") //エラー: Use of setter method insted of property access syntax 6 tvCityName.text = "${cityName}の天気:" // ${}で変数を文字列中に展開できる 8 (中略) 10 @SuppressLint("StaticFieldLeak") 11 private inner class WeatherInfoReceiver : AsyncTask<String, String, String>() {setText()やgetText()のようなsetter/getterが用意されたJavaメソッドは、Kotlinでは上記のようにイコールで代入する記述ができるようになっており、そちらが推奨されています。ご提示のコードでも、
kotlin
1 lvCityList.adapter = adapterではその手法を使われていますよね?むしろ、こちらではsetAdapter()を使わずにKotlin的な記述をされているのに、setText()の方はなぜそのままにしてしまったのかなと思います。
他の2つについては、警告を発しないようにアノテーションを付与しておけばいいかと思います。
投稿2019/10/08 15:24編集2019/10/10 08:42
総合スコア6762
回答へのコメント
```LogCat
2019-10-09 17:28:49.302 27692-28173/com.example.asynksample E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1
Process: com.example.asynksample, PID: 27692
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(
AsyncTask.java:354
)
at java.util.concurrent.FutureTask.finishCompletion(
FutureTask.java:383
)
at java.util.concurrent.FutureTask.setException(
FutureTask.java:252
)
at
java.util.concurrent.FutureTask.run
(
FutureTask.java:271
)
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
)
Caused by: java.io.FileNotFoundException:
http://weather.livedoor.com/forecast/webservise/json/v1?city=130010
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(
HttpURLConnectionImpl.java:251
)
at com.example.asynksample.MainActivity$WeatherInfoReceiver.doInBackground(MainActivity.kt:76)
at com.example.asynksample.MainActivity$WeatherInfoReceiver.doInBackground(MainActivity.kt:59)
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
)
logcatにはこのように出ていました。
実際、エラーとされたURLにブラウザでアクセスするとページが見つからないと表示されますね。アクセスすべきURLに間違いはないのでしょうか。
× webservise
○ webservice
のような気がします。スペルミスではないでしょうか。
ありがとうございます、スペルミスでした。ただ正常に実行はできますがコードに警告は残ります。これらはほっといてもいいものですか?
解決しました、ありがとうございました。
あなたの回答
tips