--- tags: LPDIM title: Android --- # TP --- Android Advanced :airplane: ## Android Jetpack 🚀 :::info Question 1 👉 Quel est l’interêt de Jetpack du point de vue d’un développeur ? Qu’est ce que la séparation des responsabilités ? ::: :::success Jetpack est une library qui est destinée aux développeur, pour les aider. ::: Au point de vue des développeurs, l'interêt de Jetpack est de facilité le code sur toutes les versions d'Android et tous les appareils. Ce qui le rend plus efficace et qui fait gagner du temps pour les développeurs. La séparation des responsabilités c'est un principe à suivre lors du développement. Celon lui, le logiciel doit être divisé en fonction des types des tâches qu’il effectue. C'est aussi un aspect fondamental de l’utilisation des couches dans les architectures d’applications. :::info Question 2 👉 Quels sont les avantages, selon vous, d’avoir un code plus concis, plus court ? Les inconvénients ? ::: :100: Avantages :100: Selon moi, avoir un code plus concis, plus court permet de se retrouver plus facilement dans notre développement et aussi un code plus lisible. :collision: inconvénients :collision: L'inconvénient c'est que si le code est plus court, il y a moins de détails donc plus difficile a comprendre. --- ## Pourquoi les architectures components ? :::info Question 1 :point_right: Qu’est ce qu’un viewModel provider ? Est-ce qu’un view model doit connaitre l’état de l’UI ? ::: :::success Un viewModel provider est une classe qui s'ajoute entre la vue et une activité. ::: Un viewModel ne connaît pas l'état de l'UI mais il se réfère à celui-là, il stock et gère ses données. :::info Question 2 :point_right: Qu’est ce qu’une Live Data ? A quoi cela sert ? Décrivez le design pattern Observer avec vos mots ::: :::success Une LiveData est une classe observable qui est conscient du lifecycle. Ainsi, il ne met que a jour les observateurs de composants d'applications qui sont dans un état de lifecycle actif. Il respect alors les activités, les fragments ou les services. ::: :gem: Design Pattern Observer :gem: Le design Pattern Observer définit une relation entre un objet, si un objet change d'état, tous les composants qui sont dépendant de lui se mettent à jour. :::info Question 3 :point_right: Que signifie qu’un composant est lifecyle aware concrètement ? ::: :::success Le Lifecyle aware effectue des actions en réponse à un changement de l'état du lifecycle d'un autre composant (fragments et activités). ::: Concrètement, lorsqu'un composant est Lifecyle aware signifie que lorsqu'un utilisateur fait une action, comme une rotation d'écran ou encore un back, le Lifecycle aware n'agit pas sur les mise à jour de l'activité et/ou du fragment. :::info Question 4 :point_right: Faut il exposer un MutableLiveData<> au viewModel ? La vue s’abonne à une LiveData avec quelle instruction ? ::: :::success Un MutableLiveData<> diffère de LiveData, il rent les méthodes `setValue()` et `postValue()` public, puisque dans le LiveData, ses méthode sont privées. ::: Il faut exposer un MutableLiveData<> au viewModel puisque le viexModel n'utilise que lui. La vue s’abonne à une LiveData avec l'instruction Observer. :hot_pepper: Voici l'exemple :hot_pepper: ![](https://i.imgur.com/HONPlmt.png) :::info (Optionnel) Question 5 :point_right: Expliquer la différence entre Databinding et Binding. Est-ce que le Databinding est nécessaire pour travailler sur Android ? ::: :::success Le Databinding est une library intégrée dans le support library du SDK, il permet de lier les composants de l'interface utilisateur. ::: :::success Le Binding est une fonctionnalité qui permet d'écrir du code plus facilement, il intéragit avec la vue. ::: La différence entre Databinding et Binding c'est que le DataBinding se fait dans le XML tandis que le Binding, lui se fait dans le fragment, c'est à dire l'UI. ***DataBinding :*** Dans le fragment : ![](https://i.imgur.com/A1fLG4q.png) Dans le XML : ![](https://i.imgur.com/OfvrNLy.png) ![](https://i.imgur.com/tzTfPYu.png) ![](https://i.imgur.com/BM06suV.png) ***Binding :*** Dans le fragment : ![](https://i.imgur.com/0M0mrCr.png) ![](https://i.imgur.com/h6objf3.png) --- ## Gestion des données - :zap: L’accès au réseau ### Les permissions :closed_lock_with_key: :::info Question 1 :point_right: Décrivez le code à utiliser pour afficher une demande explicite de permission à l’utilisateur. Créez une application simple qui demande l’accès explicitement aux fichiers de l’appareil. Faites valider par l’enseignant ::: ***MainActivity.kt*** ```kotlin= package com.example.permissions import android.Manifest import android.content.pm.PackageManager import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import android.widget.Toast import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat private const val CAMERA_PERMISSION_CODE = 100 private const val STORAGE_PERMISSION_CODE = 101 class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) goPermission(Manifest.permission.READ_EXTERNAL_STORAGE, STORAGE_PERMISSION_CODE); goPermission(Manifest.permission.CAMERA, CAMERA_PERMISSION_CODE); } private fun goPermission(permission: String, requestCode: Int) { if (ContextCompat.checkSelfPermission(this@MainActivity, permission) == PackageManager.PERMISSION_DENIED) { Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show() ActivityCompat.requestPermissions(this@MainActivity, arrayOf(permission), requestCode) } else { Toast.makeText(this, "Permission Granted", Toast.LENGTH_SHORT).show() } } } ``` ***activitymain.xml*** ```kotlin= <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> </androidx.constraintlayout.widget.ConstraintLayout> ``` ***AndroidManifest.xml*** ```kotlin= <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.permissions"> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <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/Theme.Permissions"> <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> ``` ***Emulator*** ![](https://i.imgur.com/0iWc9k5.png =300x) --- ## Faire une requête vers un web service :outbox_tray: :desktop_computer: :::info Question 2 :point_right: Une erreur a du survenir, faites valider votre appel de requête par l’enseignant. Ou si vous avez compris l’erreur, corrigez la et faites valider. Expliquez pourquoi cette erreur est survenue ? ::: Cette erreur est survenue parce que la requête était synchrone et cette requête ne peut pas se faire en même temps que le démarrage du MainActivity, qui lui utilise déjà le thread principale. Pour ne plus avoir cette erreur, il faut faire une requête asynchrone et non synchrone. :::info Question 3 :point_right: Lancez maintenant une requête pour récuperer un personnage en particulier. Faites valider. ::: ```kotlin= val request = Request.Builder() .url("https://rickandmortyapi.com/api/character/2") .build() ``` --- ## Faire une requête, c’est cool, utiliser les données c’est mieux. :bar_chart: :::info Question 4 :point_right: Lancez une requête pour récuperer un personnage, et le parser dans une instance de data class Character. Faites valider par l’enseignant. Quel Content negociator avez vous utilisé ? ::: J'ai utiliser le Content negociator Moshi, celui qui est recommander par OkHttp. ```kotlin= class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) run() } private val TAG = "MainActivity" private val client = OkHttpClient() private val moshi = Moshi.Builder().build() private val characterJsonAdapter = moshi.adapter(Character::class.java) fun run() { val request = Request.Builder() .url("https://rickandmortyapi.com/api/character/24") .build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { e.printStackTrace() } override fun onResponse(call: Call, response: Response) { response.use { if (!response.isSuccessful) throw IOException("Unexpected code $response") val character = characterJsonAdapter.fromJson(response.body!!.source()) Log.i(TAG, character?.name.toString()) } } }) } @JsonClass(generateAdapter = true) data class Character ( val id: Long, val name: String, val status: String, val species: String, val type: String, val gender: String, val origin: Location, val location: Location, val image: String, val episode: List<String>, val url: String, val created: String ) @JsonClass(generateAdapter = true) data class Location ( val name: String, val url: String ) } ``` :::info Question 5 :point_right: Faites une requête pour récuperer une liste de personnages, et désérialiser le tout dans des objets Kotlin. Faites valider. ::: ```kotlin= fun run() { val request = Request.Builder() .url("https://rickandmortyapi.com/api/character") .build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { e.printStackTrace() } override fun onResponse(call: Call, response: Response) { response.use { if (!response.isSuccessful) throw IOException("Unexpected code $response") val character = moshi.adapter(CharacterList::class.java).fromJson(response.body!!.source()) Log.i(TAG, character.toString()) } } }) } @JsonClass(generateAdapter = true) data class CharacterList ( val results: List<Character>, val info: Info ) ``` --- Léa PORTIER - 25/01/2021.