はじめに
kuwa_38です。以前Android Studioを使ってみたので、その続きとして天気情報を表示するアプリを実装してみました。簡単に実装できるかと思いきや、AndroidではAPI接続に非同期処理(AsyncTask
)を用いる必要があるらしく苦戦しました。この記事では今回苦戦した非同期処理も含め、天気情報を表示するアプリについて、実装に必要なこと、実装したコードを記載します。
前回実装した処理
今回は下記の実装に付け加える形で実装したため記載しておきます。
Android Studioを使ってHello Worldをやってみた
- 初期画面の表示
- ボタンを押すとテキストが変わる
今回実装した処理
- ボタンを押すとその日の大阪の天気が表示される
利用したAPI
Weather Hacksを利用させて頂きました。GETでエンドポイントにアクセスすることで、パラメータcity
で設定した都市の天気をJSONで返してくれます(因みに270000
は大阪です)。
補足:jqを活用してAPIレスポンス等から欲しい情報だけを抽出する【初級編】
$ curl -s http://weather.livedoor.com/forecast/webservice/json/v1\?city\=270000 | jq -r # 結果 { "pinpointLocations": [ { "link": "http://weather.livedoor.com/area/forecast/2710000", "name": "大阪市" }, # ...(中略) "link": "http://weather.livedoor.com/area/forecast/270000", "forecasts": [ { "dateLabel": "今日", "telop": "晴れ", "date": "2018-05-27", "temperature": { "min": null, "max": { "celsius": "29", "fahrenheit": "84.2" } },
Androidのバージョンに伴う注意点
それでは実装、といきたいところですが、先に私がハマった注意点について述べておきます。
- メインスレッドではネットへ接続する処理ができない
- 行おうとすると
android.os.NetworkOnMainThreadException
が発生します - 参考:【Android】メインスレッド(要は画面処理)でhttpリクエスト投げようとしたら怒られた。 〜NetworkOnMainThreadException〜
- 行おうとすると
- HttpClientが使えない
実装概要
コードなどは次の節に記述します。この節では実装すべきことについて大まかに述べます。
※ 今回は実機デバックを対象とします(エミュレータを使用する場合は、エミュレータのwifi設定などが必要です)。
ネット接続を許可する
AndroidManifest.xml
に下記を追記
<!-- ネット接続を許可する --> <uses-permission android:name="android.permission.INTERNET" />
ボタンを押すと特定の処理を実行する
- 前回の記事を参照ください
APIサーバに接続する
注意で述べたようにメインスレッドでは接続できませんので、実行用のクラスを作成してあげます
-
AsyncTask
を継承するクラス(今回はAsyncHttpRequest
というクラス名にしました)を作成する- 作成したクラス内に
doInBackground
(非同期で行いたい処理を記述する)メソッドを実装する - メインスレッド(
MainActivity.java
)で1のクラスをインスタンス化し、execute
メソッドを呼び出す
→doInBackground
が呼び出される
-
受け取ったJSONを加工する
- 受け取った文字列をJSONObjectでパースし、天気情報を取り出します
天気情報を表示する
AsyncTask
を継承するクラスでonPostExecute
(非同期処理が終わった後に実行される)メソッドを実装し、メインスレッドのラベルを変更する
実装例
下記にMainActivity.java
、AsyncHttpRequest.java
(AsyncTask
を継承するクラス)を載せますので、実装してみたい方は参考にして下さい。
※ AndroidManifest.xml
は1行追記したのみですので載せていません
MainActivity.java
import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import java.net.MalformedURLException; import java.net.URL; /** * トップ画面を制御するActivityクラス. */ public class MainActivity extends AppCompatActivity { /** * 画面を表示する. * note:デフォルトで実装されている * @param savedInstanceState savedInstanceState */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } /** * テキストラベルを変更する. * @param view view */ public void changeTextView(View view) { // 非同期処理(AsyncHttpRequest#doInBackground())を呼び出す try { new AsyncHttpRequest(this).execute(new URL("http://weather.livedoor.com/forecast/webservice/json/v1?city=270000")); } catch (MalformedURLException e) { e.printStackTrace(); } } }
AsyncHttpRequest
import android.app.Activity; import android.os.AsyncTask; import android.widget.TextView; 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.URL; /** * 非同期処理を行うクラス. */ public final class AsyncHttpRequest extends AsyncTask<URL, Void, String> { private int TODAY_FORCAST_INDEX = 0; private Activity mainActivity; public AsyncHttpRequest(Activity activity) { // 呼び出し元のアクティビティ this.mainActivity = activity; } /** * 非同期処理で天気情報を取得する. * @param urls 接続先のURL * @return 取得した天気情報 */ @Override protected String doInBackground(URL... urls) { final URL url = urls[0]; HttpURLConnection con = null; try { con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); // リダイレクトを自動で許可しない設定 con.setInstanceFollowRedirects(false); con.connect(); final int statusCode = con.getResponseCode(); if (statusCode != HttpURLConnection.HTTP_OK) { System.err.println("正常に接続できていません。statusCode:" + statusCode); return null; } // レスポンス(JSON文字列)を読み込む準備 final InputStream in = con.getInputStream(); String encoding = con.getContentEncoding(); if(null == encoding){ encoding = "UTF-8"; } final InputStreamReader inReader = new InputStreamReader(in, encoding); final BufferedReader bufReader = new BufferedReader(inReader); StringBuilder response = new StringBuilder(); String line = null; // 1行ずつ読み込む while((line = bufReader.readLine()) != null) { response.append(line); } bufReader.close(); inReader.close(); in.close(); // 受け取ったJSON文字列をパース JSONObject jsonObject = new JSONObject(response.toString()); JSONObject todayForcasts = jsonObject.getJSONArray("forecasts").getJSONObject(TODAY_FORCAST_INDEX); return todayForcasts.getString("dateLabel") + "の天気は " + todayForcasts.getString("telop"); } catch (IOException e) { e.printStackTrace(); return null; } catch (JSONException e) { e.printStackTrace(); return null; } finally { if (con != null) { con.disconnect(); } } } /** * 非同期処理が終わった後の処理. * @param result 非同期処理の結果得られる文字列 */ @Override protected void onPostExecute(String result) { TextView tv = mainActivity.findViewById(R.id.messageTextView); tv.setText(result); } }
おわりに
本記事ではAndroid Studioを使い、天気情報を表示するAndroidアプリの実装方法を紹介しました。まだまだ実用には遠いクオリティですが、非同期処理やjqコマンドなど知らなかったことを学ぶいい機会になりました。自分の学習目的で始めた部分が大きいですが、Androidアプリ開発の初心者や非同期処理の実装で困っている方の手助けになれば幸いです。
エンジニア中途採用サイト
ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
ご興味ありましたら是非ご確認をお願いします。
https://career-recruit.rakus.co.jp/career_engineer/カジュアル面談お申込みフォーム
どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
以下フォームよりお申込みください。
rakus.hubspotpagebuilder.comラクスDevelopers登録フォーム
https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/イベント情報
会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください!
◆TECH PLAY
techplay.jp
◆connpass
rakus.connpass.com