AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AsyncSample"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
</application>
</manifest>
strings
<resources>
<string name="app_name">お天気情報</string>
<string name="tv_winfo_title">お天気詳細</string>
</resources>
activity_main
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/glLvCityList"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<ListView
android:id="@+id/lvCityList"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/glLvCityList"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvWinfoTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="@string/tv_winfo_title"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/glLvCityList" />
<TextView
android:id="@+id/tvWeatherTelop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvWinfoTitle" />
<TextView
android:id="@+id/tvWeatherDesc"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvWeatherTelop" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity
package com.websarva.wings.android.asyncsample;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MainActivity extends AppCompatActivity {
private static final String DEBUG_TAG = "AsyncSample";
private static final String WEATHERINFO_URL = "https://api.openweathermap.org/data/2.5/weather?lang=ja";
private static final String APP_ID = "xxxxxxxxxxx";
private List<Map<String, String>> _list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_list = createList();
ListView lvCityList = findViewById(R.id.lvCityList);
String[] from = {"name"};
int[] to = {android.R.id.text1};
SimpleAdapter adapter = new SimpleAdapter(getApplicationContext(), _list, android.R.layout.simple_list_item_1, from, to);
lvCityList.setAdapter(adapter);
lvCityList.setOnItemClickListener(new ListItemClickListener());
}
private List<Map<String, String>> createList() {
List<Map<String, String>> list = new ArrayList<>();
Map<String, String> map = new HashMap<>();
map.put("name", "大阪");
map.put("q", "Osaka");
list.add(map);
map = new HashMap<>();
map.put("name", "神戸");
map.put("q", "Kobe");
list.add(map);
map = new HashMap<>();
map.put("name", "京都");
map.put("q", "Kyoto");
list.add(map);
map = new HashMap<>();
map.put("name", "大津");
map.put("q", "Otsu");
list.add(map);
map = new HashMap<>();
map.put("name", "奈良");
map.put("q", "Nara");
list.add(map);
map = new HashMap<>();
map.put("name", "和歌山");
map.put("q", "Wakayama");
list.add(map);
map = new HashMap<>();
map.put("name", "姫路");
map.put("q", "Himeji");
list.add(map);
return list;
}
@UiThread
private void receiveWeatherInfo(final String urlFull) {
WeatherInfoBackgroundReceiver backgroundReceiver = new WeatherInfoBackgroundReceiver(urlFull);
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> future = executorService.submit(backgroundReceiver);
String result = "";
try {
result = future.get();
}
catch(ExecutionException ex) {
Log.w(DEBUG_TAG, "非同期処理結果の取得で例外発生: ", ex);
}
catch(InterruptedException ex) {
Log.w(DEBUG_TAG, "非同期処理結果の取得で例外発生: ", ex);
}
showWeatherInfo(result);
}
@UiThread
private void showWeatherInfo(String result) {
// 都市名。
String cityName = "";
// 天気。
String weather = "";
// 緯度
String latitude = "";
// 経度。
String longitude = "";
try {
// ルートJSONオブジェクトを生成。
JSONObject rootJSON = new JSONObject(result);
// 都市名文字列を取得。
cityName = rootJSON.getString("name");
// 緯度経度情報JSONオブジェクトを取得。
JSONObject coordJSON = rootJSON.getJSONObject("coord");
// 緯度情報文字列を取得。
latitude = coordJSON.getString("lat");
// 経度情報文字列を取得。
longitude = coordJSON.getString("lon");
// 天気情報JSON配列オブジェクトを取得。
JSONArray weatherJSONArray = rootJSON.getJSONArray("weather");
// 現在の天気情報JSONオブジェクトを取得。
JSONObject weatherJSON = weatherJSONArray.getJSONObject(0);
// 現在の天気情報文字列を取得。
weather = weatherJSON.getString("description");
}
catch(JSONException ex) {
Log.e(DEBUG_TAG, "JSON解析失敗", ex);
}
// 画面に表示する「〇〇の天気」文字列を生成。
String telop = cityName + "の天気";
// 天気の詳細情報を表示する文字列を生成。
String desc = "現在は" + weather + "です。\n緯度は" + latitude + "度で経度は" + longitude + "度です。";
// 天気情報を表示するTextViewを取得。
TextView tvWeatherTelop = findViewById(R.id.tvWeatherTelop);
TextView tvWeatherDesc = findViewById(R.id.tvWeatherDesc);
// 天気情報を表示。
tvWeatherTelop.setText(telop);
tvWeatherDesc.setText(desc);
}
private class WeatherInfoBackgroundReceiver implements Callable<String> {
/**
* お天気情報を取得するURL。
*/
private final String _urlFull;
/**
* コンストラクタ。
* 非同期でお天気情報Web APIにアクセスするのに必要な情報を取得する。
*
* @param urlFull お天気情報を取得するURL。
*/
public WeatherInfoBackgroundReceiver(String urlFull) {
_urlFull = urlFull;
}
@WorkerThread
@Override
public String call() {
// 天気情報サービスから取得したJSON文字列。天気情報が格納されている。
String result = "";
// HTTP接続を行うHttpURLConnectionオブジェクトを宣言。finallyで解放するためにtry外で宣言。
HttpURLConnection con = null;
// HTTP接続のレスポンスデータとして取得するInputStreamオブジェクトを宣言。同じくtry外で宣言。
InputStream is = null;
try {
// URLオブジェクトを生成。
URL url = new URL(_urlFull);
// URLオブジェクトからHttpURLConnectionオブジェクトを取得。
con = (HttpURLConnection) url.openConnection();
// 接続に使ってもよい時間を設定。
con.setConnectTimeout(1000);
// データ取得に使ってもよい時間。
con.setReadTimeout(1000);
// HTTP接続メソッドをGETに設定。
con.setRequestMethod("GET");
// 接続。
con.connect();
// HttpURLConnectionオブジェクトからレスポンスデータを取得。
is = con.getInputStream();
// レスポンスデータであるInputStreamオブジェクトを文字列に変換。
result = is2String(is);
}
catch(MalformedURLException ex) {
Log.e(DEBUG_TAG, "URL変換失敗", ex);
}
// タイムアウトの場合の例外処理。
catch(SocketTimeoutException ex) {
Log.w(DEBUG_TAG, "通信タイムアウト", ex);
}
catch(IOException ex) {
Log.e(DEBUG_TAG, "通信失敗", ex);
}
finally {
// HttpURLConnectionオブジェクトがnullでないなら解放。
if(con != null) {
con.disconnect();
}
// InputStreamオブジェクトがnullでないなら解放。
if(is != null) {
try {
is.close();
}
catch(IOException ex) {
Log.e(DEBUG_TAG, "InputStream解放失敗", ex);
}
}
}
return result;
}
private String is2String(InputStream is) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
StringBuffer sb = new StringBuffer();
char[] b = new char[1024];
int line;
while(0 <= (line = reader.read(b))) {
sb.append(b, 0, line);
}
return sb.toString();
}
}
private class ListItemClickListener implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Map<String, String> item = _list.get(position);
String q = item.get("q");
String urlFull = WEATHERINFO_URL + "&q=" + q + "&appid=" + APP_ID;
receiveWeatherInfo(urlFull);
}
}
}
Listener
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
android:orientation="vertical">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tv_name" />
<EditText
android:id="@+id/etInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:inputType="textPersonName"/>
<Button
android:id="@+id/btClick"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/bt_click" />
<Button
android:id="@+id/btClear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/bt_clear" />
<TextView
android:id="@+id/tvOutput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text=""
android:textSize="25sp"/>
</LinearLayout>
package com.websarva.wings.android.hellosample;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btClick=findViewById(R.id.btClick);
HelloListener listener=new HelloListener();
btClick.setOnClickListener(listener);
Button btClear=findViewById(R.id.btClear);
btClear.setOnClickListener(listener);
}
private class HelloListener implements View.OnClickListener {
@Override
public void onClick(View view){
EditText input=findViewById(R.id.etInput);
TextView output=findViewById(R.id.tvOutput);
int id=view.getId();
if (id==R.id.btClick) {
String inputStr = input.getText().toString();
output.setText(inputStr + "さん、こんにちは!!");
} else if (id==R.id.btClear) {
input.setText("");
output.setText("");
}
}
}
}