2003 : création de la startup “Android Inc”.
2005 : rachat d’Android Inc par Google.
2008 : Premier téléphone commercialisé avec Android.
2014 : v5 avec support 64 bits, moteur ART et Material Design.
2021 : sortie de la version 12.
Android est un système d’exploitation dédié aux terminaux mobiles.
Android c’est :
Une application Android est une archive APK (Android PacKage) d’extension .apk
, basé sur les archives JAR de Java.
Une archive APK contient :
ART : Android Runtime
Un SDK (Software Development Kit) est un ensemble d’outils
destiné aux développeurs.
Le SDK d’Android contient :
AndroidManifest.xml
: fichier XML contenant les informations essentielles de compilation et d’exécutionjava
: code source Java du projetres/drawable
: ressources imagesres/layout
: description des interfaces graphiques en XMLres/values
: dictionnaires de variables à utiliser dans le code et l’UIbuild.gradle
: fichier de configuration de Gradle, système de build d’Android Studio.Attention, cette architecture n’est pas celle des fichiers,
c’est une vue simplifiée proposée par Android Studio.
Activity
)Services
)ContentProvider
)BroadcastReceiver
)Pour communiquer, ils utilisent les intentions (classe Intent
).
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="p4b.tp1.myapplication">
<application
android:label="@string/app_name"
android:theme="@style/Theme.App">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
<manifest />
package="..."
désigne le package racine de l’application<application />
android:label
est le nom de l’application. @string
fait référence aux balises <string>
des fichiers XML placés dans res/values
android:theme
fait référence au style <style name="Theme.App">
situé dans un fichier de res/values
<activity />
android:name
est le nom de l’activité depuis le package
racineandroid:exported
(dés)active l’accès de cette activité aux autresintent-filter
désigne cette activité comme principale, à lancer au démarrage de l’application. Il en faut nécessairement une.<uses permission />
R
res/
.int
<resources>
<r_type name="r_name">r_value</r_type>
</resources>
Accéder aux ressources…
R.r_type.r_name
setContentView(R.layout.activity_main);
… dans le code Java.
@r_type/r_name
<application android:label="@string/app_name" />
… dans les fichiers XML.
L’activité est l’élément fondamental d’une application Android.
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Activity
Activity
ou AppCompatActivity
Activity
est un point d’entrée potentiel de l’applicationActivity
Activity
.AndroidManifest.xml
.onCreate
qui invoque super.onCreate(...)
.res/layout
)setContentView(...)
.Android Studio réalise tout cela automatiquement lorsqu’on crée une nouvelle activité via le menu.
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Bonne pratique : Le constructeur d’une activité n’est jamais utilisé !
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState)
{ ... }
@Override
protected void onStart() { ... }
@Override
protected void onResume() { ... }
@Override
protected void onPause() { ... }
@Override
protected void onStop() { ... }
@Override
protected void onRestart() { ... }
@Override
protected void onDestroy() { ... }
}
Lorsqu’une application est stoppée, il est possible de sauvegarder sont état dans un Bundle
pour le restaurer ensuite.
1 - Avant d’être détruite, l’activité exécute onSaveInstanceState
:
static final String STATE_SCORE = "playerScore";
@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt(STATE_SCORE, currentScore);
super.onSaveInstanceState(savedInstanceState);
}
2.a - Le Bundle
en paramètre de onCreate
permet de restaurer les valeurs précédemment sauvegardées :
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
currentScore = savedInstanceState.getInt(STATE_SCORE);
}
2.b - … ou celui de cette méthode, appelée après onStart()
seulement s’il y a un Bundle
sauvegardé :
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Pas de vérification nécessaire de savedInstanceState != null
currentScore = savedInstanceState.getInt(STATE_SCORE);
}
Une application contient en général plusieurs activités
Tous les détails sont sur la doc officielle.
Android définit deux types d’objets graphiques :
Les Layouts (héritent de ViewGroup
)
ConstrainedLayout
LinearLayout
RelativeLayout
Les Widgets (héritent de View
)
Button
TextView
ImageView
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, I am a Button" />
</LinearLayout>
android:id="@+id/name
@+id
indique qu’il s’agit d’un nouvel identifiantR
accessible avec R.id.name
Button myButton = (Button) findViewById(R.id.my_button);
android:layout_{width|height}
="match_parent"
: l’élément remplit tout l’élément parent="wrap_content"
prend juste la place nécessaire pour s’afficher<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
tools:layout_editor_absoluteX="158dp"
tools:layout_editor_absoluteY="341dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteY="341dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:layout_editor_absoluteY="341dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
ConstrainedLayout
permet de placer ses enfants de façon flexible (surtout relative) par rapport à son parent ou un autre élément.app:layout_constraintLeft_toRightOf="@+id/widget"
signifie que la gauche de l’objet courant doit être à droite de l’objet d’id widget
.Button button =
(Button)findViewById(R.id.myButton);
button.setOnClickListener(
new OnClickListener() {
public void onClick(View v) {
...
}
};
);
TextEdit myEditText =
(TextEdit)findViewById(R.id.edit);
myEditText.addTextChangedListener(
new TextWatcher() {
@Override
public void onTextChanged( CharSequence s,
int start, int before, int count) {
...
}
);
Les écouteurs sont instanciés avec des classes anonymes.
View
possède :
OnClickListener
, OnTouchListener
, etc.set...Listener
pour chacune des interfaces.Un Intent
représente l’intention d’un composant
de déléguer une action à un autre composant.
Un Intent
peut servir à :
startActivity[ForResult] (intent)
startService(intent)
ou bindService(intent)
send[Ordered]Broadcast(intent)
Intent intent = new Intent(Intent.ACTION_INSERT);
intent.setType(Contacts.CONTENT_TYPE);
intent.putExtra(Intents.Insert.NAME, name);
intent.putExtra(Intents.Insert.EMAIL, email);
// Try to invoke the intent.
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
// Define what your app should do if no activity can handle the intent.
}
Insertion implicite d’un nouveau contact.
Un Intent
contient :
Extra
) via un Bundle
(facultatif).Uri telnumber=Uri.parse("tel:0123456789");
Intent myActivity=new Intent(Intent.ACTION_DIAL, telnumber);
startActivity(myActivity);
Intention implicite de téléphone.
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
Intention explicite de téléchargement.
Un Intent
peut être :
static final int REQUEST_SELECT_CONTACT = 1;
public void selectContact() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, REQUEST_SELECT_CONTACT);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_SELECT_CONTACT && resultCode == RESULT_OK) {
Uri contactUri = data.getData();
// Do something with the selected contact at contactUri
...
}
}
Sélection d’un contact.
Il faut implémenter la méthode onActivityResult
de la classe Activity
.