RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

Android Studioで天気情報を表示するアプリを作ってみた

はじめに

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のバージョンに伴う注意点

それでは実装、といきたいところですが、先に私がハマった注意点について述べておきます。

実装概要

コードなどは次の節に記述します。この節では実装すべきことについて大まかに述べます。

※ 今回は実機デバックを対象とします(エミュレータを使用する場合は、エミュレータwifi設定などが必要です)。

ネット接続を許可する

  • AndroidManifest.xmlに下記を追記
<!-- ネット接続を許可する -->
<uses-permission android:name="android.permission.INTERNET" />

ボタンを押すと特定の処理を実行する

  • 前回の記事を参照ください

APIサーバに接続する

  • 注意で述べたようにメインスレッドでは接続できませんので、実行用のクラスを作成してあげます

    • 参考:AsyncTaskを使った非同期処理のきほん

      1. AsyncTaskを継承するクラス(今回はAsyncHttpRequestというクラス名にしました)を作成する
      2. 作成したクラス内にdoInBackground(非同期で行いたい処理を記述する)メソッドを実装する
      3. メインスレッド(MainActivity.java)で1のクラスをインスタンス化し、executeメソッドを呼び出す
        doInBackgroundが呼び出される

受け取ったJSONを加工する

  • 受け取った文字列をJSONObjectでパースし、天気情報を取り出します

天気情報を表示する

  • AsyncTaskを継承するクラスでonPostExecute(非同期処理が終わった後に実行される)メソッドを実装し、メインスレッドのラベルを変更する

実装例

下記にMainActivity.javaAsyncHttpRequest.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アプリ開発の初心者や非同期処理の実装で困っている方の手助けになれば幸いです。


◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.