[JAVA] Wichtige Punkte bei der Entwicklung von Android-Apps, an denen altmodische Programmierer gestolpert sind

Was ist das?

Früher habe ich hauptsächlich Anwendungen geschrieben, die auf dem PC-Desktop oder der Befehlszeile in C / C ++ oder Java ausgeführt werden, jetzt schreibe ich hauptsächlich Android-Apps. Aus der Sicht von Programmierern, die mit der Entwicklung altmodischer Desktop- und Befehlszeilen-Apps begonnen haben, besteht eine große kulturelle Lücke bei der Entwicklung von Android-Apps. Ich frage mich, ob das der Grund ist, warum manche Leute nicht das tun, was die Android-Entwicklung will, oder frustriert sind. Für diese altmodischen Programmierer möchte ich einige Punkte der Android-Entwicklung schreiben, die ich beachten möchte.

Wo ist der Einstiegspunkt?

Für altmodische Programmierer sind Computerprogramme "klare Anfänge und Enden". Nehmen Sie zum Beispiel das folgende C-Programm.

#include <stdio.h>

int main(int argc, char* argv[]){
    printf("Hello, world!");
    return 0;
}

Dieses Programm beginnt am Anfang der Hauptfunktion und endet mit dem Verlassen der Hauptfunktion. Es ist so klar wie es nur sein kann. Gleiches gilt für GUI-Programme. Wenn Sie beispielsweise das X-Fenstersystem verwenden, sieht der Code folgendermaßen aus:

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Label.h>

int main(int argc, char* argv[]){
    XtAppContext context;
    Widget root, label;

    root = XtVaAppInitialize(&context, "Hello, world!", NULL, 0, &argc, argv, NULL, NULL);
    label = XtVaCreateManagedWidget("Hello, world!", labelWidgetClass, root, NULL);
    XtRealizeWidget(root);
    XtAppMainLoop(context);
    return 0;
}

Dies beginnt ebenfalls am Anfang der Hauptfunktion und endet mit dem Verlassen der Ereignisschleife und dem Verlassen der Hauptfunktion.

Aber was ist mit Android? Lassen Sie uns zunächst ein neues Projekt in Android Studio erstellen. Beim Erstellen eines Projekts stehen mehrere Vorlagen zur Auswahl. Wählen Sie jedoch zunächst Leere Aktivität. Anschließend werden verschiedene Dateien erstellt, aber MainActivity.kt (im Fall von Kotlin) mit den folgenden Inhalten wird auf dem Bildschirm angezeigt.

MainActivity.kt


package test.app.helloworld

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

Es wäre für manche Leute nicht seltsam, das zu denken:

Ich bin neugierig, dass verschiedene Dateien ohne Erlaubnis erstellt werden, aber vielleicht ist dieses onCreate () der Einstiegspunkt! Das heißt, die App muss geschlossen sein, wenn Sie dieses onCreate () beenden!

** Leider nicht. ** ** **

Kein Einstiegspunkt oder Ereignisschleife (unsichtbar)

Android Apps haben auch einen "Anfang". Aber es ist nicht die "MainActivity # onCreate ()" im obigen Code. Diese Methode ist ein Handler, der aufgerufen wird, wenn einer der Bildschirme der App erstellt wird, nicht wenn die App gestartet wird. Wo heißt die Methode, wenn die App startet?

Tatsächlich schreiben Sie bei der Entwicklung von Android-Apps keinen Einstiegspunkt, der der Hauptfunktion in C-Sprache entspricht. In Bezug auf die interne Verarbeitung des Betriebssystems werden Android-Apps als unabhängige Linux-Prozesse ausgeführt. Daher sollte es einen Einstiegspunkt geben, der intern der Hauptfunktion entspricht, aber das Android-Betriebssystem (Android-Framework) tut dies. Ich verstecke es. Daher schreibt der App-Entwickler keinen Eingabezeiger. Ebenso werden Ereignisschleifen vom Betriebssystem ausgeblendet und nicht geschrieben. App-Entwickler werden sich darauf konzentrieren, Handler für Ereignisse zu schreiben, die aus der Ereignisschleife gesendet werden.

Hmmm, aber ist das nicht selbstverständlich?

Genau. Das Ausblenden von Einstiegspunkten und Ereignisschleifen ist für das GUI-Framework von Imadoki nichts Besonderes. Ich denke jedoch, dass selbst solche Frameworks häufig eine Möglichkeit bieten, auf Ereignisschleifen zuzugreifen. Android hingegen bietet Apps keine Möglichkeit, auf Ereignisschleifen zuzugreifen. Daher ist es nicht möglich, eine eigene Ereignisschleife zu erstellen, was auf anderen Plattformen möglich ist. vielleicht.

Der Ereignishandler zum Starten der App lautet "Application # onCreate ()"

Ich hoffe, Sie verstehen, dass Sie Event-Handler schreiben, wenn Sie Android-Apps entwickeln. Wo heißt der Handler, wenn die App startet? Dies ist die onCreate () -Methode einer Klasse, die von der android.app.Application-Klasse erbt.

Was? Es gibt nirgendwo eine solche Klasse?

Ja, es gibt keine Quelle für solche Klassen in Projekten, die aus Vorlagen erstellt wurden. In solchen Fällen akzeptiert Android die Implementierung der Klasse "android.app.Application". Infolgedessen scheint der Programmierer keinen "Handler zu haben, der beim Starten der App aufgerufen wird". In den meisten Android-App-Entwicklungen müssen Sie diesen Handler nicht kennen. In einigen Fällen möchten Sie jedoch möglicherweise "Initialisierungsprozess für die gesamte Anwendung" schreiben. Implementieren Sie in solchen Fällen eine Klasse, die von der Klasse "android.app.Application" erbt, und geben Sie diese Klasse in "AndroidManifest.xml" an.

MainApplication.kt


package test.app.helloworld

import android.app.Application
import android.util.Log

class MainApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Log.d("MainApplication", "Start application!")
    }
}

AndroidManifest.xml


    <application
        android:name=".MainApplication"

Überlassen Sie den Exit der App dem Betriebssystem

Ich verstehe den Beginn des Programms. Wie wäre es mit dem Ende? In vielen Fällen, z. B. beim X-Fenstersystem, verlässt die Anwendung die Ereignisschleife und endet, wenn keine Fenster mehr auf dem Bildschirm angezeigt werden können. Beendet Android so die App automatisch, wenn auf dem Bildschirm nichts angezeigt werden kann?

Dieser Punkt ist etwas schwer zu beantworten. Die Antwort lautet "Nein", wenn Sie an "Programmende = Prozessende" denken. Unter Android endet der Vorgang der App nicht sofort, wenn alle Bildschirme (Aktivität) geschlossen sind. Wenn jedoch alle Bildschirme (Aktivität) und Dienste (Dienst) geschlossen sind, führt die App praktisch nichts aus. In diesem Sinne können Sie sich die App als geschlossen vorstellen.

Unter Android kann (und sollte) die Beendigung des App-Prozesses nicht von der App gesteuert werden. Der Prozess der App lautet "Beenden, wenn das Betriebssystem beendet werden möchte". Es ist nicht möglich, dass sich die App explizit selbst beendet. Es wird vom Betriebssystem beendet.

So eine Dominanz!

Ich verstehe dieses Gefühl gut. Aber Android hat einen solchen Mechanismus. Vordergrund-Apps (derzeit laufende Apps) werden jedoch selten abrupt beendet. Normalerweise können Apps, die in den Hintergrund treten und lange Zeit nicht verwendet wurden, gekündigt werden. Das Betriebssystem leistet überall gute Arbeit.

Die Tatsache, dass Sie den Prozess einer App nicht explizit beenden können, kann für einen altmodischen Programmierer sehr seltsam erscheinen. In Anbetracht der folgenden Punkte erscheint dieser Mechanismus jedoch vernünftig.

Mit anderen Worten, Sie können auf Ihrem Smartphone keine "benutzergesteuerte Beendigung der App" erwarten. In diesem Fall ist es sinnvoll, dass das Betriebssystem die App zum richtigen Zeitpunkt beendet.

Die Tatsache, dass es ohne Erlaubnis des Betriebssystems beendet wird, bedeutet, dass es bei der Anwendungsentwicklung erforderlich ist, es zu implementieren, damit es jederzeit beendet werden kann. Grundsätzlich wird die folgende Methode verwendet.

Ich habe auch geschrieben, dass Sie einen App-Prozess nicht explizit beenden können, aber Sie können eine Aktivität normal beenden. Ich werde später darüber sprechen, was eine Aktivität ist, aber kurz gesagt, es ist eine "Komponente, die einen einzelnen Bildschirm darstellt". Eine App kann mehrere Bildschirme oder Aktivitäten haben und jede Aktivität kann explizit beendet werden (Activity # finish ()). Daher ist es möglich, die Anwendung effektiv zu schließen, indem alle Aktivitäten beendet werden.

Was ist Aktivität oder Service?

Ich denke, es gibt einige Leute, die sich auch darüber wundern. Ich denke, dass es viele Leute gibt, die sich den Service anhand des Namens vorstellen können, aber die Aktivität fühlt sich wie "Hmm?" An.

Aktivität ist, wie ich oben geschrieben habe, eine Komponente, die einen Bildschirm darstellt. Es behält den gesamten Bildschirmstatus bei und empfängt und verarbeitet Ereignisse, die auf diesem Bildschirm auftreten. Der App-Entwickler erstellt eine abgeleitete Klasse von "android.app.Activity", erstellt eine Instanz dieser Klasse für Statusvariablen und implementiert einen Ereignishandler, um den gewünschten Bildschirm zu erreichen.

Service ist eine bildschirmlose Komponente, die hauptsächlich für die bildschirmunabhängige Hintergrundverarbeitung verwendet wird. Der App-Entwickler erstellt eine abgeleitete Klasse von "android.app.Service", lässt eine Instanz dieser Klasse die Statusvariable enthalten und implementiert einen Ereignishandler, um die gewünschte Hintergrundverarbeitung zu erreichen.

Eine App kann mehrere Aktivitäten und mehrere Dienste haben. Sie können eine App nur für Aktivitäten, eine App nur für Dienste oder natürlich eine App erstellen, die sowohl Aktivität als auch Service bietet.

Sie können aus der Aktivität eine weitere Aktivität erstellen, um einen "Bildschirmübergang" zu realisieren. Sie können einen Service auch aus einer Aktivität heraus erstellen, um eine Reihe von Prozessen im Hintergrund auszuführen. Sie können auch einen anderen Service innerhalb des Service erstellen, um verschiedene Prozesse parallel zu verarbeiten. Sie können auch eine Aktivität innerhalb des Dienstes erstellen, um den Bildschirm anzuzeigen (mit Einschränkungen von Android 10).

Hmm. Ich verstehe, dass Aktivität an den Bildschirm gebunden ist. Aber brauchen Sie Service? Warum nicht einen Thread erstellen und im Hintergrund verarbeiten?

Ja, Sie können Threads normal erstellen und sie für die parallele Verarbeitung im Hintergrund verwenden. Von der App erstellte Threads werden jedoch nicht vom Betriebssystem (Android Framework) verwaltet. Eine App, die alle Aktivitäten und Dienste abgeschlossen hat, muss mit größerer Wahrscheinlichkeit ihren Prozess vom Betriebssystem beenden. In diesem Fall ** endet der Thread problemlos, auch wenn er verbleibt. ** ** **

Daher werden normalerweise Threads verwendet, um eine Verarbeitung durchzuführen, die innerhalb von Aktivität oder Dienst abgeschlossen ist. Beispielsweise sollte ein in einer Aktivität gestarteter Thread zum Zeitpunkt des Ende der Aktivität beendet werden. Wenn dies nicht möglich ist, sollte er so schnell wie möglich beendet werden. Da nicht bekannt ist, wann der Thread nach Beendigung der Aktivität zwangsweise beendet wird, muss er unter Berücksichtigung dessen implementiert werden. [^ thread]

Der Dienst wird dagegen vom Betriebssystem verwaltet. Der Dienst muss (nicht oft) beendet werden, bis er beendet ist. [^ service_finish] Daher wird die Verarbeitung, die nicht von der Aktivität abhängt, und die Verarbeitung, die Sie fortsetzen möchten, auch wenn die Aktivität endet, als Service implementiert.

[^ thread]: Selbst wenn die Aktivität endet, bleibt der Thread weiterhin aktiv, wenn andere Aktivitäten und Dienste in der App verbleiben. Aktivitäten und Dienste sollten jedoch so implementiert werden, dass sie so unabhängig (lose gekoppelt) wie möglich von anderen Aktivitäten und Diensten sind. Sie sollten daher nicht in der Hoffnung implementiert werden, dass andere Aktivitäten / Dienste überleben. ..

[^ service_finish]: Der Dienst (Vordergrunddienst) kann vom Betriebssystem zwangsweise beendet werden, wenn dem gesamten System der Speicher ausgeht. Dies ist keine Systemstörung, und Android wurde ursprünglich so entwickelt. Daher ist es bei der Implementierung des Dienstes erforderlich, ihn unter der Annahme zu implementieren, dass er vom Betriebssystem zwangsweise beendet werden kann.

Bedeutet das, dass "Anwendung" Aktivität "oder" Dienst "besitzt?

Wenn Sie sich die Geschichte bisher anhören, denken einige Leute wie folgt.

Eine Instanz der Klasse "Application" (oder ihrer abgeleiteten Klasse) enthält und besitzt eine Instanz von "Activity" und "Service"!

Würde es so aussehen, wenn es in Code geschrieben ist?

Das funktioniert nicht


class MainApplication : Application() {
    private var mainActivity: MainActivity? = null

    override fun onCreate() {
        super.onCreate()

        mainActivity = MainActivity()
        startActivity(mainActivity)
    }
}

Ist es aus der Sicht von jemandem, der eine andere Plattform als Android entwickelt hat, nicht "wahrscheinlich genug Code"? Unter Android ist dies jedoch nicht zulässig. Die Instanziierung von "Aktivität" und "Dienst" ist Aufgabe des Betriebssystems, und die App sollte dies nicht alleine tun.

Wie nennt man Aktivität oder Service?

** Absicht verwenden. ** ** **

Ich denke, die Existenz von Intent ist einer der Punkte, über die Menschen, die zum ersten Mal Android-Apps entwickeln, stolpern. Es ist ein Konzept, das Sie auf anderen Plattformen nicht oft sehen. Wenn Sie beispielsweise eine App mit zwei Bildschirmen haben, "Aktivität1" und "Aktivität2", und Sie den Bildschirm von "Aktivität1" auf "Aktivität2" umstellen möchten, schreiben Sie den folgenden Code.

class Activity1 : AppCompatActivity() {
    fun callActivity2() {
        val intent = Intent(this, Activity2::class.java)
        intent.putExtra("param1", "some data")
        startActivity(intent)
    }
}

Dieser Code (callActivity2 () Methode) führt Folgendes aus:

  1. Erstellen Sie eine Absicht, die die Informationen der aufgerufenen Klasse enthält (Activity2).
  2. Stellen Sie die Absicht auf den Parameter ein, den Sie an den Angerufenen übergeben möchten.
  3. Senden Sie die Absicht an das Betriebssystem.

Das Betriebssystem führt dann folgende Schritte aus:

  1. Wählen Sie aus den gesendeten Intent-Informationen die anzurufende Klasse aus. Dieses Mal wird, da die Klasse explizit angegeben ist, die Klasse "Activity2" ausgewählt.
  2. Instanziieren Sie diese Klasse.
  3. Übergeben Sie die vom Anrufer gesendete Absicht an die instanziierte Aktivität.
  4. Zeigen Sie die instanziierte Aktivität im Vordergrund an.

Dadurch wird der Bildschirmübergang realisiert. Die aufgerufene Aktivität kann die Absicht abrufen, die von der Methode "Aktivität # getIntent ()" empfangen wird, sodass es möglich ist, die Parameter von dieser Absicht abzurufen.

        val param1 = getIntent().getStringExtra("param1")

Gleiches gilt für den Anruf beim Service. Um beispielsweise "Service1" von "Activity1" aus aufzurufen, benötigen Sie den folgenden Code.

    fun callService1() {
        val intent = Intent(this, Service1::class.java)
        intent.putExtra("param1", "some data")
        startService(intent)
    }

Wie Sie sehen können, ist es fast dasselbe wie in Aktivität. [^ launch_service]

[^ launch_service]: In diesem Beispiel wird die Methode "startService ()" verwendet, aber der mit dieser Methode gestartete Dienst kann unter Android 8.0 oder höher nicht lange verarbeitet werden. Wenn der gestartete Dienst lange am Leben bleiben soll, müssen Sie ihn als Vordergrunddienst mit der Methode startForegroundService () starten.

Im obigen Beispiel wurde die Klasse der aufgerufenen Aktivität oder des aufgerufenen Dienstes explizit angegeben. Sie können jedoch auch die Kategorie usw. angeben, anstatt sie explizit anzugeben. Sie können auch Aktivitäten und Dienste aufrufen, die in anderen Apps implementiert sind. Intent ist leicht als Medium für den Informationsaustausch zwischen Aktivitäten / Diensten zu verstehen.

** Wichtig hierbei ist, dass das Konzept der Absicht als Polster verwendet wird, anstatt direkt anzurufen, unabhängig davon, ob es sich um Aktivität oder Service handelt. ** ** **

Dieser Mechanismus ermöglicht es dem Anrufer nicht, eine Instanz der aufrufenden Aktivität oder des aufrufenden Dienstes zu empfangen. Der angerufene Teilnehmer kann die aufrufende Instanz auch nicht empfangen. Daher können sie nicht auf die Instanzmethoden und -variablen des anderen zugreifen. Wenn eine Zusammenarbeit zwischen Aktivität / Dienst erforderlich ist, legen Sie die Parameter in Absicht fest und übergeben Sie sie wie im obigen Beispiel, oder verwenden Sie einen anderen Mechanismus (startActivityForResult () Methode, um Ergebnisinformationen zurückzugeben, ` Sie verwenden (z. B. Austausch über eine abgeleitete Anwendungsklasse, Verwendung von Broadcast usw.).

Darüber hinaus sind die Parameter, die im Intent festgelegt werden können (die Daten, die über den Intent ausgetauscht werden können), Java-Grundtypen, Zeichenfolgentypen und deren Array-Typen. Daten anderer Typen müssen in Reihe übergeben werden. Dies bedeutet, dass Sie nicht einfach eine Instanz des Objekts übergeben können.

Dank dessen ist jede Aktivität / jeder Dienst auf hohem Niveau lose miteinander verbunden.

Es scheint verschiedene Gründe für diese Architektur zu geben, aber ich denke, der Hauptgrund ist, dass Android ursprünglich auf Hardware mit wenig Speicher abzielte. Es ist notwendig, den Speicher im Terminal mit einem kleinen Speicher effizient freizugeben. Wenn jede Aktivität lose gekoppelt ist (ohne direkten Bezug zueinander), können Sie jede Aktivität, die in den Hintergrund getreten ist, jederzeit verwerfen und Speicher freigeben (mit Ausnahme natürlich). Auf diese Weise können Sie effizient Speicher freigeben, ohne die App selbst löschen zu müssen. Ich denke, das ist ein großer Vorteil dieser Architektur. Die meisten modernen Hardwarekomponenten mit übermäßigem Speicher genießen diesen Vorteil möglicherweise nicht sehr oft. .. ..

Was bedeutet "Starten einer App" überhaupt?

Ich verstehe. Die Sache ist, eine abgeleitete Klasse der Application-Klasse zu erstellen, eine Absicht zu erstellen, die die Aktivität des Bildschirms angibt, der zuerst in der onCreate () -Methode angezeigt werden soll, und dostartActivity ()! ??

Ich denke, es ist natürlich, vom bisherigen Ablauf der Geschichte so zu denken, aber ** ist es nicht. ** ** ** Wie bereits erwähnt, ist es selten, eine abgeleitete Klasse für "Anwendung" zu erstellen. Sie können die Aktivität auf dem ersten Bildschirm starten, ohne dies zu tun. Geben Sie dazu in "AndroidManifest.xml" die Aktivität an, die Sie zuerst starten möchten.

AndroidManifest.xml


        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

Diese Beschreibung entspricht der Erstellung, als das Projekt aus der Vorlage erstellt wurde. Diese Beschreibung definiert die Einstellungen für die Klasse "MainActivity", aber das Wichtigste ist "" im Element "". Es ist eine Beschreibung. Diese Beschreibung gibt an, dass "MainActivity" die erste Aktivität ist, die gestartet wird. (Übrigens gibt die Beschreibung "" unten an, dass "MainActivity" eine Aktivität ist, die vom Launcher aus gestartet werden kann.)

Wenn die App gestartet wird, erstellt das Betriebssystem eine Absicht, die diese Anweisung "abfängt" und "startActivity ()" ausführt. Infolgedessen wird die Klasse "MainActivity" als zu startende Aktivität identifiziert und "MainActivity" instanziiert und angezeigt. Dies ist, was das Betriebssystem tut, so dass Sie es nicht auf der App-Seite codieren müssen.

Hmm? startActivity () ist nur eine "start Activity" -Methode, nicht wahr? Ist "Starten einer App" nicht eine andere Sache?

Das ist etwas verwirrend. Bei der Entwicklung von Android-Apps achten wir nicht besonders auf das "Starten von Apps". Beachten Sie stattdessen, dass Sie Aktivitäten und Dienste in Ihrer App starten.

Angenommen, Sie möchten eine andere App über Ihre eigene App starten. Schreiben Sie den folgenden Code, um andere Apps zu starten.

        val intent = packageManager.getLaunchIntentForPackage("test.app.other_app")
        startActivity(intent)

PackageManager # getLaunchIntentForPackage () gibt eine Absicht zurück, die die erste Aktivität angibt, die für die im Argument angegebene App gestartet werden soll. Wenn Sie diese Absicht an "startActivity ()" übergeben, wird die erste Aktivität der Ziel-App gestartet. Mit anderen Worten, es wird als "die erste Aktivität wird gestartet" und "die App wird gestartet" angesehen.

Ja! ?? Aber ist die App noch nicht in Bearbeitung? Ist es in Ordnung, eine Absicht an Apps zu senden, die den Prozess noch nicht gestartet haben? ??

Korrekt. Wenn Sie eine Absicht senden, müssen Sie sich keine Gedanken darüber machen, ob die Ziel-App ausgeführt wird. Dies liegt daran, dass das Betriebssystem den Prozess ** "Wenn der Prozess der Zielanwendung nicht gestartet wird, starten Sie ihn" ausführt. ** Mit anderen Worten, wenn der Prozess der Zielanwendung nicht gestartet wird, führt startActivity () für die Aktivität dieser Anwendung die folgende Verarbeitung aus.

  1. Der App-Prozess wird gestartet.
  2. Die Klasse "Application" (oder ihre abgeleitete Klasse) wird instanziiert und "Application # onCreate ()" wird aufgerufen.
  3. Die von Intent angegebene Aktivitätsklasse wird instanziiert und "Activity # onCreate ()" wird aufgerufen und im Vordergrund angezeigt.

Dies ist ein üblicher "App-Start" unter Android.

Darüber hinaus ist es selbstverständlich, eine bestimmte Aktivität einer anderen App direkt zu starten. Es ist auch möglich, Parameter zu übergeben.

        val intent = Intent().apply {
            setClassName("test.app.other_app", "test.app.other_app.Activity2")
            flags = Intent.FLAG_ACTIVITY_NEW_TASK
            putExtra("param1", "hoge")
        }
        startActivity(intent)

Wenn Sie dies tun, werden Sie zu den Bildschirmen anderer Apps weitergeleitet, als ob die Bildschirmübergänge in Ihrer App normal stattgefunden hätten. Möglicherweise weiß der Benutzer nicht, dass die App gewechselt hat.

Ebenso ist es möglich, einen bestimmten Dienst einer anderen App direkt zu starten.

Wie Sie sehen können, sind die Grenzen zwischen Apps unter Android sehr dünn. Sie können Bildschirmübergänge unabhängig von Ihrer eigenen Anwendung oder anderen Anwendungen vornehmen. Sie können die Funktion unabhängig von Ihrer eigenen Anwendung oder anderen Anwendungen aufrufen. Ich denke, dass es möglich ist, Apps auf anderen Plattformen so wie sie sind zu verknüpfen, aber ist Android nicht die einzige, die so flexibel sein kann? Ich denke, das macht Android so interessant und macht süchtig.

Was bedeutet es, über eine abgeleitete Klasse von "Anwendung" zu kommunizieren?

Im vorherigen Abschnitt habe ich "Austausch über eine abgeleitete Klasse von" Anwendung "" als eine der Möglichkeiten zum Datenaustausch zwischen Aktivitäten und Diensten erwähnt. Was bedeutet das??

In der vorherigen Erklärung habe ich geschrieben, dass "Anwendung" keine "Aktivität" oder "Dienst" "besitzt". "Anwendung", "Aktivität" und "Dienst" haben jedoch die folgende Beziehung.

Daher sollten die Daten und Objekte, die Sie in der gesamten App freigeben möchten, in der Instanz der abgeleiteten Klasse "Anwendung" gespeichert werden.

MainApplication.kt


class MainApplication : Application() {
    val sharedHoge = Hoge()
}

Activity1.kt


class Activity1 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_1)
        
        (application as MainApplication).sharedHoge.fuga()
    }
}

Service1.kt


class Service1 : Service() {
    override fun onCreate() {
        super.onCreate()

        (application as MainApplication).sharedHoge.fuga()
    }
}

Es versteht sich jedoch von selbst, dass diese Technik nur zum Teilen von Daten zwischen Aktivitäten und Diensten innerhalb der App verwendet werden kann. Wenn Sie Daten mit anderen Apps teilen möchten, können Sie dem Intent entweder einen Parameter wie oben beschrieben geben oder ihn als Inhaltsanbieter bezeichnen (https://developer.android.com/guide/topics/providers/content-providers). Sie werden den Mechanismus verwenden.

Sollten wir nicht einfach statische Variablen ohne solchen Aufwand verwenden?

** Benutze es nicht. ** ** **

Auf statische Java-Mitglieder und Kotlin-Objekte kann zugegriffen werden, ohne eine Instanz der Klasse anzugeben. Ich bin sicher, dass einige Leute versuchen werden, damit Daten gemeinsam zu nutzen. Zum Beispiel der folgende Code.

SharedData.kt


object SharedData {
    var hoge: String = ""
}

Activity1.kt


class Activity1 : AppCompatActivity() {
    fun onClickFugaButton() {
        SharedData.hoge = "fuga"
    }
}

Activity2.kt


class Activity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_2)
        findViewById<TextView>(R.id.textViewHoge).text = SharedData.hoge
    }
}

In diesem Beispiel versuchen wir, Daten mithilfe von SharedData.hoge wie eine globale Variable gemeinsam zu nutzen. Dies ist auf anderen Plattformen in Ordnung, aber Android erlaubt es nicht.

Was? Aber es gibt keinen Kompilierungsfehler und es funktioniert normal?

Ja, es gibt keine Fehler. Manchmal funktioniert es wie erwartet. Aber manchmal funktioniert es nicht wie erwartet. Es funktioniert normal oder funktioniert nicht, so dass es leicht zu mehr Verwirrung kommt.

Im Gegensatz zum typischen Java-Garbage-Collector entlädt der Android-Garbage-Collector Informationen aus dem Speicher sowie aus der Instanz selbst und der Klasse selbst. Und die Daten, die die statische Variable beim Entladen der Klasse enthält, werden ebenfalls zerstört. Der obige Code bewirkt, dass "SharedData.hoge" die Zeichenfolgendaten enthält, aber der Garbage Collector kann das "SharedData" -Objekt (Klasse) entladen. Dann werden auch die gespeicherten Zeichenkettendaten zerstört. Wenn Sie dann versuchen, "SharedData.hoge" zu lesen, wird das "SharedData" -Objekt (Klasse) geladen und erneut initialisiert, und leere Zeichen werden zurückgegeben.

Es kann der größte Punkt sein, an dem Leute stolpern, die von anderen Plattformen zur Android-Entwicklung gewechselt sind. Aus diesen Gründen sollten statische Java-Variablen und Kotlin-Objekte bei der Entwicklung von Android-Apps nur auf folgende Weise verwendet werden.

Auf diese Weise sollte die Verwendung statischer Variablen und Objekte für den Datenaustausch (Datenübertragung) zwischen Aktivitäten und Diensten grundsätzlich vermieden werden. Wie bisher erläutert, sollte die an "startActivity ()" oder "startService ()" übergebene Absicht einen Parameter haben, die abgeleitete Klasse von "Application" sollte gemeinsame Daten haben oder die Daten sollten in "SharedPreferences" gespeichert sein. Sie können es für andere freigeben oder als normale Datei speichern.

Was ist, wenn der Dienst beispielsweise ein Ereignis in Aktivität auslösen soll?

Bisher haben wir erklärt, wie eine Aktivität oder ein Dienst gestartet wird. Als kurze Zusammenfassung wissen Sie, dass Sie eine Absicht erstellen und an die Methoden startActivity () und startService () übergeben können.

Bei der Verknüpfung zwischen Aktivitäten und Diensten ist es jedoch möglicherweise nicht möglich, den Zweck einfach durch "Starten der Zielaktivität / des Zieldienstes" zu erreichen. Betrachten Sie beispielsweise das Starten eines Dienstes über eine Aktivität und teilen Sie der Aktivität mit, dass der Dienst die Hintergrundverarbeitung abgeschlossen hat und "abgeschlossen" ist. Die Aktivität kann den Dienst mit startService () (oder startForegroundService ()) starten, wie wir gesehen haben. Wie kann der Dienst die Aktivität darüber informieren, dass sie ausgeführt wurde?

In diesem Fall ist es nicht immer angebracht, "startActivity ()" zu verwenden, da die Aktivität bereits gestartet wurde. In solchen Fällen wird die Methode "sendBroadcast ()" verwendet. Diese Methode dient wie "startActivity ()" und "startService ()" zum Senden von Absichten, jedoch an einen BroadcastReceiver anstelle einer Aktivität oder eines Dienstes. BroadcastReceiver kann in Aktivität oder Dienst generiert und registriert werden, sodass er zum Empfangen von Absichten verwendet wird.

MainActivity.kt


class MainActivity : AppCompatActivity() {
    private val myBroadcastReceiver = object: BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            findViewById<TextView>(R.id.textView1).text = "Fertige Bearbeitung"
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //Registrieren Sie BroadcastReceiver
        LocalBroadcastManager.getInstance(this).registerReceiver(
            myBroadcastReceiver,
            IntentFilter("test.app.actions.SERVICE_FINISHED")
        )
        
        //Servicestart
        startService(Intent(this, Service3::class.java))
    }

    override fun onDestroy() {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadcastReceiver)
        super.onDestroy()
    }
}

Service3.kt


class Service3: IntentService("Service3") {
    override fun onHandleIntent(intent: Intent?) {
        //Verarbeite verschiedene Dinge
        Thread.sleep(1000)

        //Senden Sie eine Sendung
        LocalBroadcastManager.getInstance(this).sendBroadcast(
            Intent("test.app.actions.SERVICE_FINISHED")
        )
    }
}

Ich habe hier "LocalBroadcastManager # sendBroadcast ()" verwendet, weil das Ziel der Sendung eine In-App-Aktivität ist, aber ich verwende "Context # sendBroadcast ()", um sie an eine andere App zu senden ("Context" ist "Activity"). Und "Service" sind übliche Elternklassen. [^ Broadcast1] [^ Broadcast2]

[^ Broadcast1]: Um die Wahrheit zu sagen, müssen Sie BroadcastReceiver nicht unbedingt verwenden, wenn Sie ein Ereignis von Service to Activity in Ihrer App senden möchten. Dies liegt daran, dass es möglich ist, eine Aktivitätsinstanzmethode vom Dienst über eine abgeleitete Klasse von "Anwendung" aufzurufen. Selbst wenn Sie dies tun, sollten Sie Ihre Aktivität und Ihren Service sorgfältig so gestalten, dass sie so locker wie möglich gekoppelt sind.

[^ Broadcast2]: BroadcastReceiver kann auch Systemereignisse empfangen, die vom Betriebssystem ausgelöst werden. Beispielsweise ist es möglich, eine Verarbeitung unmittelbar nach dem Starten des Terminals auszuführen. Wenn jedoch weder Aktivität noch Dienst vorhanden sind, sollten Sie der Meinung sein, dass es unvermeidlich ist, dass der Prozess beendet wird, wenn die Verarbeitung von "BroadcastReceiver # onReceive ()" beendet wird. Wenn Sie also längere Zeit verarbeiten möchten, " Sie sollten den Dienst in BroadcastReceiver # onReceive () `starten und zeitaufwändige Vorgänge innerhalb dieses Dienstes ausführen.

Zusammenfassung

Es ist lange her, also habe ich es viel gefaltet, aber wenn ich etwas nicht verstehe, werde ich ihn bitten, es zu googeln.

Die Android-Architektur ist ziemlich einzigartig, nicht wahr? Zuerst konnte ich verstehen, dass es für Lebenszyklusereignisse notwendig war, aber sobald ich versuchte, Aktivität / Service zu verknüpfen, wurde es "???". Ich hoffe, es hilft Menschen, die am selben Ort stolpern.

Recommended Posts

Wichtige Punkte bei der Entwicklung von Android-Apps, an denen altmodische Programmierer gestolpert sind
Startschuss für die persönliche Entwicklung der Android-App
ROS App Entwicklung auf Android
Punkte, auf die ich beim Erstellen einer Android-App gestoßen bin [Von Zeit zu Zeit aktualisiert]
Riot (Chat App) Entwicklung (Einstellungen) in Android Studio