Rheinwerk Computing < openbook >

 
Inhaltsverzeichnis
Vorwort
Teil I Grundlagen
1 Android – eine offene, mobile Plattform
2 Hallo Android!
3 Von der Idee zur Veröffentlichung
Teil II Elementare Anwendungsbausteine
4 Wichtige Grundbausteine von Apps
5 Benutzeroberflächen
6 Multitasking
Teil III Gerätefunktionen nutzen
7 Telefonieren und surfen
8 Sensoren, GPS und Bluetooth
Teil IV Dateien und Datenbanken
9 Dateien lesen, schreiben und drucken
10 Datenbanken
Teil V Multimedia und Produktivität
11 Multimedia
12 Kontakte und Organizer
A Einführung in Kotlin
B Jetpack Compose
C Häufig benötigte Codebausteine
D Literaturverzeichnis
E Die Begleitmaterialien
Stichwortverzeichnis

Ihre Meinung?
Spacer
<< zurück
Android 11 von Thomas Künneth
Das Praxisbuch für App-Entwickler
Buch: Android 11

Android 11
Pfeil 4 Wichtige Grundbausteine von Apps
Pfeil 4.1 Was sind Activities?
Pfeil 4.1.1 Struktur von Apps
Pfeil 4.1.2 Lebenszyklus von Activities
Pfeil 4.2 Kommunikation zwischen Anwendungsbausteinen
Pfeil 4.2.1 Intents
Pfeil 4.2.2 Kommunikation zwischen Activities
Pfeil 4.2.3 Broadcast Receiver
Pfeil 4.3 Fragmente
Pfeil 4.3.1 Grundlagen
Pfeil 4.3.2 Ein Fragment in eine Activity einbetten
Pfeil 4.3.3 Mehrspaltenlayouts
Pfeil 4.4 Berechtigungen
Pfeil 4.4.1 Normale und gefährliche Berechtigungen
Pfeil 4.4.2 Tipps und Tricks zu Berechtigungen
Pfeil 4.5 Navigation
Pfeil 4.5.1 Jetpack Navigation
Pfeil 4.5.2 Die Klasse »BottomNavigationView«
Pfeil 4.6 Zusammenfassung
 
Zum Seitenanfang

4.4    Berechtigungen Zur vorigen ÜberschriftZur nächsten Überschrift

Android-Apps werden immer in einer Sandbox ausgeführt. Innerhalb dieses geschützten Bereichs können sie sich frei entfalten. Möchte eine App aber andere Systemkomponenten nutzen oder auf fremde Anwendungen, deren Bausteine und Daten zugreifen, muss sie hierfür eine entsprechende Berechtigung besitzen.

 
Zum Seitenanfang

4.4.1    Normale und gefährliche Berechtigungen Zur vorigen ÜberschriftZur nächsten Überschrift

Android unterscheidet zwischen normalen und gefährlichen Berechtigungen. Erstere finden Verwendung, wenn eine App zwar auf Daten oder Ressourcen außerhalb der eigenen Sandbox zugreift, dies aber keinen nennenswerten Einfluss auf die Privatsphäre des Benutzers oder die Integrität anderer Apps hat. Beispiele für normale Berechtigungen sind etwa Zugriff auf das Internet, Setzen der Zeitzone, Setzen eines Wallpapers sowie Benachrichtigung über den Abschluss des Boot-Vorgangs (Abschnitt 4.2.3, »Broadcast Receiver«).

Fordert eine App eine normale Berechtigung an, gewährt das System diese automatisch. Gefährliche Berechtigungen hingegen müssen vom Anwender ausdrücklich gewährt oder verweigert werden. Vorher ist die Nutzung der entsprechenden Funktion nicht möglich. Beispiele für gefährliche Berechtigungen sind der lesende oder schreibende Zugriff auf Kontakt- und Kalenderdaten, die Nutzung von Kamera oder Mikrofon, das Versenden und Empfangen von SMS sowie in vielen Fällen das Laden und Speichern von Dateien.

Berechtigungen deklarieren

Apps müssen in der Manifestdatei sowohl normale als auch gefährliche Berechtigungen anfordern. Wie ein entsprechender Eintrag aussieht, zeigt Listing 4.25. Es gehört zu dem Beispielprojekt PermissionDemo. Jede Berechtigung erhält ihr eigenes <uses-permission ... />-Tag. Achten Sie bitte darauf, sie an der richtigen Stelle einzufügen, als Kinder von <manifest ... />.

[»]  Hinweis

Auch Activities und Services können Berechtigungen definieren. Diese haben aber eine andere Bedeutung und funktionieren anders. Hinweise hierzu finden Sie in Abschnitt 6.2, »Services«.

Bis einschließlich Android 5 wurden Berechtigungen während der Installation abgefragt. Der Benutzer musste entscheiden, ob er sie in Gänze akzeptieren wollte, und falls nicht, so wurde die App nicht installiert. Ein selektives Zustimmen oder Ablehnen war ebenso wenig möglich wie das nachträgliche Entziehen oder Gewähren einer Berechtigung.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.thomaskuenneth.androidbuch.permissiondemo">
<uses-permission
android:name="android.permission.READ_PHONE_NUMBERS" />
<application
...
<activity android:name=".MainActivity">
..
</activity>
</application>
</manifest>

Listing 4.25    Eine »Berechtigung« anfordern

Dies hat sich 2015 mit der Einführung von Marshmallow glücklicherweise geändert. Die sogenannten Runtime Permissions geben Anwendern viel Kontrolle darüber, was eine App darf. Ein entsprechendes Programmiermodell sorgt dafür, dass Berechtigungsabfragen erst zur Laufzeit erfolgen. Apps müssen hierfür als targetSdkVersion in der Datei build.gradle des Moduls app mindestens 23 eintragen. Andernfalls werden die Rechte weiterhin während der Installation abgefragt. Bitte denken Sie aber daran, dass Google bei Uploads in den Play Store die targetSdkVersion prüft. Apps mit zu niedrigem API-Level können Sie weder neu einstellen noch aktualisieren. Weitere Infos hierzu finden Sie in Abschnitt 3.3.2, »Apps in Google Play einstellen«. Das nachträgliche Entziehen oder Gewähren von Rechten über die Einstellungen funktioniert ab Android 6 übrigens unabhängig von der targetSdkVersion. Ein sorgfältiges Behandeln von Rückgabewerten und Ausnahmen wäre also auch bei niedrigerer targetSdkVersion Pflicht.

Berechtigungen prüfen und anfordern

Runtime Permissions bedeuten für Entwickler etwas zusätzliche Arbeit. Bitte sehen Sie sich hierzu Listing 4.26 an. Es demonstriert den Umgang mit ihnen, indem es in der Methode getLine1Number() die Telefonnummer eines Geräts ermittelt. Sie wird nur aufgerufen, wenn die App die nötige Berechtigung hat. Trotzdem ist die Behandlung der sonst geworfenen SecurityException nötig, weil sonst die in Android Studio eingebaute statische Codeanalyse (zu Recht) meckert.

In onCreate() wird die Benutzeroberfläche wie üblich mit setContentView() geladen und angezeigt sowie für eine Schaltfläche ein OnClickListener registriert. Dieser ruft die private Methode requestPermission() auf. Spannendes geschieht in onStart(): Als Erstes wird mit checkSelfPermission() geprüft, ob die App aktuell Telefonnummern auslesen darf. Ist dies der Fall, ruft mein Beispiel die Methode outputLine1Number() auf, in der die primäre Telefonnummer ermittelt und ausgegeben wird. Hat die App hingegen keine Berechtigung, diese Nummer auszulesen, fordert sie diese durch Aufrufen meiner privaten Methode requestPermission() an. Unter Umständen tut sie das aber nicht sofort, denn Google sieht vor, dass eine App den Benutzer über den Grund, weshalb sie ein bestimmtes Recht haben möchte, informieren soll, wenn er ihr die Berechtigung schon einmal verweigert hat.

package com.thomaskuenneth.androidbuch.permissiondemo

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.telephony.TelephonyManager
import android.util.Log
import android.view.View
import android.widget.*
import androidx.appcompat.app.AppCompatActivity

private const val REQUEST_READ_PHONE_NUMBER = 123
private val TAG = PermissionDemoActivity::class.simpleName
class PermissionDemoActivity : AppCompatActivity() {

private lateinit var tv: TextView
private lateinit var bt: Button

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tv = findViewById(R.id.tv)
bt = findViewById(R.id.bt)
bt.setOnClickListener { requestPermission() }
}

override fun onStart() {
super.onStart()
bt.visibility = View.GONE
if (checkSelfPermission(Manifest.permission.READ_PHONE_NUMBERS)
!= PackageManager.PERMISSION_GRANTED) {
if (shouldShowRequestPermissionRationale(
Manifest.permission.READ_PHONE_NUMBERS)) {
tv.setText(R.string.explain1)
bt.visibility = View.VISIBLE
} else {
requestPermission()
}
} else {
outputLine1Number()
}
}

override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<String>,
grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions,
grantResults)
if (requestCode == REQUEST_READ_PHONE_NUMBER) {
bt.visibility = View.GONE
if (grantResults.isNotEmpty() && grantResults[0]
== PackageManager.PERMISSION_GRANTED) {
outputLine1Number()
} else {
tv.setText(R.string.explain2)
}
}
}

private fun requestPermission() {
requestPermissions(arrayOf(
Manifest.permission.READ_PHONE_NUMBERS),
REQUEST_READ_PHONE_NUMBER)
}

private fun outputLine1Number() {
tv.text = getString(R.string.template,
getLine1Number())
}

private fun getLine1Number(): String {
var result = "???"
getSystemService(TelephonyManager::class.java)?.run {
try {
result = line1Number
} catch (ex: SecurityException) {
Log.e(TAG, "getLine1Number()", ex)
}
}
return result
}
}

Listing 4.26    Die Klasse »PermissionDemoActivity«

Hierfür ist die Methode shouldShowRequestPermissionRationale() zuständig. Liefert sie true, blendet PermissionDemo die Schaltfläche Verstanden ein und zeigt den Hinweis »Bitte gewähren Sie der App die Berechtigung« an. Abbildung 4.13 zeigt die App, nachdem die Schaltfläche Verstanden angeklickt wurde (im Hintergrund sind Button und Text zu sehen). Nach dem ersten Ablehnen einer Berechtigungsanfrage wird hingegen nur »Die Berechtigung wurde verweigert« ausgegeben.

Die App nach dem Anklicken von »Verstanden«

Abbildung 4.13    Die App nach dem Anklicken von »Verstanden«

Wie sich eine App verhält, wenn der Anwender eine Berechtigung verweigert, ist davon abhängig, wie zentral das Recht für ihr Funktionieren ist. Berührt es nur eine Funktion, müssen Sie nur diese temporär stilllegen. Ist hingegen ohne die Berechtigung der Betrieb der App nicht möglich, sollte die Info an den Benutzer wie in PermissionDemo entsprechend deutlich ausfallen.

Auf Rechtevergaben reagieren

Eine Methode meines Beispiels habe ich bislang noch nicht besprochen, nämlich onRequestPermissionsResult(). Sie wird von Android aufgerufen, nachdem meine private Methode requestPermission() folgende Anweisung ausgeführt hat:

requestPermissions(arrayOf(
Manifest.permission.READ_PHONE_NUMBERS),
REQUEST_READ_PHONE_NUMBER)

Hierbei handelt es sich um das Pendant zum <uses-permission />-Tag in der Manifestdatei. requestPermissions() muss vor dem Auslesen der Telefonnummer aufgerufen werden, wenn checkSelfPermission() ergeben hat, dass die App die Berechtigung aktuell nicht hat. Bis einschließlich Android 10 bot der Berechtigungsdialog übrigens ein Verweigern und nicht mehr fragen. Android 11 sieht das wiederholte Anfordern von denselben Berechtigungen als schlechten Stil an und verwehrt diese nach dem zweiten Versuch.

Sicher ist Ihnen aufgefallen, dass der Abfragedialog vage von »Telefonanrufe tätigen und verwalten« schreibt, obwohl die App doch ganz genau eine Berechtigung, android.permission.READ_PHONE_NUMBERS, anfordert. Android fasst Berechtigungen zu sogenannten Berechtigungsgruppen zusammen. Möchte eine App eine bestimmte Berechtigung erhalten, zeigt das System die Meldung, die zu derjenigen Gruppe passt, der eine Berechtigung zugeordnet ist. Das bedeutet, dass kein Meldungsdialog mehr erscheint, wenn eine andere Berechtigung angefordert wird, die zu derselben Gruppe gehört. Bitte denken Sie auch daran, dass normale Berechtigungen grundsätzlich ohne Benutzerinteraktion gewährt werden.

 
Zum Seitenanfang

4.4.2    Tipps und Tricks zu Berechtigungen Zur vorigen ÜberschriftZur nächsten Überschrift

Die Methode shouldShowRequestPermissionRationale() steuert also, ob eine App die Nutzung einer Berechtigung erklären sollte. Hat der Anwender ein Recht endgültig entzogen, so liefert sie false. Wenn eine Geräterichtlinie einer App verbietet, eine Berechtigung zu erhalten, wird ebenfalls false zurückgegeben.

Für Tests ist es sehr praktisch, Berechtigungen über die Kommandozeile steuern zu können. Das folgende Kommando entzieht PermissionDemo die Berechtigung, die Telefonnummer auszulesen. Sie können das Kommando zum Beispiel im Werkzeugfenster-Terminal eingeben.

adb shell pm revoke com.thomaskuenneth.androidbuch.permissiondemo android.permission.READ_PHONE_NUMBERS

Das Schlüsselwort grant gewährt die Berechtigung.

Anwender erhalten über die in Abbildung 4.14 dargestellte Einstellungsseite App-Info Zugriff auf Berechtigungen.

Die Einstellungsseite »App-Info«

Abbildung 4.14    Die Einstellungsseite »App-Info«

Falls Sie diese Seite aus einer Activity Ihrer App heraus anzeigen möchten, sind nur wenige Zeilen Code nötig:

val intent = android.content.Intent()
intent.action = android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = android.net.Uri.fromParts("package", packageName, null)
intent.data = uri
startActivity(intent)

Listing 4.27    Einstellungsseite »App-Info« aufrufen

Ein Klick auf Berechtigungen öffnet die Seite mit den Berechtigungen, die von einer App angefordert werden. In Abbildung 4.15 ist diese Seite zu sehen.

Die Einstellungsseite »App-Berechtigungen«

Abbildung 4.15    Die Einstellungsseite »App-Berechtigungen«

Berechtigungen und der Lebenszyklus von Activities

Vielleicht fragen Sie sich, ob es »den besten Zeitpunkt« für das Prüfen und Anfordern von Berechtigungen gibt. Wie Sie wissen, sollten Sie Berechtigungen nur dann anfordern, wenn sie für das Ausführen einer bestimmten Aktion erforderlich sind. Aber was bedeutet das? Activities durchlaufen einen komplexen Lebenszyklus einschließlich einer ganzen Reihe von Callbacks, also Methoden, die Sie bei Bedarf überschreiben können.

onCreate() wird von praktisch jeder Activity überschrieben. Theoretisch können Sie also dort Ihre Berechtigungsprüfungen machen. Allerdings wird diese Methode nicht immer aufgerufen, sondern nur, wenn die Activity noch nie gestartet oder nach einem früheren Lauf zerstört wurde. Deshalb bietet sich onStart() als Alternative an. Hierbei kann es aber zu einem unerwarteten Effekt kommen. Wenn Sie mit requestPermissions() den systemweiten Berechtigungsdialog öffnen und der Benutzer während dessen Anzeige das Gerät dreht, also einen Orientierungswechsel vornimmt, wird wieder onStart() aufgerufen. Und damit wieder der Berechtigungsdialog. Android 7 und alle folgenden Versionen ignorieren den zweiten, unnötigen requestPermissions()-Aufruf, aber unter Android 6 erscheint der Dialog tatsächlich mehrmals. Mir ist keine Empfehlung von Google bekannt, wie man damit am besten umgehen sollte. Auf der sicheren Seite sind Sie, wenn Sie in Ihrer App speichern, dass gerade die Antwort auf eine Berechtigungsanfrage aussteht. Hierfür bieten sich die Shared Preferences an, die ich Ihnen in Abschnitt 5.2, »Vorgefertigte Bausteine für Oberflächen«, vorstelle. Da das Problem aber eine recht alte Version betrifft und auch eher selten auftreten dürfte, können Sie es wahrscheinlich auch ignorieren.

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
Zur Rheinwerk-Konferenz für Kotlin
 Buchempfehlungen
Zum Rheinwerk-Shop: Kotlin

Kotlin


Zum Rheinwerk-Shop: Praxisbuch Usability und UX

Praxisbuch Usability und UX


Zum Rheinwerk-Shop: Flutter und Dart

Flutter und Dart


Zum Rheinwerk-Shop: App-Design

App-Design


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2023
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern