W tym poście chciałbym opisać krótką aplikację pozwalającą na odczytanie pogody z serwisu OpenWeatherMap.
[Źródło: https://www.pngfly.com/png-7ezqh6/]
Aby pobrać pogodę dla danego miasta należy wysłać odpowiedni string, w którym zostanie wpisana nazwa miasta oraz API użytkownika.
Pobranie i obsłużenie komunikatu od serwera wykonywane jest za pomocą AsyncTask.
Całość wykonywana jest przez cztery elementy:
- onPreExecute;
- doInBackground;
- onProgressUpdate;
- onPostExecute;
Jako pierwsze wywołana jest funkcji onPreExecute. Wywołane na samym początku zanim rozpocznie się obsługa zdarzenia. Wykorzystywana do ustawienia widoczności poszczególnych elementów na ekranie:
- protected void onPreExecute() {
- super.onPreExecute();
- SetMainWindowVisibility_StartSettings();
- }
- private void SetMainWindowVisibility_StartSettings()
- {
- findViewById(R.id.loader).setVisibility(View.VISIBLE);
- findViewById(R.id.mainContainer).setVisibility(View.GONE);
- findViewById(R.id.errorText).setVisibility(View.GONE);
- }
Wywołanie super.onPreExecute nadpisuje domyślne wystąpienie metody na tą wykorzystaną w funkcji.
Wykonanie metody onProgressUpdate zostawiłem bez nadpisywania funkcji. Wykorzystywana jest on podczas wykonywania metody doInBackground w celu np. wyświetlenia paska postępu. W tym przypadku pobranie danych z serwisu jest bardzo szybkie i nie ma potrzeby wykonywania takich funkcji.
Ostatnim krokiem jest wywołanie metody onPostExecute. Tutaj następuje obrobienie otrzymanych danych wraz z wyświetleniem ich na ekranie.
- protected void onPostExecute(String result) {
- try {
- JSONObject jsonObj = new JSONObject(result);
- if(CheckIfLoadingErrorOccurs(jsonObj.getLong("cod"))) {
- //If error when receiving data it loads previous window
- SetMainWindowVisibility_ProperData();
- return;
- }
- JSONObject main = jsonObj.getJSONObject("main");
- JSONObject sys = jsonObj.getJSONObject("sys");
- JSONObject wind = jsonObj.getJSONObject("wind");
- JSONObject coords = jsonObj.getJSONObject("coord");
- JSONObject clouds = jsonObj.getJSONObject("clouds");
- JSONObject weather = jsonObj.getJSONArray("weather").getJSONObject(0);
- addressTxt.setText(jsonObj.getString("name") + ", " + sys.getString("country"));
- updated_atTxt.setText("Date: " + new SimpleDateFormat("dd/MM/yyyy hh:mm a", Locale.ENGLISH).format(new Date(jsonObj.getLong("dt") * 1000)));
- statusTxt.setText(weather.getString("description").toUpperCase());
- tempTxt.setText(main.getString("temp") + "°C");
- temp_minTxt.setText("Min: " + main.getString("temp_min") + "°C");
- temp_maxTxt.setText("Max: " + main.getString("temp_max") + "°C");
- feelsLikeTxt_Val.setText("Temp Fells Like: " + main.getString("feels_like") + "°C");
- sunriseTxt_Val.setText("Sunrise: " + new SimpleDateFormat("hh:mm a", Locale.ENGLISH).format(new Date(sys.getLong("sunrise") * 1000)));
- sunsetTxt_Val.setText("Sunset: " + new SimpleDateFormat("hh:mm a", Locale.ENGLISH).format(new Date(sys.getLong("sunset") * 1000)));
- windTxt_Val.setText("Wind: " + wind.getString("speed"));
- pressureTxt_Val.setText(main.getString("pressure") + " hPa");
- humidityTxt_Val.setText(main.getString("humidity") + "%");
- latTxt_Val.setText("Latitude: " + coords.getString("lat"));
- lonTxt_Val.setText("Longitude: " + coords.getString("lon"));
- cloudinessTxt_Val.setText("Cloudiness: " + clouds.getLong("all") + "%");
- SetMainWindowVisibility_ProperData();
- }
- catch (JSONException e)
- {
- SetMainWindowVisibility_Error();
- }
- }
Obsługa JSON'a musi być obudowana blokiem try{} catch{}. Pobrane dane są zapisywane bezpośrednio do kontrolek.
Na samym początku po pobraniu danych sprawdzony zostaje kod odpowiedzi ze strony. Tak aby nie rozpoczynać obrabiania danych gdy wystąpi błąd wynikający np. z wpisania błędnej nazwy miasta.
- @org.jetbrains.annotations.Contract(value = "null -> false", pure = true)
- private boolean CheckIfLoadingErrorOccurs(Long pageLoadingStatus)
- {
- if(pageLoadingStatus == 404) {
- return true;
- }
- return false;
- }
Po pobraniu danych z serwera w postaci formatu JSON otrzymuje się następujące informacje:
- {"coord":{
- "lon":19.94,
- "lat":50.06},
- "weather":[
- {"id":803,
- "main":"Clouds",
- "description":"broken clouds",
- "icon":"04n"}],
- "base":"stations",
- "main":{
- "temp":1.2,
- "feels_like":-3.91,
- "temp_min":0,
- "temp_max":2.22,
- "pressure":1026,
- "humidity":80},
- "visibility":10000,
- "wind":{
- "speed":4.1,
- "deg":320},
- "clouds":{"all":75},
- "dt":1577484733,
- "sys":{
- "type":1,
- "id":1701,
- "country":"PL",
- "sunrise":1577428713,
- "sunset":1577457804},
- "timezone":3600,
- "id":3094802,
- "name":"Krakow",
- "cod":200}
Dane są obrabiane w funkcji asynchronicznej. Obsługa formatu JSON wykonywana jest przez interfejs JsonObject.
W drugim kroku (natychmiast po zakończeniu onPreExecute) wykonuje pobranie danych w formacie JSON. Aby to osiągnąć należy przesłać nazwę miasta, skrót od nazwy kraju, oraz klucz dostępu wygenerowany po utworzeniu konta na stronie:
- protected String doInBackground(String... args) {
- String receiveDataString = HttpRequest.excuteGet
- ("https://api.openweathermap.org/data/2.5/weather?q=" +
- CITY_NAME + "," + COUNTRY_SHORT +
- "&units=metric&appid=" + API);
- return receiveDataString;
- }
Deklarowane wartości ustawiłem jako domyślne:
- String CITY_NAME = "gdansk";
- String COUNTRY_SHORT = "pl";
- final String API = "API_KEY";
Zdefiniowałem je w taki sposób aby zaraz po uruchomieniu możliwe było odczytanie pogody dla interesującego mnie miejsca. W celu zmiany miasta i/lub kraju wykorzystałem dwie kontrolki EditTxt oraz przycisk. Po jego wciśnięciu następuje zapisanie nowych wartości do zmiennych oraz wywołanie zdarzenia asynchronicznego do pobrania danych z serwera:
- public void loadDataBtnClick(View view)
- {
- CITY_NAME = city_EditTxt.getText().toString();
- COUNTRY_SHORT = country_EditTxt.getText().toString();
- new getWeatherAsyncTask().execute();
- }
Layout całej aplikacji prezentuje się w następujący sposób:
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:padding="5dp"
- android:background="@drawable/image">
- <RelativeLayout
- android:id="@+id/mainContainer"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="visible">
- <LinearLayout
- android:id="@+id/addressContainer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="vertical">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="horizontal">
- <EditText
- android:id="@+id/cityEditTxt_Control"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:hint="city"
- android:textColor="#ffffff"
- android:textColorHint="#808080"
- android:tooltipText="Write city name" />
- <Space
- android:layout_width="50dp"
- android:layout_height="wrap_content" />
- <EditText
- android:id="@+id/countryEditTxt_Control"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:hint="pl"
- android:textColor="#ffffff"
- android:textColorHint="#808080"
- android:tooltipText="Write country shortcut" />
- </LinearLayout>
- <Button
- android:id="@+id/loadDataBtnClick"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@android:color/transparent"
- android:onClick="loadDataBtnClick"
- android:text="Load Data For City"
- android:textColor="#ffffff" />
- <Space
- android:layout_width="wrap_content"
- android:layout_height="2dp" />
- <TextView
- android:id="@+id/address"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="CITY, CUNTRY_SHORT"
- android:textColor="#ffffff"
- android:textSize="24dp" />
- <Space
- android:layout_width="wrap_content"
- android:layout_height="2dp" />
- <TextView
- android:id="@+id/updated_at"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="20 April 2012, 20:08 PM"
- android:textColor="#ffffff"
- android:textSize="14dp" />
- <Space
- android:layout_width="wrap_content"
- android:layout_height="2dp" />
- </LinearLayout>
- <LinearLayout
- android:id="@+id/overviewContainer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="@+id/addressContainer"
- android:layout_centerVertical="true"
- android:orientation="vertical">
- <Space
- android:layout_width="wrap_content"
- android:layout_height="60dp" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:layout_alignParentBottom="@+id/addressContainer"
- android:orientation="horizontal">
- <TextView
- android:id="@+id/temp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:fontFamily="sans-serif-thin"
- android:text="29°C"
- android:textColor="#ffffff"
- android:textSize="90dp"
- android:textStyle="bold" />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="@+id/overviewContainer"
- android:gravity="center"
- android:orientation="horizontal">
- <TextView
- android:id="@+id/temp_min"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Min Temp: 05:05 AM"
- android:textColor="#ffffff" />
- <Space
- android:layout_width="50dp"
- android:layout_height="wrap_content" />
- <TextView
- android:id="@+id/temp_max"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Max Temp: 05:05 PM"
- android:textColor="#ffffff" />
- </LinearLayout>
- <Space
- android:layout_width="wrap_content"
- android:layout_height="20dp" />
- <TextView
- android:id="@+id/status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="Clear Sky"
- android:textColor="#ffffff"
- android:textSize="18dp" />
- <TextView
- android:id="@+id/lonTxtView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="Longitude: "
- android:textColor="#ffffff"
- android:textSize="22dp" />
- <TextView
- android:id="@+id/latTxtView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="Latitude: "
- android:textColor="#ffffff"
- android:textSize="22dp" />
- <TextView
- android:id="@+id/sunriseTxtView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="Sunrise: "
- android:textColor="#ffffff"
- android:textSize="22dp" />
- <TextView
- android:id="@+id/sunsetTxtView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="Sunset: "
- android:textColor="#ffffff"
- android:textSize="22dp" />
- <TextView
- android:id="@+id/feelsLikeTxtView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="Fells Like: "
- android:textColor="#ffffff"
- android:textSize="22dp" />
- <TextView
- android:id="@+id/windTxtView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="Wind: "
- android:textColor="#ffffff"
- android:textSize="22dp" />
- <TextView
- android:id="@+id/cloudinessTxtView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="Cloudiness: "
- android:textColor="#ffffff"
- android:textSize="22dp" />
- <TextView
- android:id="@+id/pressureTxtView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="Pressure: "
- android:textColor="#ffffff"
- android:textSize="22dp" />
- <TextView
- android:id="@+id/humidityTxtView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="Humidity: "
- android:textColor="#ffffff"
- android:textSize="22dp" />
- </LinearLayout>
- </RelativeLayout>
- <ProgressBar android:id="@+id/loader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:visibility="gone"/>
- <TextView android:id="@+id/errorText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:visibility="gone"
- android:text="Loading Error"/>
- </RelativeLayout>
Poniżej screen aplikacji: