Life Cycles

Eine Activity oder ein Fragment reagiert auf Änderungen im Life- Cycle einer Android App , indem die entsprechenden Call- Back Methoden aufgerufen werden (onCreate(), onStart(),onResume() etc…).

Dabei kann es aber unter Anderem passieren das, während der Code der in onStart() ausgeführt wird – nehmen wir an es findet der Verbindungsaufbau zu einem Bluetooth Gerät statt – onStop() vom System aufgerufen wird. wenn man nun dort die Bluetooth– Verbindung über diese Änderung informieren möchte, dann wird es zu einem Fehler kommen weil es ja noch keine Verbindung gibt (am ehestens bekommt man eine null pointer exception wenn man beispielsweise mit dem connectedThread– Objekt kommunizieren möchte).

Das oben beschriebene Fehlverhalten kann vermieden werden, indem man jene Komponenten einer Activity oder eines Fragmentes (also zum Beispiel das oben erwähnte bluetoothConnectedThread– Objekt – selbst auf die Änderungen im Life- Cycle reagieren läßt. Dabei ist die Activity oder das Fragment immer der Life- Cycle Owner. Die jeweilige Komponente ist der sogenannte Observer der über die Änderungen entsprechend informiert wird und darauf reagieren kann.

Neben dem Vorteil der Fehlervermeidung wird der Code lesbarer. Wenn man mehrere Komponenten hat die Life- Cycle abhängig sind, dann hat man in jeder Komponente den kompletten Code stehen und nicht teilweise in der jeweiligen Call- Back Methode der Activity oder des Fragemntes.

Parent Layout füllen

Klar, der Parameter match-parent hat den gewünschten, namensgebenden Effekt. Aber, wenn ich mehrere view– Elemente in einem parent darstelle und ich möchte, dass das am unteren Bildschirmrand steht genau die Größe hat, die noch zur Verfügung steht, was dann? Vor allem wenn es sich bei diesem Element um eine list- view handelt is das ein bisschen problematisch. eine Lösung steht hier.

  <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/fragment_fav_covid_data_view"
        app:layout_constraintBottom_toTopOf="@+id/nav_view"
        app:navGraph="@navigation/mobile_navigation" />

Entscheidend ist hier: Man setzte die Höhe auf null und den constraint auf den Kopf des folgenden Elements, das die Höhe einschränken soll (deswegen heist das auch constraint 😉

Singleton Pattern

Ist eine Klasse von der – während der Laufzeit eines Programms – nur ein Objekt abgeleitet werden kann.

Wird oft in Verbindung mit Datenbank- Zugriffen genannt und anhand solcher Beispiele auch erklärt. Ich persönlich habe noch keinen Fall gehabt, wo ich das gebraucht hätte. Ich beschränke mich deshalb erst einmal darauf das Pattern zu zeigen:

class SingletonExample {

// private field that refers to the object
   private static SingletonExample singleObject;

   private SingletonExample() {
      // constructor of the SingletonExample class
   }

   public static SingletonExample getInstance() {
      // write code that allows us to create only one object
      // access the object as per our need
   }
}

Das Code- Beispiel stammt aus der unter [1] verlinkten Quelle.

Man beachte: Der Leere Konstruktor ist als privat deklariert. Das bedeutet, neue Objekte können nicht außerhalb der eigenen Klasse erzeugt werden. Über die getInstance() Methode besorgt man sich eine Instanz dieser Klasse. Diese Instanz kann nur einmal erzeugt werden, wenn man sicherstellt, dass es nicht schon eine gibt. Beispiel:

 public static ThreadGetCovidData getInstance(getCovidDataInterface g,String sK,String sQ){
        if (getCovidData==null) {
            gC=g;
            apiAddressStadtkreise=sK;
            searchQuery=sQ;
            getCovidData = new ThreadGetCovidData();
        }
        return getCovidData;
    }

Das war es auch schon. Wenn ich mit eigenen Worten, ein Beispiel beschreiben kann, in dem es notwendig war ein Objekt genau einmal zu erzeugen und nicht etwa einfach eine statische Klasse zu verwenden, dann schreibe ich das hier hin.

[1] Prgrammiz.com: Singleton

Android App, Update prüfen

Ich beschreibe hier, wie man es schafft, die Nutzer einer in der Play- Store veröffentlichten App über Updates zu informieren.

Grundsätzlich habe ich mir zwei Varianten angeschaut. Die erste funktioniert indem man sich der Play Core Library bedient. Die Zweite Methode funktioniert über den Abgleich der aktuellen Versionsnummer der App auf dem Android Gerät mit der in der Play Store veröffentlichen Variante.

Play Core Library

Unter [1] findet man den Link zur offiziellen Dokumentation von Goggle.

Ich habe zunächst einmal nur ein Stück Code getestet, dass prüft ob ein Update verfügbar ist.

//
        // Play core library
        //
        AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(getApplicationContext());

        // Returns an intent object that you use to check for an update.
        Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();

        appUpdateInfoTask.addOnSuccessListener(new OnSuccessListener<AppUpdateInfo>() {
            @Override
            public void onSuccess(AppUpdateInfo result) {
                if (result.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
                    Toast.makeText(getApplicationContext(), "Update available.....", Toast.LENGTH_LONG).show();
                    Log.v("UPDATEUPDATE:", result.availableVersionCode() + "");
                }else
                    Log.v("UPDATEUPDATE:","No Update");
            }
        });

Ich habe den Code mit meiner App „Beam Calc“ getestet (Verlinkt unter [2]). Ob das funktioniert wäre noch zu prüfen. Ich werde berichten, wenn ich es getestet habe, also, spätestens nach dem nächsten Update der App.

Wenn ein neues Update da ist, dann kann man den Update Prozess direkt aus der App heraus starten.

Abgleich über die Versionsnummern im Play- Store und der App die auf dem Android Gerät läuft

Die hier gezeigte Lösung ist nichts anderes als Copy- Paste. Der zugrundeliegende Artikel findet sich unter [3]. Der hier abgebildete Code ist daraus entnommen.

Eine wichtige Voraussetzung ist: Wenn man einen neuen Release erzeugt, dann muss man unbedingt dafür sorgen, dass keine älteren APK’s dem neuen Release beigefügt werden. Ansonsten wird bei der Angabe der Versions- Nummer im Play Store (im übrigen gilt das auch für die verfügbaren Bildschirm Auflösungen) „Variiert ja nach Gerät“ angezeigt.

Der Code bedient sich der Jsoup- Library, einem HTML– Parser für Java. Den Link findet man unter [4]. Am schnellsten geht die Integration in das eigene Projekt über Gradle (das unten stehende in die build.gradle auf Modul- Ebene einfügen):

 // jsoup HTML parser library @ https://jsoup.org/
    implementation 'org.jsoup:jsoup:1.13.1'

Der eigentliche Versions- Checker schaut so aus:

import android.os.AsyncTask;
import org.jsoup.Jsoup;
import java.io.IOException;

/**
 * Version checker.
 *
 * Retrieves the version of this app from the Google Play- Store.
 *
 * Source: https://stackoverflow.com/questions/34309564/how-to-get-app-market-version-information-from-google-play-store
 */
public class VersionChecker extends AsyncTask<String, String, String> {

    String newVersion;

    @Override
    protected String doInBackground(String... params) {

        try {
            newVersion = Jsoup.connect("https://play.google.com/store/apps/details?id=" + "berthold.beamcalc" + "&hl=de")
                    .timeout(30000)
                    .userAgent("Mozilla/5.0 (Windows; U; WindowsNT 5.1; en-US; rv1.8.1.6) Gecko/20070725 Firefox/2.0.0.6")
                    .referrer("http://www.google.com")
                    .get()
                    .select("div.hAyfc:nth-child(4) > span:nth-child(2) > div:nth-child(1) > span:nth-child(1)")
                    .first()
                    .ownText();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return newVersion;
    }
}

Der Aufruf wird so erledigt:

 //
        // Version checker
        //
        VersionChecker vc=new VersionChecker();
        try {
            String latest = vc.execute().get();
            Toast.makeText(getApplicationContext(), "Latest Version Code:"+latest, Toast.LENGTH_LONG).show();
        } catch (Exception e){}
    }

Ich werde an dieser Stelle weiter über die Ergebnisse berichten.

[1] Google Play Core Library, ofizelle Dokumentation von Google.
[2] Beam Calc App.
[3] Stack Overflow, Get version from Play Store.
[4] jsoup: Java HTML Parser.

Einfeldträger (Beam Calc) neue Version 1.3

Die neue Version ist im Testbetrieb und wird in den nächsten Tagen auf Google- Play veröffentlicht.

Einfeldträger (Beam Calc) Version 1.3

Die Benutzeroberfläche wurde etwas entschlackt. Die Maßketten können nun nicht mehr ausgeblendet werden und werden immer angezeigt. Die Anzeige der resultierenden Auflagerkräfte und der mathematische Lösungsterm kann über das Antippen eines einzelnen Symbols ein oder ausgeschaltet werden.

Die einwirkenden Lasten können nun etwas komfortabler und übersichtlicher in einem eigenen Eingabedialog definiert werden. Die Textgröße in der grafischen Darstellung der Lösung wurde angepasst. Die Maßketten und die Angaben zu den Lasten sind jetzt auch auf kleineren Bildschirmen besser lesbar.

[1] Projektseite zur App, hier im Blog.

Einsatzplan

Ich habe die app verbessert. Aktuelles hier, genaueres kann man auf der Projektseite dazu, hier im Blog, nachlesen. Der Link findet sich am Ende dieses Artikels unter [1].

Im wesentlichen handelt es sich bei der app um ein Tool, mit dem man Kalendereinträge, die in einer Textdatei, zeilenweise, abgelegt sind in eine besser lesbare Liste umwandeln kann die sich dann filtern oder durchsuchen lässt. Das schönste daran ist aber, dass man einzelne Einträge oder Eintragserien ganz einfach in die Kalender app des Android Gerätes übernehmen kann.

Derzeit funktioniert das nur für Einsatzpläne die von der Firma für die ich arbeite erzeugt werden. Eine Anpassung kann aber ganz leicht, für ähnliche Einsatzzwecke und andere Datenformate erledigt werden.

Einsatzplan. Für jeden Eintrag kann eine Detailansicht ausgewählt werden, die es erlaubt einen Lehrgangstag oder den kompletten Lehrgang in die Kalender app des Android Gerätes zu übernehmen.

Ich habe das erzeugen von Kalendereinträgen und E- Mail Anfragen vereinfacht. Beides kann jetzt aus der Detailansicht, die man für jeden Eintrag aufrufen kann, heraus erledigt werden. Der Code wurde etwas entschlackt und optimiert. Im Ergebnis läuft die app jetzt sehr flüssig.

Details zur app habe ich hier im Blog veröffentlicht:

[1] Einsatzplan app, Projektseite.

Beam Calc II

Ich habe die Library überarbeitet. Es werden nun auch veränderliche Linien- Lasten unterstützt. Dreiecks Lasten und Trapez- Lasten können eingegeben und berechnet werden.

Beam Calc II, der Name ist etwas irreführend. Es gibt nämlich keinen ersten Teil. Die Namensgebung kommt daher, weil ich die erste Version über Code Review habe beurteilen lassen. Das verbesserte Ergebnis ist Beam Calc II.

Die Library kann man sich hier anschauen/ herunterladen: GitHub CodingByChanche.

Beispiel Rechnung mit detailliertem Ergebnis.

Die Library benutze ich zur Entwicklung einer Android App. Die kann man sich hier im im Blog anschauen: Projekte, Einfeldträger.

Wer möchte kann mich im Google Play Store besuchen, dort kann man sich die aktuelle Version dieser App direkt herunterladen. Einfeldträger (BeamCalc) bei Google Play.

Einfeldträger (BeamCalc) App.

Android Live Data

Livedata Ist Bestandteil der Android Architecture Components. Ermöglicht es einer Activity oder einem Fragment auf Änderungen in einem View Model zu reagieren.

Das ViewModel ist eine prima Sache, wenn man vorübergehend Nutzerdaten einer Activity oder eines Fragmentes einfach verwalten möchte.

LiveData kann mit einem ViewModel kombiniert werden, wenn man möchte, dass die aktive Activity oder das aktive Fragment unmittelbar auf Änderungen der Daten im ViewModel reagiert. Man erspart sich damit beispielsweise die implementation einer eigenen Ablauflogik (savedInstanceState). LiveData ist vergleichbar mit dem Observer Pattern. der Unterschied ist aber der, dass LiveData den Android LifeCycle kennt und nur aktive Komponenten über Updates informiert.

Ich stelle hier eine kleine App vor, die das ganze demonstriert.

Demo App

Vorbereitung

Im build.grade für die app müssen die aktuellen Bibliotheken eingetragen werden:

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'<br>implementation 
"androidx.lifecycle:lifecycle-viewmodel:2.2.0"

Layout

In der layout- Dateie ist nicht besonderes zu beachten. Ich habe die hier nur der Vollständigkeit halber mit aufgenommen.

<?xml version="1.0" encoding="utf-8"?>
<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">

    <TextView
        android:id="@+id/liveDataView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="LIVE_DATA"
        app:layout_constraintHorizontal_bias="0.533"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/updateLiveDataView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Update"
        app:layout_constraintTop_toBottomOf="@+id/liveDataView"
        tools:layout_editor_absoluteX="39dp"></Button>

</androidx.constraintlayout.widget.ConstraintLayout>

Das ViewModel

package com.example.livedatatest;

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class MainViewModel extends ViewModel {

    private MutableLiveData<String> name;


    /**
     * Note: There is no 'setName' method within this viewModel....
     * LiveData Objects are updated by invoking  the observers 'setValue' method
     *
     * setValue should be used from the mainThread. postValue can be used
     * from any other thread.
     *
     * @return Either an empty string or the updated liveData which was
     * passed by the 'set' Method invoked by the active Activity or Fragment
     */
    public MutableLiveData<String> getName(){
        if (name==null)
            name=new MutableLiveData<String>();
        return name;
    }
}

Das ViewModel soll ein einfaches String– Objekt für seine verbundene Activity speichern. Das Entscheidende hier ist, dass man keine getter- Methode für das Objekt findet. Weshalb das so ist, wird deutlich, wenn man sich die Activity anschaut.

Die Activity

package com.example.livedatatest;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private  MainViewModel mainViewModel;
    private Button updateLiveDataView;
    private TextView liveDataView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // UI
        updateLiveDataView=findViewById(R.id.updateLiveDataView);
        liveDataView=findViewById(R.id.liveDataView);

        // View Model
        mainViewModel = new ViewModelProvider(this).get(MainViewModel.class);

        // Create the observer which updates the UI.
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                liveDataView.setText(newName);
            }
        };

        // Observe Live Data
        //
        // This invokes onChanged method of the nameObserver if the liveData object
        // in the viewModel holds data. If not, onChanged is not invoked...
        mainViewModel.getName().observe(this, nameObserver);


        // Update the view which displays the current liveData objects
        // contents....
        // Replaces the current value inside the liveData Object with this...
        updateLiveDataView.setOnClickListener(v -> {

            // There is no dedicated 'setter' method for the liveData.
            // Live Data Objects are updated by invoking the 'setValue'
            // method of he Observer.
            // When not inside the main thread, use the 'postValue'
            // method instead.
            Long time=System.currentTimeMillis();
            mainViewModel.getName().setValue("Hi+"+time);
        });
    }
}

Es wird ein Observer implementiert, der die Methode onChanged enthält. Die Methode getName aus dem ViewModel wird über Methode observe mit dem ViewModel verbunden.

Wenn man nun das LiveData Objekt aktualisiert, dann wird jedesmal die onChanged– Methode des Observers aufgerufen. Die Akltuallisierung geschieht nun nicht über eine eigene setter- Methode im ViewModel, sondern über die ‚setValue‚ Methode des Observers.

mainViewModel.getName().setValue("Hi+"+time); 

Wenn man das LiveData- Objekt nicht vom main- thread aus aktuallisiert, sondern, beispielsweise aus einem anderen thread heraus, dann sollte man stattdessen die ‚postValue‚ Methode benutzen.

Noch etwas grob, nur so zur Notiz…..