Une première application

Dans ce chapitre, nous allons créer une premier projet avec Android Studio pour comprendre la structure d’une application Android.

Création d’une application simple

Lancez Android Studio et sur la fenêtre d’accueil, choisissez Start a new Android Studio project.

../_images/android_studio_welcome.png

L’écran d’accueil d’Android Studio

Parmi la liste des modèles de projet, choisissez Empty Activity et cliquez sur le bouton Next.

../_images/android_studio_new_project.png

L’écran de choix du modèle de projet

Puis vous devez préciser les informations relatives à votre projet : son nom, le package par défaut et la localisation du projet sur votre disque dur.

Le package correspond à l’identifiant technique de votre application. Si vous souhaitez déployer plus tard votre application sur Android Store, c’est le package qui permet d’identifier de manière unique votre application et l’organisation à qui elle appartient.

../_images/android_studio_premiere_app_new_project.png

L’écran de configuration du projet

Important

Si vous utilisez Android Studio 3, n’oubliez pas de cocher la case Use AndroidX artifacts sur l’écran de configuration du projet !

AndroidX est une refonte d’une partie de l’API Android. Elle fournit une nouvelle implémentation de classes qui est compatible quelle que soit la version de l’API Android que vous utilisez dans votre projet. Il est recommandé d’utiliser AndroidX car l’ancienne API n’est actuellement plus maintenue.

Cliquez sur le bouton Finish et l’éditeur d’Android Studio s’ouvre une fois les fichiers du projet créés. Le projet est automatiquement construit et il ne doit pas y avoir d’erreur.

../_images/android_studio_premiere_app_ide.png

L’éditeur de projet Android Studio

Organisation du projet

Un projet Android Studio est géré par l’outil de build Gradle qui est un outil dédié aux projets Java. Un projet géré par Gradle possède un ou plusieurs fichiers build.gradle pour configurer sa construction.

Les projets Android sont composés de modules. Les projets les plus simples n’ont besoin que d’un seul module qui contient l’ensemble des fichiers sources. Par défaut, un projet Android contient un seul module nommé app. Dans le répertoire de votre projet, vous pouvez trouver un répertoire app qui contient l’ensemble des fichiers pour ce module.

../_images/premiere_app_structure_projet.png

Structure des fichiers d’un projet Android

Dans le répertoire d’un module, vous trouverez un fichier build.gradle pour configurer la construction du module et les répertoires suivants :

  • src/main/java : ce répertoire contient les sources Java du module

  • src/main/res : ce répertoire contient les ressources du module (Cf. plus bas)

  • src/test/java : ce répertoire contient les sources Java des tests unitaires

  • src/androidTest/java : ce répertoire contient les sources Java des tests instrumentés (c’est-à-dire les tests qui doivent être exécutés directement sur un périphérique Android).

  • libs : ce répertoire contient éventuellement les bibliothèques nécessaires au module

  • build : ce répertoire contient les fichiers de travail créés par Gradle.

On trouve également le fichier src/main/AndroidManifest.xml qui correspond au manifeste du module. Un fichier manifeste fournit au système l’ensemble des informations nécessaires à l’exécution du module.

Par défaut, Android Studio affiche l’explorateur de fichiers à gauche de l’éditeur. Cet explorateur est par défaut en mode Android, c’est-à-dire qu’il vous affiche une vue adaptée. Il ne vous affiche donc pas l’organisation réelle des fichiers sur votre disque dur. Vous pouvez choisir le mode Project pour voir l’organisation réelle mais le mode Android est généralement plus lisible et plus adapté pour réaliser les développements.

../_images/premiere_app_structure_android_studio.png

La structure des fichiers présentée par Android Studio

Dans ce mode, vous avez le nom du module à la racine et vous trouvez directement les répertoires :

  • manifests : qui contient le manifeste du module

  • java : qui présente toutes les sources Java en séparant les packages de l’application, des packages de tests (unitaires et instrumentés).

  • res : qui présente les répertoires de ressources (Cf. plus bas).

  • Gradle Scripts: ce répertoire regroupe tous les fichiers Gradle de l’application et des modules.

Lancement de l’application

Depuis Android Studio, vous pouvez lancer une application sur votre téléphone Android. Il vous suffit de brancher votre appareil en USB sur votre station de travail et de choisir le menu Run > Run “app”. Android Studio utilise l’outils adb (Android Debug Bridge) pour installer l’application créée sur votre appareil et l’exécuter. Une fois installée, l’application est accessible depuis le menu des applications Android sur votre appareil.

Pour pouvoir utiliser le mode debug depuis Android Studio, vous devez activer le mode developer sur votre appareil. Pour plus d’information, consultez le guide utilisateur :

Note

L’application générée par Gradle porte l’extension .apk et se trouve dans le répertoire app/build/output/debug (pour le cas où le module principal de l’application porte le nom par défaut app).

L’émulateur Android

Si vous ne disposez pas d’un appareil Android ou si vous souhaitez tester votre application dans des conditions particulières, vous pouvez utiliser l’émulateur Android. Il vous permet de choisir le type d’appareil que vous souhaitez émuler.

Pour créer un émulateur, cliquez sur le menu Tools > AVD Manager (AVD pour Android Virtual Device).

../_images/android_studio_premiere_app_avd_manager.png

Cliquez sur le bouton Create Virtual Device… pour ajouter, depuis la liste fournie, le modèle d’appareil à émuler.

À partir de la fenêtre du AVD Manager, vous pouvez directement lancer l’émulateur. Lorsque vous lancez votre application depuis Android Studio, l’émulateur apparaît dans la liste des périphériques disponibles.

Note

Si l’émulateur n’est pas lancé lorsque vous démarrez votre application, il sera préalablement lancé par Android Studio. L’émulateur peut consommer beaucoup de ressources systèmes, notamment au lancement. Je vous conseille de ne pas fermer l’émulateur après chaque test. Contentez-vous de relancer votre application depuis Android Studio de manière à ce qu’elle soit redéployée dans l’émulateur en cours d’exécution.

Notion d’activité

Le modèle de projet choisi génère le code pour une application avec une activité. Une activité est un composant d’une application Android qui représente un écran avec lequel l’utilisateur va pouvoir interagir. Un composant Android est représenté par une classe Java. Cependant, un composant n’est pas instancié directement dans le code de l’application. C’est lors de l’exécution que le système prend en charge la création et le cycle de vie d’un objet de cette classe. Dans le cas d’une activité, un objet correspondant est instancié par le système quand il est nécessaire d’afficher l’écran à l’utilisateur (par exemple au lancement de l’application).

Pour notre application, le générateur de projet a créé une classe MainActivity dans le répertoire java dont voici le code :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package dev.gayerie.premiereapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

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

La classe MainActivity hérite de AppCompatActivity. Cette dernière fournit du code de base pour la gestion d’une activité. Dans notre exemple, nous surchargeons la méthode onCreate pour changer le comportement de l’activité au moment de sa création.

Important

Remarquez qu’à la ligne 11, la méthode onCreate commence par appeler sa super-implémentation. Cette manière de faire est courante dans l’API Android. Lorsque l’on veut modifier le comportement de l’activité, il faut s’assurer que le code d’origine est bien appelé pour que l’activité puisse fonctionner correctement.

La ligne 12 constitue le seul véritable changement de comportement pour notre activité :

setContentView(R.layout.activity_main);

Une activité est associée à une vue, c’est-à-dire à une ensemble de composants graphiques prenant en charge le rendu de l’écran. Il est tout à fait possible de créer les composants de vue en Java mais il est plus facile d’utiliser un layout qui permettra de créer dynamiquement pour nous les composants nécessaires. La méthode setContentView attend en paramètre un identifiant de ressources d’un layout.

Nous allons voir dans la section suivante que la variable R.layout.activity_main correspond en fait à l’identifiant d’une ressource qui décrit le rendu de l’activité. Cette ligne de code permet donc de créer l’ensemble des composants graphiques qui doivent s’afficher dans l’activité.

Notion de ressources

En plus du code Java, une application Android peut contenir un ensemble d’informations que l’on désigne de manière générale comme des ressources. Le type d’une ressource peut être très divers. Il peut s’agir d’un nombre, d’une chaîne de caractères, d’une image, d’un fichier quelconque, de la description du rendu d’une activité…

Les ressources sont stockés dans des sous-répertoires dans [module]/src/main/res. Dans le module par défaut app, les ressources sont donc stockées dans app/src/main/res. Dans la vue Android de l’éditeur, le répertoire res est directement affiché sous le module. Par défaut, ce répertoire contient les sous-répertoires :

  • drawable : ce répertoire contient les images (PNG, JPEG…) ou des fichiers XML décrivant des images vectorielles (format SVG).

  • layout : ce répertoire contient les fichiers XML décrivant la disposition des vues d’une activité.

  • mipmap : ce répertoire contient les icônes des l’application. Comme une application Android doit pouvoir être installée sur différents périphériques, il est nécessaire de fournir plusieurs résolutions de la même icône. La technique du mipmap consiste précisément à fournir plusieurs résolution d’une même image afin d’éviter la pixellisation à l’affichage en permettant au système de choisir la résolution adéquate.

  • values : ce répertoire contient les variables que l’on ne souhaite pas écrire en dur dans le code (comme les libellés et les messages).

Note

Il existe d’autres types de ressources. Par exemple, il est possible d’ajouter un fichier quelconque dans le répertoire [module]/src/main/res/raw. Ce fichier peut ensuite être lu lors de l’exécution de l’application. Pour en savoir plus :

Pour notre application, le répertoire app/res/layout contient le fichier activity_main.xml. Il s’agit d’une fichier de layout, c’est-à-dire un fichier qui décrit le rendu visuel de l’activité. Android Studio intègre un éditeur de layout qui vous permet d’éditer graphiquement votre layout. L’éditeur est ouvert par défaut lorsque vous double-cliquez sur le nom du fichier. Vous pouvez à tout moment consulter le source XML en cliquant sur l’onglet Text sous l’éditeur.

../_images/android_studio_premiere_app_layout_editor.png

L’éditeur de layout

Accéder à un identifiant de ressource depuis le code Java

Toutes les ressources sont accessibles à partir de leur identifiant (une constante numérique créée automatiquement par le compilateur). Depuis votre code Java, l’identifiant est fourni par la classe R. La classe R est une classe dont le code source est automatiquement écrit par Android Studio et qui est déclarée dans le package par défaut de votre application. Pour accéder à l’identifiant d’un layout, il faut écrire

R.layout.[nom du layout]

Le nom du layout correspond au nom du fichier XML en omettant l’extension.

De la même manière, vous pouvez accéder aux identifiants des fichiers dans les différents sous-répertoires de ressources. Il existe une particularité pour le répertoires values. Ce dernier contient des fichiers XML qui permettent de déclarer des valeurs comme des entiers, des valeurs booléennes, des chaînes de caractères, des couleurs…

Si vous éditez le fichier app/res/values/strings.xml, vous trouverez :

<resources>
    <string name="app_name">Première Application</string>
</resources>

Ce fichier déclare une chaîne de caractères nommée app_name est qui correspond au nom de l’application. Pour accéder à l’identifiant de cette constante depuis le code source Java, on utilise la classe R de la manière suivante :

int appNameId = R.string.app_name;

Note

Il existe plusieurs classes R dans l’API qui permettent d’accéder aux identifiants de ressources fournies en standard par Android.

Accéder à une ressource depuis une autre ressource

Il est très souvent utile de pouvoir accéder à une ressource depuis une autre ressource. C’est notamment le cas dans les fichiers de layout. Si l’on souhaite placer un composant graphique avec un libellé, la valeur du libellé peut être externalisé dans le fichier app/res/values/strings.xml et avoir lui-même un ID. Dans ce cas, on utilise la notation suivante :

@[type]/id

Par exemple, pour accéder au nom de l’application déclaré dans le fichier app/res/values/strings.xml, on écrira :

@string/app_name

On peut donc très facilement ajouter du texte dans le layout d’une activité pour afficher le nom de l’application:

<TextView
    android:text="@string/app_name"
/>

N’importe quelle valeur peut être remplacée par un identifiant de ressource à condition que le type de la ressource soit compatible avec le type de la valeur.

Identifier une ressource dans un layout

Lorsqu’on définit un layout dans un fichier XML, il est utile de pouvoir associer un composant graphique à un identifiant. Nous verrons que nous pouvons alors récupérer en Java une référence sur ce composant à partir de son identifiant.

La déclaration d’un identifiant dans un layout XML se fait avec l’attribut android:id et le format de l’identifiant est :

@+id/[ID]

Par exemple, pour identifier un TextView dans un fichier XML de layout :

<TextView
    android:id="@+id/monTexte"
/>

Exercices

Utiliser un identifiant de ressource dans le layout

Modifier le layout app/res/layout/activity_main.xml afin que le texte affiché dans l’activité soit externalisé dans le fichier app/res/values/strings.xml.

Ajouter un bouton

Modifiez le layout app/res/layout/activity_main.xml afin d’ajouter un bouton avec l’ID bouton. Ajouter également l’ID texte au libellé (de type TextView) déjà présent dans le layout de l’activité.

Nous pouvons maintenant modifier le code Java de notre activité. La méthode onCreate est habituellement utilisée pour récupérer une référence sur les Views (les composants graphiques) grâce à la méthode findViewById. Avec une référence sur une View, on peut positionner des valeurs (par exemple le contenu dans champ ou le texte à afficher) ou on peut ajouter des listeners à appeler en réponse à une action utilisateur. Pour notre application, nous allons enregistrer un listener pour agir lorsque l’utilisateur clique sur le bouton.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package dev.gayerie.premiereapplication;

import androidx.appcompat.app.AppCompatActivity;

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

public class MainActivity extends AppCompatActivity {

    private TextView texte;

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

        texte = findViewById(R.id.texte);
        Button bouton = findViewById(R.id.bouton);
        bouton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                texte.setText("Vous avez cliqué sur le bouton !");
            }
        });
    }
}

Aux lignes 19 et 20, notez l’appel à la méthode findViewById qui attend une identifiant de View et qui retourne l’objet correspondant au composant graphique. Pour accéder à un identifiant, on utilise la classe R de la façon suivante :

R.id.[nom ID]

Améliorez le code de l’application en externalisation la chaîne de caractères présente dans le code dans le fichier de ressources res/values/strings.xml.

Note

Pour améliorer la lisibilité du code, vous pouvez utiliser les lambdas. Pour l’exercice précédent, vous pouvez écrire à la place :

package dev.gayerie.premiereapplication;

import androidx.appcompat.app.AppCompatActivity;

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

public class MainActivity extends AppCompatActivity {

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

        TextView texte = findViewById(R.id.texte);
        Button bouton = findViewById(R.id.bouton);
        bouton.setOnClickListener(v -> texte.setText("Vous avez cliqué sur le bouton !"));
    }
}

Pour que votre projet compile, vous devez néanmoins activer le support Java 1.8. Pour cela, éditez le fichier build.gradle du module app et assurez-vous que la section suivante est bien présente dans la section android :

android {

    // ....

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

Vous devez ensuite resynchroniser votre projet comme demandé par Android Studio.

Le fichier AndroidManifest.xml

Chaque module de votre projet fournit un fichier manifeste nommé AndroidManifest.xml. Il se trouve dans le répertoire src/main de votre module. Android Studio l’affiche dans le répertoire manifests. Ce fichier est lu au lancement de l’application par le système pour identifier le comportement de votre application. Nous verrons au cours des chapitres suivants que ce fichier contient la déclaration de tous les composants de votre application (activités, broadcast receivers, services) mais aussi la déclaration des autorisations à demander à l’utilisateur à l’installation, la version d’Android requise…

Pour notre application simple, le fichier AndroidManifest.xml contient :

Le fichier AndroidManifest.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="dev.gayerie.premiereapplication">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Parmi les nombreuses informations fournies, on peut regarder plus précisément :

  • ligne 3 : l’attribut package fournit le package identifiant l’application. C’est notamment dans ce package que la classe R sera créée.

  • ligne 5 : l’élément application marque le début de la déclaration des informations sur notre application.

  • ligne 7 : l’attribut android:icon déclare l’icône de l’application qui pointe vers la ressource @mipmap/ic_launcher.

  • ligne 8 : l’attribut android:label fournit le nom de l’application à partir de la ressource @string/app_name (déclarée dans app/res/values/strings.xml)

  • ligne 12 : l’élément activity permet de déclarer une activité en fournissant sa classe d’implémentation grâce à l’attribut android:name. Remarquez que dans l’exemple, la classe d’implémentation de l’activité est .MainActivity. Le point au début signale que la classe doit être cherchée dans le package de l’application. Donc l’activité est fournie par la classe :

    dev.gayerie.premiereapplication.MainActivity

  • les lignes 13 à 15 indiquent que cette activité est l’activité principale. Autrement dit, lorsque l’application est lancée par l’utilisateur, c’est cette activité qui doit être affichée. Nous reviendrons plus tard sur les notions d’intent et de filtre d’intent qui sont très importantes dans le développement Android pour l’échange d’information entre composants.