Un guide trop complet sur les bibliothèques Android sous-utilisées

Publié: 2022-03-11

Tout développeur expérimenté vous dira que son meilleur code n'est pas celui qu'il a écrit. C'est du code qu'ils ont pris du travail de quelqu'un d'autre.

Oui, nous, les développeurs, sommes des résolveurs de problèmes innovants, mais bon nombre des problèmes que nous rencontrons ont déjà été résolus et les remèdes regroupés dans des bibliothèques accessibles à tous. Pourquoi réinventer la roue quand les roues libres sont partout ?

Android ne fait pas exception. La source ultime de réutilisation du code est le SDK Android lui-même, qui est livré avec d'excellentes constructions et services qui feront une grande partie de votre travail pour vous.

Mais là où le SDK manque, la communauté Android a créé des bibliothèques haut de gamme qui peuvent vous faire économiser des tonnes de travail de codage, en le remplaçant par des implémentations hautement optimisées, contrôlées et testées. Je ne parle pas des bibliothèques évidentes - Android Support Library, Android Design Support Library, Gson. Je fais référence à des outils que vous ne connaissez peut-être pas. Et même si vous le faites, vous ne les utilisez probablement pas encore.

L'une des principales différences entre un développeur standard et un développeur maître est l'utilisation correcte des bibliothèques tierces. Un développeur maître accomplira à peu près la même tâche trois fois plus vite qu'un novice, et généralement avec un code plus court. Une grande partie de cela vient de savoir quelles bibliothèques tierces utiliser et comment les intégrer correctement dans votre projet.

J'ai développé, encadré et dirigé des équipes Android pendant des années, et j'ai étudié et utilisé des dizaines d'outils et de bibliothèques externes. (Je suis même connu pour avoir lu leur code d'implémentation et discuté de leurs composants internes avec le développeur.) Beaucoup ont été très efficaces pour m'aider à faire le travail, mais la vérité est que la plupart ne l'ont pas été.

C'est pourquoi j'ai créé ce guide. Appuyez-vous sur mon expérience, ainsi que sur celle d'autres développeurs mobiles, pour vous assurer que vous utilisez les meilleures bibliothèques. J'en ai choisi sept. Je soupçonne qu'ils feront partie de vos favoris très bientôt aussi.

Sélection de la bonne bibliothèque Android

Lors du choix d'une bibliothèque, je recherche quatre caractéristiques clés :

  • Il fournit une solution cohérente et de haute qualité pour un problème réel et non trivial.
  • Il utilise une API aussi simple que possible.
  • Cela ne force aucun changement dans mon architecture globale.
  • Il a une large base d'utilisateurs et, de préférence, une communauté de développeurs active.

Les trois premières fonctionnalités sont décisives. S'ils ne sont pas présents, je passe à autre chose ou je commence à coder à la main.

Bibliothèques Android

Les bibliothèques que je couvre ci-dessous réussissent les quatre tests. Ils résolvent également certains des aspects les plus difficiles du développement mobile.

  • Deux bibliothèques pour l'injection de dépendances, la liaison entre la mise en page et Java, les objets fictifs.
  • Modèle de messagerie pub/sub intégré à l'application.
  • Couche de communication HTTP sécurisée, efficace et à récupération automatique.
  • Manipulation d'images : téléchargement, mise en cache, redimensionnement et chargement dans la RAM.
  • Diffusion vidéo en temps réel.
  • Détection de fuite mémoire.

ButterKnife : l'outil ultime d'injection de dépendances

Il s'agit de la bibliothèque d'injection de dépendance ultime pour Android. Simple, robuste, super rapide (pas de réflexion !) et capable de supprimer une grande partie du code passe-partout de votre application.

Certains diront que ButterKnife est ce que la mise en page d'Android vers le mappage Java aurait dû être en premier lieu.

Injection de dépendance Android Butterknife

Fini le besoin de lier directement chacune de vos vues via un appel à findViewById() ; au lieu de cela, il existe une vue annotée qui vous donne un accès direct au code. ButterKnife élimine également le besoin d'événements d'interface utilisateur passe-partout tels que onClick , onTouch , etc., et les remplace par du code auto-injecté.

Mais assez bavardé, voyons le code.

Afficher la liaison de champ :

 class MyButterKnifeActivity extends Activity { @BindView(R.id.name) TextView name; @BindView(R.id.address) TextView address; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.bind(this); // MUST BE CALLED BEFORE ACCESSING UI FIELDS name.setText(“etc etc”); } }

Liaison de ressource :

 class ExampleActivity extends Activity { @BindString(R.string.username) String username; @BindDrawable(R.drawable.graphic) Drawable graphic; @BindColor(R.color.bg_color) int bgColor; @BindDimen(R.dimen.lower_padding) Float lowerPadding; // and no need for getResources().getString()/getDrawable/getColor() }

Liaison d'événement d'interface utilisateur :

 @OnClick(R.id.my_button) public void clickHandler(View view) { // onClick logic goes here }

AndroidAnnotations : Faire passer l'injection de dépendances au niveau supérieur

Juste derrière ButterKnife en matière d'injection de dépendances, AndroidAnnotations utilise une approche légèrement différente : les classes générées automatiquement, qui, une fois que vous les maîtrisez, sont extrêmement simples. Encore plus utile, il vous permet d'injecter des dépendances "basées sur le nom". Par exemple, @ViewById ListView myUserList; demande à la bibliothèque d'attribuer à ce champ un layoutListView du même nom.

AndroidAnnotations est également extrêmement rapide, mais il y parvient d'une manière quelque peu différente de celle de ButterKnife. Au lieu de l'injection de dépendance de liaison d'exécution, AndroidAnnotations crée une duplication au moment de la construction de toutes les activités affectées et y intègre sa logique de connexion, vous permettant ainsi d'obtenir les mêmes performances qu'avec une logique codée à la main.

Mais les capacités d'injection d'AndroidAnnotations vont encore plus loin que cela. Vous pouvez injecter à la fois l'état et la mise en page dans une activité.

Mise en œuvre d'Android Annotations :

 @NoTitle @Fullscreen @EActivity(R.layout.my_layout) public class MyActivity extends Activity { @ViewById ListView customerList; // auto-binded to R.id.customerList @App MyApplication app; // auto-binded to app object @AminationRes Animation fadeoutAnimation; @UiThread void updateUI() { // main thread action } }

La dernière annotation nécessite un peu plus d'explications : une tâche courante pour une application Android multithread est de passer des threads d'arrière-plan (ou de travail) au thread avant (ou principal ou UI), qui est le seul qui permet l'accès aux composants de l'UI. . Cette tâche, bien que simple, est souvent requise et implique un codage compliqué :

 new Handler(Looper.getMainLooper()).post(new Runnable() { logic goes here } ); // NO ANNOTATIONS

Dans AndroidAnnotations, tout ce que vous avez à faire est d'annoter votre fonction avec @UiThread, et elle est désormais garantie de toujours s'exécuter :

 @UiThread void updateUI() {..} // WITH ANNOTATIONS

Notez que cette annotation s'applique aux classes de composants Android standard (activités, services, etc.). Mais que se passe-t-il lorsque je souhaite également annoter mes propres cours ?

Ici, AndroidAnnotations propose un nouveau concept, celui de l' EBean . Tout ce que vous avez à faire est de marquer votre classe comme telle en utilisant @EBean , et vous êtes prêt à partir :

 @EBean public class MyNonComponentClass { @SystemService NotificationManager notifManager; @Bean MyOtherClass dependency; @UiThread void updateUI() { // main thread work goes here } }

EventBus : la communication entre composants simplifiée

La bibliothèque EventBus transforme un problème qui hante les développeurs Android depuis des années en une promenade dans le parc. La communication entre composants n'a jamais été aussi simple : utilisez un simple modèle pub/sub pour communiquer entre deux parties de votre système.

Animation EventBus

L'utilisation du bus d'événements se traduira par un code plus robuste, car il vous oblige à découpler vos composants les uns des autres.

Votre service d'interrogation en arrière-plan n'a plus besoin de connaître vos fragments pour les alimenter avec des événements de changement.

L'utilisation d'EventBus est simple.

une. Créer des classes d'événements. Travailler avec des POJO ici est préférable :

 class NewUserEvent { String fullname; String address; String role; // add getters and setters }

b. Créez des méthodes de gestion d'événements dans la classe, toute classe à laquelle vous souhaitez vous abonner pour ces événements :

 class MySubscriber { @Subscribe public void newUserHandler(NewUserEvent event) { // handle NewUserEvent } @Subscribe public void newUserHandler(AnotherEvent event) { // handle AnotherEvent } }

Mais bon, tout développeur Android à moitié expérimenté s'arrêterait et demanderait à ce stade : quel est le modèle de threading de ces gestionnaires ? Et puis-je forcer un gestionnaire à exécuter le thread principal si, par exemple, cela implique un accès aux composants de l'interface utilisateur ? Bonne question…

Par défaut, toutes les méthodes de gestionnaire s'exécutent sur un thread de travail extrait d'un pool de threads alloué et géré par EventBus lui-même. Si vous avez besoin d'une méthode de gestionnaire pour s'exécuter sur le thread principal, développez vos annotations d'abonnement comme suit :

 @Subscribe(threadMode = ThreadMode.MAIN) public void runOnMainThreadHandler(AnotherEvent event) { … }

Avertissement : n'abusez pas de cette fonctionnalité ! Les opérations de longue durée ne doivent jamais être exécutées sur le thread principal , et même avec des opérations rapides, soyez prudent. Submerger le fil principal est le moyen le plus sûr de rendre votre application lente, nerveuse et fondamentalement moins amusante pour vos utilisateurs.

c. Gérez le cycle de vie d'inscription EventBus de votre classe d'abonnés, c'est-à-dire quand se connecte-t-il et quand se déconnecte-t-il du bus ? Un flux d'inscription raisonnable pour une activité serait :

 class MySubscriberActivity extends Activity { @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); // START RECEIVING EVENTS HERE } @Override public void onStop() { EventBus.getDefault().unregister(this); // NO MORE EVENTS super.onStop(); } }

Ce qui précède n'est bien sûr qu'un exemple. Vous pouvez effectuer la (dés)inscription où vous le souhaitez.

ré. Et enfin, déclenchez réellement un événement :

 EventBus.getDefault().post(new MyEvent(“I'm here”));

Il y a beaucoup plus à savoir sur l'utilisation d'EventBus : les événements de multidiffusion (le comportement par défaut), l'utilisation d'événements persistants, les threads de livraison, les priorités, etc. Mais ce qui précède est suffisant pour vous permettre de démarrer avec cette technologie simple mais puissante.

OkHttp : HttpClient d'Android sur les stéroïdes

C'est ainsi que HttpClient d'Android aurait dû être écrit. Très simple, très intelligent. La bibliothèque OkHttp s'occupe en interne de la boucle de nouvelle tentative, de la compression automatique de la charge utile, de la prise en charge de Http/2, du regroupement des connexions et de la mise en cache des réponses afin d'éviter tout accès réseau inutile.

Animation de la bibliothèque OkHttp

L'utilisation d'OkHttp est une évidence.

HTTP POST :

 OkHttpClient client = new OkHttpClient(); MediaType JSON = MediaType.parse("application/json; charset=utf-8"); RequestBody body = RequestBody.create(JSON, json_str); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).execute(); return response.body().string();

HTTP OBTENIR :

 OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(urls[0]) .build(); Response responses = client.newCall(request).execute(); String jsonData = responses.body().string();

OkHttp prend également en charge des fonctionnalités utiles telles que la mise en réseau asynchrone, la demande de requête de routage de redirection, la requête de cache local, etc. N'hésitez pas à les utiliser si nécessaire. La plupart des développeurs utilisent OkHttp pour remplacer plus intelligemment le client HTTP par défaut d'Android, HttpURLConnection. En fait, tout ce projet a commencé comme un fork privé pour HttpURLConnection.

J'aime sa robustesse - il ajoute immédiatement à votre couche réseau.

Avec seulement quelques lignes de code, OkHttp fait en sorte que votre application se comporte comme si vous aviez passé des nuits à déboguer et à optimiser votre couche réseau.

Picasso : il y a une bonne raison pour laquelle Google l'utilise aussi !

Picasso est le moyen le plus simple et le plus robuste de gérer le téléchargement, la mise en cache, le redimensionnement et le recadrage des images.

Cette déclaration:

 Picasso.with(context).load(url).resize(50,50).centerCrop().into(imageView)

Le fera pour vous :

  • Connectez-vous à une URL distante.
  • Télécharger une image.
  • Stockez-le dans un cache LRU local qu'il gérera également pour vous.
  • Redimensionnez l'image d'origine avant de la charger en mémoire.
  • Exécutez tout ce qui précède sur un pool de threads géré par Picasso.
  • Utilisez l'image redimensionnée pour remplir votre imageView.
  • Avant toute exécution future, vérifiez le cache local pour vous assurer qu'un aller-retour réseau est vraiment nécessaire.

Construire l'ensemble de tâches ci-dessus nécessiterait de nombreuses heures de travail, même pour un maître développeur. Et cela suppose que vous vous souveniez de tout. Et si vous oubliez, disons, la partie redimensionnement ?

Animation de la bibliothèque Picasso Android

Eh bien, sur un appareil Android moyen, une application n'obtient pas plus de 50 à 60 mégaoctets de RAM, et le facteur pixels à octets pour la plupart des appareils Android est de 4. Cela signifie qu'il faut essayer de charger une image de 13 mégapixels à partir de la carte SD. nécessiterait 52 Mo de RAM. En d'autres termes, votre application planterait immédiatement.

Ce n'est qu'un exemple de la force de Picasso. L'une des premières choses que je fais lors de l'optimisation / du débogage d'un projet hérité à forte intensité multimédia est de transférer tout le chargement d'images sur Picasso. Vous seriez surpris de l'impact de cette simple étape sur la qualité de l'application.

L'un des témoignages les plus forts de la puissance de cette bibliothèque : de nombreux exemples de code Android de Google des deux dernières années utilisent Picasso pour le chargement d'images.

ActiveAndroid : ORM sans surcoût de performance

ORM, abréviation de mappage relationnel d'objet, a été rendu populaire à l'époque de J2EE. Il vous permet de stocker vos POJO dans une base de données et de les récupérer sans avoir à les convertir en champs séparés.

ORM Android actif

Est-ce utile ? Tout à fait, car cela vous permet d'écrire une grande partie de votre application sans coder aucune instruction SQL.

C'est aussi très efficace. Autrefois, les plates-formes ORM reposaient massivement sur la réflexion et étaient réputées pour leur lenteur. Les plates-formes modernes, y compris ActiveAndroid, sont beaucoup plus rapides et, pour la plupart des exigences pratiques, ne souffriront pas de surcharges de performances par rapport au codage SQL brut.

Pas d'instructions SQL codées à la main, pas de surcharge de performances !

Usage:

une. Initialiser dans l'objet application en étendant une classe Application personnalisée :

 public class MyApplication extends extends com.activeandroid.app.Application { … }

b. Créez POJO, dérivé d'une classe de modèle, avec des classes pour chacun des enregistrements que vous prévoyez de stocker dans la base de données. Chacun de ces POJO peut résider dans sa propre table. Les annotations doivent être utilisées pour spécifier le nom des champs de la base de données pour chaque membre stocké :

 @Table(name = "Categories") public class UserDetails extends Model { @Column(name = "Name") public String name; @Column(name = "Address") public String address; @Column(name = "Age") public int age; }

Si vous souhaitez définir un index pour un membre, utilisez l'annotation suivante :

 @Column(name = "ID", index = true) public String userID;

c. Pour empêcher la bibliothèque d'itérer sur tout votre temps de démarrage le plus classique, ce qui est le comportement par défaut, il est fortement recommandé de spécifier toutes vos classes de modèle dans la section manifeste suivante :

 <meta-data android:name="AA_MODELS" android:value=“com.myapp.MyModelA, com.myapp.MyModelB" />

Remarque : Les classes de modèles n'apparaissant pas dans cette liste ne seront pas reconnues par ActiveAndroid.

ré. Écrire dans la base de données :

 UserDetails usr = new UserDetails(); usr.save(); // RUNS ON A BACKGROUND THREAD

Si plusieurs écritures sont nécessaires, un moyen plus efficace serait de les regrouper en une seule transaction :

 ActiveAndroid.beginTransaction(); try { for (UserDetails u: userList) item.save(); ActiveAndroid.setTransactionSuccessful(); } finally { ActiveAndroid.endTransaction(); }

e. Lire les POJO de la base de données :

 new Select() .from(UserDetails.class) .where("name = ?", usr.getName()) .orderBy("Age") .executeSingle();

ORM était un outil indispensable pendant mes jours en tant que développeur côté serveur. Il a eu une entrée un peu tardive dans le domaine Android. Mais, enfin, ça y est : la programmation de base de données aussi simple que possible. Profitez-en.

LibStreaming : Streaming vidéo indolore

Le streaming vidéo en temps réel était autrefois un problème majeur en raison des API non documentées, des différences de version entre les SDK, de l'utilisation de la réflexion, etc.

Animation de la bibliothèque libStreaming

Heureusement, libStreaming a changé tout cela en encapsulant la plupart des complexités du streaming et en exposant une API simple et conviviale qui vous permet d'écrire une application de streaming de base en quelques heures.

En un mot, il rationalise le streaming vidéo.

Pour l'utiliser pour H.264 et AAC, vous devez procéder comme suit :

une. Initialisez un objet de session à la méthode onCreate de votre activité principale. Les objets de session représentent le streaming multimédia vers un pair :

 protected void onCreate(Bundle savedInstanceState) { mSession = SessionBuilder.getInstance() .setCallback(this) .setSurfaceView(mSurfaceView) .setPreviewOrientation(90) .setContext(getApplicationContext()) .setAudioEncoder(SessionBuilder.AUDIO_NONE) .setAudioQuality(new AudioQuality(16000, 32000)) .setVideoEncoder(SessionBuilder.VIDEO_H264) .setVideoQuality(new VideoQuality(320,240,20,500000)) .build(); mSurfaceView.getHolder().addCallback(this); }

b. Démarrez réellement la session :

 mSession.setDestination(destination_server_url); mSession.start();

c. Arrêtez la session lorsque vous avez terminé :

 mSession.stop();

Maintenant, s'il vous plaît, ne vous méprenez pas. Le streaming en temps réel est désordonné par nature et libStreaming ne supprime pas cette complexité. Cependant, il fait un très bon travail en vous le cachant la plupart du temps. Dans certains cas, vous devrez faire face à la complexité, par exemple lors de la sélection de la politique de signalisation des pairs, du choix de l'encodage de la caméra (vous souhaiterez généralement utiliser MediaCodec/surface-to-buffer) ou de la mise en paquets.

Pourtant, vous constaterez que les bons gars derrière libStreaming ont fait un effort supplémentaire pour fusionner ces complexités en douceur dans une API simple à utiliser.

LibStreaming prend en charge la plupart des encodeurs utilisés par les applications Android, notamment H.264, H.263, AAC et AMR.

J'ai obtenu d'excellents résultats avec cette bibliothèque. Plusieurs des applications de streaming les plus populaires l'utilisent dans le cadre de son infrastructure. Si jamais vous rencontrez le besoin, je suis certain que cela rendra votre expérience de streaming multimédia beaucoup plus fluide.

LeakCanary : Détecter les fuites de mémoire dans une ligne de code

Commençons par la motivation derrière cette bibliothèque : les fuites de mémoire . Les applications Android y sont sujettes, surtout si vous ne faites pas attention à votre codage. En fait, créer des fuites de mémoire est très simple. Tout ce que vous avez à faire est de stocker une référence d'activité en dehors de son contexte. En fait, même le stockage d'une référence à un seul objet de vue en dehors du contexte de son activité créera une fuite.

Pourquoi? Parce qu'une vue (toutes les vues, en fait) stocke en interne une référence de contexte à son activité contenante. Tant qu'une référence à la vue est conservée, son activité contenante, ainsi que ce qu'elle contient, y compris les drawables, la hiérarchie de la vue et les ressources, ne peut pas être récupérée par le ramasse-miettes.

Garder une référence à une activité qui fuit n'est pas toujours évident en tant que paramètre statique. Chaque fois que vous créez une classe interne ou générez un thread dans une activité, une référence à cette activité sera créée et l'activité ne pourra pas être récupérée tant que cette classe interne ou ce thread ne sera pas terminé.

Les fuites ne sont bien sûr pas uniques à Android, mais étant un système mobile avec des ressources mémoire limitées, l'impact est plus immédiat.

La fuite d'une référence à une seule activité gourmande en ressources suffit parfois à faire planter votre application avec une exception " Mémoire insuffisante".

Bibliothèque LeakCanary

Comment pouvez-vous vous protéger contre eux? Commencez par des pratiques de codage rigoureuses , bien sûr. Mais nous ne sommes pas tous des développeurs Android expérimentés, et même les développeurs expérimentés oublient parfois les règles.

Des révisions périodiques du code mettant l'accent sur les fuites de mémoire peuvent être utiles, mais elles prennent du temps. De plus, certaines fuites sont vraiment sournoises et difficiles à détecter par une simple révision du code.

L'utilisation de l'outil de mémoire du DDMS est un excellent moyen de savoir, au fil du temps, si votre application fuit. Vous devriez certainement l'utiliser. Cependant, il ne vous dira pas ce qui cause la fuite.

Voici leakCanary à la rescousse. C'est le meilleur détecteur de fuites de mémoire qui existe et il fournit une détection automatique des fuites - comme dans une ou deux lignes de code - pour toutes vos activités.

Pour l'utiliser, initialisez simplement leakCanary avec l'objet onCreate() de votre application :

 public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this); // more initialisations } }

Et tu as fini. LeakCanary surveillera les fuites de mémoire et enverra une notification s'il en détecte une.

LeakCanary réalise cette magie en injectant automatiquement un objet appelé ActivityRefWatcher dans toutes vos activités et en surveillant leur nombre de références après l'appel de onDestroy() . Un décompte de références > 0 sur une activité détruite ne peut signifier qu'une fuite.

Important : La détection des fuites ne fonctionne que pour les applications en mode débogage. Ne testez jamais les fuites (enfin, pas avec LeakCanary) dans un APK en mode de publication.

Mais que se passe-t-il si je veux tester d'autres parties de mon système à la recherche de fuites ? Ici, LeakCanary propose un objet appelé refWatcher, qui est en fait la valeur de retour de l'appel d'initialisation :

 refWatcher = LeakCanary.install(this);

Il peut être utilisé pour surveiller les valeurs qui seront bientôt récupérées. Plus précisément, des valeurs dont je pense qu'elles seront bientôt reconquises. Pour cela, appelez :

 refWatcher.watch(my_soon_to_be_reclaimed_obj);

La bibliothèque vous indiquera si cet objet n'a pas été libéré peu de temps après l'appel de surveillance.

Je n'ai trouvé nulle part la valeur de ce « court laps de temps », mais ce n'est probablement pas si important. Avec leakCanary, les choses fonctionnent. Inestimable.

Sommaire

Les développeurs expérimentés ont réduit leurs phases de codage et de débogage pendant des jours et des semaines à l'aide de ces bibliothèques, il n'y a donc aucune raison pour que vous ne puissiez pas faire de même.

Pour résumer, voici ce que ma sélection de librairies Android peut faire pour vous :

  • ButterKnife - Le code auto-injecté vous aidera à supprimer une grande partie du code passe-partout de votre application. C'est l'injection de code ultime pour Android. Ai-je besoin d'en dire plus ?

  • AndroidAnnotations – Utilisez des classes générées automatiquement à une vitesse fulgurante et l'injection de code basée sur le nom pour gagner du temps sans pénaliser les performances par rapport à la logique codée à la main.

  • EventBus - Découplez les composants pour un code plus robuste, la communication entre composants n'a jamais été aussi simple.

  • OkHttp - Un remplacement intelligent pour HttpURLConnection, avec prise en charge de la mise en réseau asynchrone, requête de requête de routage de redirection, requête de cache local, etc.

  • Picasso - Manipulation d'image simplifiée qui est si bonne qu'elle est maintenant utilisée par Google. C'est un gain de temps important dans les projets lourds de médias et certains projets hérités.

  • ActiveAndroid - ORM simplifié sans surcharge de performances.

  • LibStreaming - Streaming vidéo en temps réel, utilisé par les principales applications de streaming.

Sont-ce les seules bibliothèques Android qui valent votre temps ? Certainement pas. Mais je vous promets ceci : utiliser n'importe lequel d'entre eux sur votre prochain projet fera de vous un bien meilleur développeur. Si vous voulez les voir en action, jetez un œil à mon GitHub.

Si vous en utilisez déjà certaines ou toutes, ou si vous utilisez des bibliothèques alternatives, je vous invite à partager vos expériences dans les commentaires ci-dessous.

Connexes : Android 7.0 pour les développeurs : nouvelles fonctionnalités, mises à niveau des performances et autres éléments dont vous ne vous souciez pas