Atomare Updates mit Android LiveData

Android’s LiveData is a valuable asset in wrinting easy to maintain software. It introduces a life-cycle for data updates that help to reduce boilerplate code and make user interfaces more flexible. Unfortunately LiveData only covers one data point. If you need more data in your UI, you’ll face the problem that you see each update separately and therefore have unnecessary updates of your UI. Here I’ll show you how to get rid of those by updating the UI with atomic updates.

Introduction: LiveData basics

LiveData follows the observer pattern which was described over 25 years ago: you have a mutable object and you get informed when this object changes. What makes this flexible is that there can be zero or many observers on the object. This allows loose coupling of the components of an application.

When it comes to data updates, concurrency is always something to pay attention to. On Android there is a special thread called the main or UI thread. All UI updates happen on it as well as the handling of all UI events like button presses. It is therefore imperative that you don’t block this thread for too long and it is prohibited to do network IO on it. LiveData notifies its observers on the main thread as well. It therefore offers two methods that are important for multi-threading:

  • setValue(x)
  • postValue(x)

The difference is that setValue may only be called on the main thread but changes the value immediately while postValue may be called on any thread and puts the update to the value into the queue on the main thread. This means that any changes via postValue do not take effect immediately but delayed. If you run this code:

liveData.setValue(1);
liveData.getValue();  // returns 1
liveData.postValue(2);
liveData.getValue();  // still returns 1!

then both calls to getValue will return 1 as the change to 2 has not happened yet on the second call.

If you run this code

liveData.postValue(2);
liveData.setValue(1);

then every observer will first get the value 1 and then the value 2.

What happens if you update it several times? This code (which can only run on the main thread)

liveData.setValue(1);
liveData.setValue(2);

will call each observer twice (immediately), first with 1 and then with 2, while this code (which can run on any thread)

liveData.postValue(1);
liveData.postValue(2);

will call the observers only once with the value 2 after everything is done on this call from the main thread.

The problem

Imagine you have a UI that shows two different items. Let’s say it is a map that has a location and a zoom level. Both of these values can change independently. They are also unrelated in the sense, that each can live without the other. You could model them into one object LocationAndZoom, but this would violate the single responsibility principle. So you would model them with two independent LiveData:

LiveData<Location> liveLocation;
LiveData<Zoom> liveZoom;

You want your UI to update whenever one of them changes, but you need both values to paint the map.

liveLocation.observe(this, t -> updateMap());
liveZoom.observe(this, s -> updateMap());

Now picture that after a search you want you to update both location and zoom level. So you would do

liveLocation.postValue(searchResult.getLocation());
liveZoom.postValue(ZOOM_CITY);

This results in two calls to updateMap(), one with stale and one with current data. As map redraws are expensive, we should optimize it!

The solution

So you want to get only one update no matter how many of your UI variables change at the same time. For this you need another LiveData with an object that consolidates all data that you need:

private static class LocationAndZoom {
    private final Location location;
    private final Zoom zoom;

    private LocationAndZoom(Location location, Zoom zoom) {
        this.location = location;
        this.zoom = zoom;
    }
}

This is not a violation of the single responsibility principle as a change to it is driven by the same stake holder („and now we need humidity as well in the UI“). Now we can define a LiveData that holds this LocationAndZoom. But how can we achieve that it only notifies its observers once even when there are multiple updates? We will use the fact that postValue postpones updates to the next run of the main thread. This is the idea:

1st run of the main thread

Both LiveData will be updated:

liveLocation.postValue(searchResult.getLocation());
liveZoom.postValue(ZOOM_CITY);

2nd run of the main thread

This results in two calls to the observeable which will update the consolidated LiveData:

// from liveLocation observer:
liveLocationAndZoom.postValue(new LocationAndZoom(...)); // <= here the zoom is stale

// from liveZoom observer
liveLocationAndZoom.postValue(new LocationAndZoom(...)); // <= here both are recent

Certainly not like this in the code, but you should get the point that there are two updates.

3rd run of the main thread

As both calls are postValue, only the last one wins and resulting in just one call to the observables of liveLocationAndZoomd with the recent value.

Implementation

The MediatorLiveData allows to change LiveData objects on the fly. This is used to change from the source changes to the consolidated change. To simplify the code use this class:

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.Observer;

public abstract class Merger<T> {
    private MediatorLiveData<T> liveMerged;

    private T currentMerged;

    protected abstract boolean isValid(final T merged);

    public synchronized LiveData<T> getMerged() {
        if (null == liveMerged) {
             liveMerged = new MediatorLiveData<>();
             init();
        }
        return liveMerged;
    }

    protected abstract void init();

    protected <S> void addSource(
            @NonNull LiveData<S> source, @NonNull Observer<? super S> observer) {
        liveMerged.addSource(source, observer);
    }

    protected T getCurrentMerged() {
        return currentMerged;
    }

    protected void updateCurrent(T newMerged) {
        currentMerged = newMerged;
        if (isValid(currentMerged)) {
            liveMerged.postValue(currentMerged);
        }
    }
}

now you can implement the consolidates LiveData like this:

private static class LocationAndZoomMerger extends Merger<LocationAndZoom> {
    private final LiveData<Location> liveLocation;
    private final LiveData<Zoom> liveZoom;

    private LocationAndZoomMerger(final LiveData<Location> liveLocation,
                                  final LiveData<Zoom> liveZoom) {
        this.liveLocation = liveLocation;
        this.liveZoom = liveZoom;
    }

    @Override
    protected void init() {
        updateCurrent(new LocationAndZoom(null, null));
        addSource(
            liveLocation,
            l -> updateCurrent(new LocationAndZoom(l, getCurrentMerged().zoom)));
        addSource(
            liveZoom,
            z -> updateCurrent(new LocationAndZoom(getCurrentMerged().location, z)));
    }

    @Override
    protected boolean isValid(final LocationAndZoom locationAndZoom) {
        return null != locationAndZoom.location && null != locationAndZoom.zoom;
    }
}

Finally observe the merged LiveData and enjoy atomic updates:

new LocationAndZoomMerger(liveLocation, liveZoom)
        .getMerged()
        .observe(this, m -> Log.e("Atomic", "update"));

 

Bildschirm des Model 3 wird schwarz, wenn der Fahrer das Fahrzeug verlässt [GELÖST]

Wenn der Fahrer das Model 3 verlässt, wird die Musik aus- und der Bildschirm abgeschaltet. Das ist natürlich unschön, wenn ein Beifahrer im Auto sitzt. Wie bekommt man den Bildschirm wieder an?

Einfach auf den Bildschirm tippen, wenn er schwarz ist und er schaltet sie wieder ein und damit auch die Musik.

SD Karte als Speicher für Apps auf dem Fairphone 3 verwenden

Ich habe mir ein Fairphone 3 mit Android 9 zugelegt und eine 200GB SD-Karte. Leider wurde diese nicht als Speicher für Apps erkannt, sondern lediglich zum Austauschen von Daten. Da ich es als Erweiterung für die lediglich 64GB internen Speicher (ich bin wohl ein Power-User, da ich mehr brauche) vorgesehen habe, war das natürlich nicht so dolle. Nach etwas Rumprobieren habe ich herausgefunden, wie es geht:

Man muss die Partitionierung der SD-Karte entfernen – bei Windows heißt das glaube ich „fabrikneu“. Unter Linux erreicht man dies am einfachsten, indem man das erste MB mit Nullen überschreibt:

sudo dd if=/dev/zero bs=1M count=1 of=/dev/mmblk0

Danach erhielt ich auf dem Fairphone die Abfrage, ob ich es als App-Speicher nutzen will und freue mich jetzt über 264GB Speicher.

Alle unterstützten Audioformate von Tesla

Ein Tesla hat USB-Ports über die er Audiodaten abspielen kann. Aber welche Dateiformate und Codecs werden unterstützt? Ich habe es ausprobiert und hier das Ergebnis: MP3, AAC, Vorbis und Flac werden unterstützt.

EndungCodecUnterstützt?
m4aAAC - Advanced Audio CodecJa
mp3MP3 - MPEG Audiio Layer 3Ja
oggOGG/VorbisJa
flacFLAC - Free Lossless Audio CodecJa
ac3ATSC A/52 - AC-3, Dolby DigitalNein
m4aApple iTunes ALAC / AAC-LCNein
mp2MP2 - MPEG Audui Layer 2Nein
oggOPUSNein
ttaTrue Audio Lossless AudioNein
wmaWindows Media AudioNein

Wie man sieht werden die üblichen Verdächtigen MP3 und AAC unterstützt. Interessant für audiophile Hörer ist das verlustfreie Format FLAC, was zwar große Dateien erzeugt, dafür aber den Sound 1:1 vom Original abspielt. Und auch die Unterstützung des freien OGG/Vorbis-Formates erfreut, da damit auch Nutzer Freier Software eine Möglichkeit haben, ihre Musik abzuspielen. Verwunderlich ist allerdings die Abwesenheit von WMA. Anscheinend wird dem Microsoft-Format keine große Zukunft zugetraut.

Zuschuss-Karte bei Ölmagnat

Zuschuss-Karte von Öl-MagnatAls Kind haben wir immer Ölmagnat gespielt. Schon damals hat uns geärgert, dass in der Anleitung die „Zuschuss $ 500“-Karte nicht erklärt war. In der Tat findet man in der Anleitung lediglich den Halbsatz, dass die Karte bedeutet, dass „eine Öl-Ausschöpfungsgenehmigung erteilt wird“. Jedoch wird nirgendwo erklärt, was dies bedeutet.

Glücklicherweise gibt es die Original-Anleitung von KingOil von Hasbro. Hier heißt es: „The oil depletion allowance card of $500 is a payment of $500 without regard to the number of derricks a player has erected.“ auf Deutsch: „Die Öl-Ausschöpfungsgenehmigungskarte über $500 ist eine Auszahlung von $500, umabhängig davon, wie viele Bohrtürme ein Spieler errichtet hat.“

Dies bedeutet, dass der Spieler der diese Karte zieht, immer $500 erhält, egal wie viele Bohrtürme er hat.

Damit ist auch klar, dass wir das damals immer falsch gespielt hatten, denn bei uns musste der Spieler die $500 bezahlen.

Bonus:

Bohrturm von Öl-Magnat Wisst ihr, warum der Bohrturm unterschiedliche Farben hat? In der US-Version wurden die Kosten einer erfolgreichen Bohrung nicht erwürfelt, sondern hingen davon ab, wie tief der Bohrturm in das Spielfeld eindrang:

  • war er ganz drin, so war das Bohrloch trocken, also genau umgekehrt, wie bei der deutschen Variante, wo dies bedeutet, dass man Öl gefunden hat
  • ist er eine Stufe draußen (rot), so muss der Spieler $6.000 bezahlen
  • sind zwei eine Stufe draußen (gelb), so muss der Spieler $4.000 bezahlen
  • sind alle 3 Stufe draußen (blau), so muss der Spieler $2.000 bezahlen

 

xscreensaver sperrt nicht richtig

Ich verwende seit ein paar Tagen XScreensaver unter Manjaro Linux. Wenn ich damit mein Laptop in den Schlafmodus versetze und wieder aufwecke, dann ist der Bildschirminhalt noch kurz zu sehen, bevor der Sperrbildschirm kommt. Dies ist natürlich nicht sonderlich schön, da so jeder einfach an mein Laptop gehen und den zuletzt dargestellten Inhalt sehen kann.

Der Grund hierfür ist eine Einstellung, die beim Sperren den Bildschirm sanft ausblendet. Wird das Laptop in den Schlafmodus gesetzt, so wird zuvor zwar die Bildschirmsperre aktiviert, diese wird mit dem Ausblenden aber nicht fertig, bevor der Schlafmodus erreicht wird. Dem entsprechend wird das Ausblenden nach dem Aufwachen weiter ausführt, aber während diese Zeit, bleibt der Bildschirminhalt weiter sichtbar.

Was kann man dagegen tun? Die einfachste Lösung ist das Deaktivieren des „Übergang beim Schwärzen“. Hierzu xscreensaver-demo starten und oben auf Komplex klicken. Dort dann unten rechts bei Übergang und Farbpalette den Haken bei Übergang beim Schwärzen entfernen:

xscreensaver Einstellungsdialog

Sobald man dies gemacht hat, wird der Bildschirm beim Sperren sofort schwarz und dem entsprechend ist nichts mehr sichtbar, wenn das Laptop aufgeweckt wird.

Über 20% schnelleres Laden beim Tesla Model 3

WARNUNG: Mit diesem Trick umgeht man Sicherheitsvorkehrungen von Tesla. Deshalb nur verwenden, wenn ein zertifizierter Elektriker die Unbedenklichkeit bescheinigt hat!

Nachdem diese gesagt wurde geht es in alter Sledge Hammer „Ich weiß was ich tue!“-Tradition los: das von Tesla mitgelieferte Ladegerät für Schuko-Dosen ist auf 13A begrenzt:

Ladung mit 13A

Wichtig ist hierbei die Zahl unterhalb des Fahrzeuges: 13 von 13A 230V

Sie gibt an, dass gerade mit 13 Ampère geladen wird und die netzseitige Spannung 230 Volt beträgt (nicht zu verwechseln mit dem gleichnamigen Elektroauto). Daraus ergibt sich eine Ladeleistung von

13 * 230 = 2.990 Watt

was unten links auch als „3 kW“ angezeigt wird. Soweit so gut.

Das mitgelieferte Ladegerät kommt nicht nur mit einem Anschluss für Schuko-Steckdosen („normale Haushaltssteckdose“), sondern auch mit einem für „Blaues CEE„, was man beispielsweise auf Campingplätzen und Wohnmobilstellplätzen findet.

 


Dazu passend gibt es einen Adapter von CEE auf Schuko, den man beispielsweise braucht, um die Bordbatterie eines Wohnmobils an einer Haushaltssteckdose aufzuladen.

 

 

 

Jetzt stellt sich natürlich die interessante Frage was passiert, wenn man damit einen Tesla lädt?

 

 

Und siehe da, der maximale Ladestrom beträgt jetzt 16 Ampère:

Daraus ergibt sich eine Ladeleistung von

16 Ampère * 227 Volt = 3.632 Watt

also gute 21,5% mehr, als beim Schukostecker, was schon beeindruckend ist!

Aber wenn das so einfach geht, warum lädt dann das normale Netzteil nicht mit 13A? Die Ursache sind die Stromleitungen im Haushalt, die normalerweise nicht für diese Leistungen ausgelegt sind. Der Strom ist zwar mit 16A abgesichert, aber dieser verteilt sich üblicherweise auf mehrere Steckdosen. Ist es deshalb gefährlich so viel Strom über eine Steckdose zu ziehen? Kommt darauf an!

Alle Leitungen üben auf Strom einen Widerstand aus, der sie erwärmt – genau so funktioniert eine Elektroheizung: Strom durch einen Widerstandsdraht. Die wichtige Frage ist jetzt, wie viel Strom wird in den Leitungen in Wärme umgewandelt? Hierzu muss man schauen, wie hoch die Spannung direkt nach dem Start der Ladung ist, wenn noch keine Ladung erfolgt:

In diesem Fall sind es 235V. Bei Ladung mit vollem Strom sind es 227V. Hieraus ergibt sich

(235 Volt – 227 Volt) * 16 Ampère = 128 Watt

Unser Stromkabel und die Steckdose haben sich also in eine Heizung mit 48 Watt verwandelt. Das klingt nach nicht viel, wer aber man an eine CPU im Betrieb ohne Kühlkörper gefasst hat weiß, dass 128 Watt ganz schön heiß machen können.


Ganz gefährlich wird es, wenn man ein Verlängerungskabel benutzt. Ich habe zur Demonstration mal dieses Verwendet, was nur für geringe Ströme ausgelegt ist.

 

 


Vergleicht man die Kabel – links das vom Tesla Ladegerät und rechts das von dieser Kabeltrommel – sie sieht man schon, dass dies keine wirklich gute Idee ist.

Macht dies nicht zu Hause!

Ich hab es auch im Freien vor meiner Garage gemacht.

 

 

Wenn man sich die gesamte Konstruktion anschaut, sieht es auch nicht sonderlich vertrauenserweckend aus.

 

 

Ein Blick auf die Ladeanzeige bestätigt den Verdacht:

Die Spannung ist auf 221 Volt eingebrochen, also um ganze 14 Volt! Damit ergibt sich eine Heizleistung von:

(235 Volt – 221 Volt) * 16 Ampère = 224 Watt

Wenn man bedenkt, dass eine Mikrowelle mit 750 Watt arbeitet, sieht man, dass eine geschmolzene Kabeltrommel noch das kleinere Übel wäre. Im schlimmsten Fall kann es zu einem Brand kommen!

Wenn man allerdings eine dedizierte, moderne Steckdose hat, die fachmännisch angeschlossen ist und 16A zulässt, dann steht dem schnelleren Laden nichts im Weg.

Wer selbst mal einen Tesla schneller laden sowie 1.500km kostenloses Superchargen möchte, der kann meinen Empfehlungscode verwenden:

https://ts.la/kurt59764

Mit dem Model 3 in der Waschstraße

  1. Wie jedes andere Auto auch, sollte man sein Tesla Model 3 hin und wieder mal waschen. Gerade jetzt, wenn es länger nicht regnet, lohnt es sich, die Fliegen und den Staub abzuwaschen. Bei der Fahrt durch eine Waschstraße gibt es ein paar Dinge zu beachten, damit alles reibungslos funktioniert.

Scheibenwischer ausZuerst muss man die Scheibenwischer abschalten, damit sie in der Waschanlage nicht angehen und beschädigt werden können. Hierzu im Display unten links auf das Scheibenwischersymbol drücken. Falls man eine andere Ansicht hat, kann man auch einmal den Blinkerhebel reindrücken, dann wischt es einmal und das Bild erscheint. Hier einfach einmal auf das Wischersymbol tippen, um die Automatik zu deaktivieren.

Spiegel einklappenWenn man der Waschstraße nicht ganz vertraut, kann man sicherheitshalber die Spiegel einklappen. Hierzu im Display unten auf das Auto-Symbol tippen und in den Schnelleinstellungen die Spiegel einklappen.

Sobald man dann in die Waschstraße reingewunken wurde, muss man den Leerlauf einlegen. Hierzu den rechten Hebel eine (1) Raste nach oben drücken und 2 Sekunden festhalten. Damit ist der Leerlauf eingelegt:

Leerlauf eingelegt

Wichtig ist, dass man den rechten Hebel nicht ganz nach oben drückt, sondern nur halb bis zum ersten Widerstand, da ansonsten der Rückwärtsgang eingelegt wird.

Wer noch keinen Tesla für die Waschstraße hat und 1.500km kostenloses Superchargen möchte, der kann meinen Empfehlungscode verwenden:

https://ts.la/kurt59764

Wie viel Energie steckt im Model 3 bei 170 km/h?

Bei dem erfolgreichen Weltrekordversuch neulich hat Bjørn Nyland überrascht festgestellt, dass beim Abbremsen durch Rekuperation von 170km/h der Akku wieder 1% Ladung erhalten hat. Ist dies erstaunlich? Rechnen wir mal nach:

Die kinetische Energie eines Körpers in Bewegung ist

E = ½ mv²

nicht zu verwechseln mit E=mc², was die Energie bei Umwandlung in Strahlung ist, was wir mit dem Model 3 besser nicht machen sollten. Das Leergewicht des verwendeten Tesla Model 3 LR AWD beträgt 1.847 kg; Bjørn war nicht alleine im Auto von daher rechnen wir mal mit rund 1.900 kg. Die 170 km/h sind

170km/h / 3.600s * 1.000m = 47,2m/s

Hieraus ergibt sich:

E = ½ * 1.900kg * (47,2 m/s)² = 2.116.448Ws

Umgerechnet in kWh sind dies

2.116.448Ws / 3.600s = 587,9 Wh = 0,5879kWh

Bei einer Batteriekapazität von 75 kWh sind dies 0,78%. Geht man von einer Effizienz der Rekuperation von 80% aus, so kommt man auf 0,62%. Das sind zwar nicht ganz 1%, aber es erklärt gut, warum die Ladeanzeige um eins hochgesprungen ist.

Wenn wir schon dabei sind: wie viel Strom benötigt man für einen Sprint von 0 auf die Maximalgeschwindigkeit von 232 km/h?

232km/h / 3.600s * 1.000m = 64,4m/s

was wiederum

E = ½ * 1.900kg * (64,4 m/s)² = 3.939992Ws = 1,094Wh = 1,46%

sind. Über den Daumen gepeilt entleert also ein Sprint auf Maximum die Batterie um 1,5%. Wenn man nicht „in die Eisen geht“ sondern rekuperiert, so bekommt man immerhin fast 1,2% wieder zurück, so dass einen der Spaß etwa 0,3% Akku kostet.

Na dann viel Spaß!

P.S.: In der Praxis werden es durch den Luftwiderstand allerdings etwas mehr sein – dafür hat mich sich aber auch fortbewegt.

Wer das selbst ausprobieren und sich hierfür einen Tesla zulegen möchte und 1.500km kostenloses Superchargen dazu, der kann meinen Empfehlungscode verwenden:

https://ts.la/kurt59764