Comment parler des principaux composants d'Android en 15 minutes

Présentation


Cet article explique comment informer une personne qui ne connaissait pas auparavant la programmation pour Android, de ses principaux composants. Intéresser et montrer que tout n'est pas aussi difficile qu'on le pense. En même temps, faites-le en 15 minutes et sans entrer dans l'explication d'une théorie de base que tout le monde peut lire par lui-même et revenir avec des questions de clarification.


Quand j'ai essayé de le faire pour la première fois, j'ai été désagréablement surpris par moi-même. Mon explication «simple et compréhensible» s'est transformée en ennuyeuse, dans le cadre de laquelle une tentative désespérée de saisir l'immensité et de dire en bref tout ce qui était un peu était clairement tracée. Inutile de dire qu'une telle histoire n'est pas susceptible d'intéresser, mais fait plutôt peur à votre interlocuteur, tout en diminuant le désir de faire quelque chose de votre propre chef, même si vous aviez auparavant une petite calculatrice dans vos plans.


Ce n'est un secret pour personne qu'un grand nombre d'articles sur ce sujet sont publiés sur Internet, mais dans mon cas, le récit sera légèrement différent: il n'y aura que de la pratique visuelle, sans définitions et autres détails. Autrement dit, nous regardons - nous voyons - nous commentons ce qui se passe. Il semble, à mon avis, tout est assez simple et clair, les morceaux de code sont également petits et très simples, prêts à être utilisés rapidement dans votre propre projet. Il me semble que cette approche donne un aperçu assez large des outils Android classiques, et lors de l'écriture de la première application, au lieu des questions «que dois-je utiliser», il y aura des questions plus spécifiques «exactement comment dois-je utiliser le composant X». Et déjà tous les détails sur cette personne pourront le découvrir par lui-même - s'il le veut.


Alors allons-y!


Apprentissage des composants


Installez l'application, lancez-la et ... pour l'instant, il suffit que MainActivity soit ouverte avant nous. On répondra plus tard à la question «pourquoi est-ce exactement».


Tout d'abord, regardons d'où il vient - de main_activity.xml, où tous les éléments d'interface sont déclarés. Ils sont publiés dans LinearLayout, il est donc peu probable que des questions se posent ici.


Afficher le code
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Android Demo Application" /> <TextView android:id="@+id/textView3" android:layout_width="match_parent" android:layout_height="wrap_content" android:text=" 1" /> <Button android:id="@+id/buttonShowToast" android:layout_width="match_parent" android:layout_height="wrap_content" android:text=" Toast" /> ... </LinearLayout> 

Composants simples


Toast


Voir l'image


Passons maintenant à MainActivity.java et au premier bouton de son interface - "Show Toast" (notification pop-up).
Recherchez l'identifiant du bouton dans main_activity.xml et accédez à son OnClickListener dans MainActivity.java.


Afficher le code
 Button btn = findViewById(R.id.buttonShowToast); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "This is a Toast", Toast.LENGTH_LONG).show(); } }); 

Il s'avère que pour afficher une notification pop-up, une ligne de code suffit! Super, non?


Interaction avec une autre activité


Voir l'image

Première activité

Deuxième activité


Essayons maintenant d'aller quelque part en dehors de la page principale de l'application. Par exemple, vers une autre de ces pages! Allez à "Interaction avec une autre activité" - et nous entrons dans une autre activité avec d'autres contrôles. Comment différentes activités d'une même application transfèrent-elles des données entre elles? Voici le moment de parler de deux mécanismes différents: un stockage constant des valeurs - shared_prefs, ainsi que startActivityForResult / onActivityResult (je ne voulais pas insérer cela au début, mais encore brièvement: si vous démarrez une nouvelle activité à partir d' une activité ouverte en utilisant startActivityForResult, puis une fois la deuxième activité terminée) sera appelé onActivityResult dans la première activité. Ne vous inquiétez pas si ce n'est pas encore clair).
Et bien sûr, démontrez en pratique!


Entrée dans shared_prefs:


Afficher le code
 String valueToSave = "test"; SharedPreferences.Editor editor = getSharedPreferences("demoapp", MODE_PRIVATE).edit(); editor.putString("myValue", valueToSave); editor.apply(); 

Lecture à partir de shared_prefs:


Afficher le code
 SharedPreferences prefs = getSharedPreferences("demoapp", MODE_PRIVATE); String storedValue = prefs.getString("myValue", "NOT_FOUND"); 

Un exemple avec onActivityResult - voir dans la source de l'application.


Enregistrement et restauration des paramètres


Voir l'image


Puisque nous avons mentionné shared_prefs, terminons avec eux. Nous allons dans "Enregistrer et restaurer les paramètres", où nous sommes présentés avec une carte typique d'un compte typique avec différents types de champs (notez que leur type est défini par une seule variable - un commutateur). Nous allons enregistrer le contenu de ces champs dans shared_prefs, puis les restaurer. Jusqu'à présent, un bouton séparé, aucun onResume - nous ne les avons pas encore atteints!
Enregistrer:


Afficher le code
 SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit(); EditText et = findViewById(R.id.editTextName); String name = et.getText().toString(); editor.putString("name", name); editor.apply(); 

Nous restaurons:


Afficher le code
 SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE); EditText et = findViewById(R.id.editTextName); et.setText(prefs.getString("name", "")); ... 

Menu simple


Voir l'image


La section suivante est un menu simple. Nous apprenons de lui qu'il n'y a rien de compliqué dedans - il suffit de définir onCreateOptionsMenu et de le remplir avec la structure de my_menu.xml.


Afficher le code
 @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.my_menu, menu); return true; } 

Menu contextuel


Immédiatement après - un bouton de menu contextuel. Il est déjà plus intéressant que le précédent même avec la présence de mémoire de réglages et la présence de sous-menus.
Le code du menu contextuel peut être consulté dans la source de l'application.


Lecteur audio


Voir l'image


En utilisant l'exemple du lecteur audio, en plus du fait qu'il n'y a rien de compliqué à jouer de la musique, vous pouvez démontrer la liaison de seekBar à quelque chose, dans ce cas, à la position actuelle du lecteur multimédia. Dans le contrôle du volume - aussi rien de surnaturel. Le bonus montre une erreur. Ouvrez l'activité avec le lecteur multimédia, lancez la lecture et appuyez sur le bouton "Retour". La musique continue de jouer, et elle ne peut plus être arrêtée ... Le problème! Comment le résoudre - nous le découvrirons un peu plus tard.
Le code du lecteur audio se trouve dans le code source de l'application.


Navigateur Web


Eh bien, en conclusion de la première partie, nous montrons qu'il n'y a rien de divin dans le navigateur Web non plus.


Voir l'image


Afficher le code
 WebView view = findViewById(R.id.webView); view.setWebViewClient(new WebViewClient()); view.getSettings().setJavaScriptEnabled(true); view.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); view.loadUrl("https://google.com"); 

La partie suivante est déjà plus difficile.


Services et notifications


BroadcastReceiver


Voir l'image


Rappelez-vous comment nous avons transmis le résultat d'une activité à une autre. Là, d'une manière ou d'une autre (nous ne savons pas encore comment), il s'est avéré que lorsque la deuxième activité a été fermée, le résultat a volé dans le résultat onActivityResult de la première. Mais pouvons-nous transférer le résultat n'importe où dans notre application? Oui, nous pouvons annoncer et enregistrer un auditeur qui nous entend de n'importe où dans le programme. Un tel auditeur est appelé BroadcastReceiver.


Afficher le code
 public BroadcastReceiver MyReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(getApplicationContext(), "Broadcast receiver: received!", Toast.LENGTH_LONG).show(); } }; 

Ici, vous ne pouvez pas vous passer d'intentions, mais au niveau le plus primitif: pour l'instant, le fait qu'elles nous suffisent est envoyé à un certain bus commun, et que selon une action prédéterminée, BroadcastReceiver nous entendra où que nous soyons.


Afficher le code
 IntentFilter filter = new IntentFilter(); filter.addAction("MyCustomActionName"); registerReceiver(MyReceiver, filter); sendBroadcast(new Intent("MyCustomActionName")); 

Pour le moment, nous avons une idée de base de ce qu'est le récepteur, mais pourquoi il est nécessaire n'est pas clair: c'est normal, et maintenant cela deviendra plus facile.


Service simple


Voir l'image


Passez en toute transparence au service, où BroadcastReceiver trouvera son application complète. Et au lieu d'essayer de dire en quelques mots ce qu'est un service Android, je propose de commencer la démonstration tout de suite. Exécutez le service, qui démarre son travail en arrière-plan. Parallèlement à cela, nous enregistrerons deux récepteurs: un en activité et un en service.
Le récepteur en service est nécessaire pour démontrer que, malgré l'exécution en arrière-plan, nous pouvons toujours l'atteindre à partir de l'activité.


Afficher le code
 public BroadcastReceiver MyServiceReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(getApplicationContext(), "Toast from Service: I hear you!", Toast.LENGTH_LONG).show(); } }; 

Le récepteur en activité est nécessaire pour afficher les résultats du service dans TextView.


Afficher le code
 public BroadcastReceiver MyPrintReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(getApplicationContext(), "pong", Toast.LENGTH_SHORT).show(); TextView tv = findViewById(R.id.textViewSimpleServiceStatus); String msg = intent.getStringExtra("msg"); tv.setText(msg); } }; 

Et, pour bien comprendre, lors de l'envoi d'un message du service à Activity, nous afficherons Toast ("ping"). Et lorsque nous recevons le message dans l'activité et que nous dessinons la valeur dans TextView, nous afficherons Toast ("pong").
Que la tâche principale du service soit d'envoyer de tels «pings» à l'activité, et la tâche de l'activité est de simplement les afficher dans son interface.


Afficher le code
 Handler handler = new Handler(); runnable = new Runnable() { public void run() { if (running) { printMsg("Service is still running " + running); handler.postDelayed(runnable, 5000); //   } else { printMsg("Service exited"); } } }; handler.postDelayed(runnable, 5000); 

Un exemple de l'opération du gestionnaire sera examiné en détail plus tard, pour le moment, il ne s'agit que d'un moyen d'envoyer un ping toutes les 5 secondes.


Et maintenant, après le démarrage du service, nous voyons Toast ("Service créé!") Et envoyer des notifications de ping-pong. Et dans TextView, les messages du service ont commencé à arriver.


Grande exécution en arrière-plan que nous avons vue. Fermez maintenant l'application! Sur les dernières versions d'Android, nous verrons ce qui suit: le service a redémarré (Toast ("Service créé!") Apparaîtra) et enverra des "pings". En même temps, il n'y a pas de "pongs" - après tout, il n'y a plus d'activité qui les traite! Après quelques secondes, les pings se sont également arrêtés sur mon smartphone. Le service est en train d'être détruit. Ouvrez les paramètres d'énergie, désactivez l'optimisation pour notre application et recommencez la procédure. Maintenant, le service n'est pas détruit, et même après la fermeture du programme, nous voyons des «pings» entrants stables. Mais, bien sûr, il n'y a aucune garantie ici, et un tel service ne durera pas très longtemps. Du point de vue du développeur, cela peut être terrible, mais regardons-le à travers les yeux d'un simple utilisateur: voulons-nous qu'une application fonctionne en arrière-plan si librement et en toute impunité, en mangeant une batterie? À peine. Comment alors travailler pleinement en arrière-plan?


Pour ce faire, il vous suffit d'en informer l'utilisateur. Il s'agit d'une exigence obligatoire d'Android, qui, en présence d'un canal de notification, vous permet de lancer non seulement l'habituel, mais le service Foreground, qui fonctionnera pleinement en arrière-plan sans risquer d'être tué à l'improviste.
Ajoutez à notre création de notre service:


Afficher le code
 String CHANNEL_ID = "my_channel_01"; NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Channel human readable title", NotificationManager.IMPORTANCE_DEFAULT); ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel); Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("") .setContentText("").build(); startForeground(1, notification); 

Et nous l'exécuterons avec la commande:


Afficher le code
 startForegroundService(new Intent(getApplicationContext(), TestServiceForeground.class)); 

Nous reviendrons à notre application de démonstration, rétablirons les paramètres d'économie d'énergie à leur état d'origine. Et maintenant, nous allons lancer le service Foreground. En fermant l'application lorsque le service est en cours d'exécution, nous notons que le service ne redémarre plus et n'est pas détruit, mais continue simplement de fonctionner de manière stable en arrière-plan, en envoyant ses "pings" toutes les 5 secondes. Dans le même temps, une icône de notification se bloque dans les notifications.


Voir l'image


L'utilisateur pourra masquer ces notifications s'il le souhaite, mais vous ne pouvez pas les masquer par programme au moment de la création du canal de notification.


Bouton flottant (superposition)


Voir l'image


Sachant ce qu'est un service, vous pouvez passer à son utilisation la plus simple et la plus évidente - travailler avec des boutons flottants. Sur les androïdes actuels, vous ne pouvez pas simplement déclarer les droits de rendu des superpositions comme ça - vous devez demander explicitement à l'utilisateur l'autorisation "Au-dessus de toutes les fenêtres". Ensuite, nous cliquons sur Dessiner la superposition et nous voyons une icône flottante, derrière laquelle se trouve vraiment un service qui écoute les clics dessus.


Envoi de notifications


Voir l'image


Une autre caractéristique importante qui sera très probablement utile dans la plupart des applications Android est l'envoi de notifications à l'utilisateur. Voyons brièvement comment cela fonctionne. Pour commencer (sur les androïdes actuels), nous devons créer un canal de notification - oui, oui, l'un de ceux qui sont généralement disponibles en grand nombre dans les applications modernes.


Afficher le code
 private void createNotificationChannel() { try { CharSequence channelName = CHANNEL_ID; String channelDesc = "channelDesc"; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { int importance = NotificationManager.IMPORTANCE_LOW; NotificationChannel channel = new NotificationChannel(CHANNEL_ID, channelName, importance); channel.setDescription(channelDesc); NotificationManager notificationManager = getSystemService(NotificationManager.class); assert notificationManager != null; NotificationChannel currChannel = notificationManager.getNotificationChannel(CHANNEL_ID); if (currChannel == null) { notificationManager.createNotificationChannel(channel); Toast.makeText(getApplicationContext(), "channel created", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), "channel exists", Toast.LENGTH_SHORT).show(); } } } catch (Exception e) { } } 

Eh bien, vous pouvez alors lui envoyer des notifications sans restrictions.


Afficher le code
 public void setNotify() { try { Intent snoozeIntent = new Intent("ActionFromNotify"); PendingIntent snoozePendingIntent = PendingIntent.getBroadcast(this, 0, snoozeIntent, 0); String title = "Start"; Intent intent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_launcher_background, title, snoozePendingIntent).build(); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_launcher_background) .setContentTitle("MyNotification") .setContentText("Text here") .setPriority(NotificationCompat.PRIORITY_LOW) .setContentIntent(null) .setOngoing(true) //   .setSound(null) .addAction(action); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); int notificationId = (int) (System.currentTimeMillis() / 4); notificationManager.notify(notificationId, mBuilder.build()); } catch (Exception e) { Log.e("error", e.toString()); } } 

Service d'envoi de notifications


Voir l'image


L'envoi de notifications directement depuis l'activité, vous voyez, n'est pas très impressionnant. Nous sommes encore habitués à autre chose. Mais que savons-nous de l'exécution en arrière-plan jusqu'à présent? Juste qu'il y a des services. Alors ne nous précipitons pas et ne faisons pas un service simple (pas même Foreground - pour plus de simplicité) qui envoie une nouvelle notification toutes les 5 secondes. D'accord, c'est déjà plus joli et plus impressionnant que d'envoyer simplement des notifications par un bouton.


Comme une courte pause après des services apparemment compliqués (si vous ne les avez jamais rencontrés auparavant), nous considérerons quatre contrôles faciles à comprendre. Et là encore, nous nous tournons vers des matériaux complexes - vers les flux.


Composants supplémentaires


Tableau de données


Voir l'image


Commençons notre pause avec le tableau de données. L'étude du code source est laissée au lecteur.


Fenêtre à onglets


Voir l'image


Le prochain arrêt est une fenêtre à onglets. À l'aide d'une simple TabView, vous pouvez placer plusieurs activités sur un seul écran.


Afficher le code
 public class TabsActivity extends TabActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tabs_activity); //  TabHost TabHost tabHost = getTabHost(); TabHost.TabSpec tabSpec; tabSpec = tabHost.newTabSpec("tag1"); tabSpec.setIndicator(""); tabSpec.setContent(new Intent(this, SaveRestorePrefsActivity.class)); tabHost.addTab(tabSpec); tabSpec = tabHost.newTabSpec("tag2"); tabSpec.setIndicator(""); tabSpec.setContent(new Intent(this, FloatingMenuActivity.class)); tabHost.addTab(tabSpec); ... } } 

Affichage des objets de structure: fragment et table


La sortie des objets de structure peut se faire avec des fragments et une table.
Voici à quoi ressemble le remplissage de fragments


Voir l'image


Et donc la table


Voir l'image


Cycle de vie de l'activité


Voir l'image


Finissant progressivement notre pause, nous procédons au cycle de vie de l'activité. Voici le moment de découvrir qu'il existe un certain nombre d'autres méthodes en plus de onCreate. Un petit morceau de code et des notifications contextuelles au début aideront à mieux les comprendre que toute explication.


Afficher le code
 @Override protected void onResume() { super.onResume(); Toast.makeText(getApplicationContext(), "onResume -    ", Toast.LENGTH_SHORT).show(); } @Override protected void onDestroy() { super.onDestroy(); Toast.makeText(getApplicationContext(), "onDestroy -  ", Toast.LENGTH_SHORT).show(); } @Override protected void onPause() { super.onPause(); Toast.makeText(getApplicationContext(), "onPause -      ", Toast.LENGTH_SHORT).show(); } 

Il en va de même pour OnTouchListener'ov et onTextChanged.


Afficher le code
 CheckBox cb = findViewById(R.id.checkBoxChangeExample); cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { printMsg("CheckBox - OnCheckedChangeListener: new value is checked = " + isChecked); } }); SeekBar seekBar = (SeekBar) findViewById(R.id.seekBarChangeExample); seekBar.setMax(100); seekBar.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Integer progress = ((SeekBar)v).getProgress(); printMsg("SeekBar - OnTouchListener: new value is = " + progress.toString()); return false; } }); 

Exécution différée, parallèle et régulière


Nous passons à la partie la plus difficile de l'histoire - l'exécution différée et parallèle.


Exécution différée: gestionnaire


Voir l'image


Commençons la plongée avec Handler. Qu'est-ce qu'un gestionnaire dans tous les détails - une personne le lira plus tard lui-même, nous ne le priverons pas de ce plaisir. Mais, puisque nous l'envisageons, il est important de savoir l'essentiel - qu'il vous permet d'effectuer une tâche en attente, et ne le fait pas en parallèle.
Montrez-le. Créons un gestionnaire, ajoutons-lui la tâche "toast Toast in 5 seconds". Nous voyons que Toast a été retiré et aucun sommeil (pauses dans l'exécution de l'ensemble du programme) n'a été nécessaire.


Afficher le code
 Handler handler = new Handler(); Runnable r = new Runnable() { public void run() { Toast.makeText(getApplicationContext(), "Delayed task executed", Toast.LENGTH_SHORT).show(); } }; handler.postDelayed(r, 5000); 

Ajoutez maintenant une tâche cyclique au gestionnaire:


Afficher le code
 Runnable r = new Runnable() { public void run() { Toast.makeText(getApplicationContext(), "Delayed task executed", Toast.LENGTH_SHORT).show(); handler.postDelayed(this, 5000); //   } }; handler.postDelayed(r, 5000); 

Après vous être assuré qu'il s'exécute toutes les 5 secondes, désactivez le gestionnaire


Afficher le code
 handler.removeCallbacksAndMessages(null); 

Il reste à démontrer que l'exécution des tâches de Handler ne se fait pas en parallèle. La façon la plus simple de le montrer est de le charger avec quelque chose de lourd et en même temps simple. Comme ... tout (vrai) sans sommeil! Dix secondes, pour ne pas tuer complètement l'application.


Afficher le code
 Runnable r = new Runnable() { public void run() { long initTime = System.currentTimeMillis(); boolean timeElapsed = false; while(!timeElapsed){ if(System.currentTimeMillis() - initTime > 10000 ){ timeElapsed = true; //   ,   ! (  ,    ).         sleep } } } }; Toast.makeText(getApplicationContext(), "Hard Delayed task started", Toast.LENGTH_SHORT).show(); handler.postDelayed(r, 100); 

En lançant une telle tâche, nous voyons que l'application ne répond pas à nos clics pendant ces 10 secondes - elle est complètement et complètement occupée à traiter notre cycle complexe. La deuxième conclusion qui découle de cet exemple est que vous ne pouvez pas exécuter de tâches gourmandes en ressources dans le même thread avec l'interface. Le thread d'interface utilisateur doit toujours être libre et ses fonctions doivent être définies le plus rapidement possible. Certaines opérations dans le flux d'interface utilisateur sont explicitement interdites: par exemple, Android plantera l'application s'il essaie d'accéder à Internet dans le flux d'interface utilisateur.


Exécution simultanée: flux


Voir l'image


La question logique est maintenant de savoir comment créer de nouveaux threads et travailler en parallèle?
Nous montrons à la fois la création du flux et le fait de son fonctionnement parallèle. Créons un flux et chargez-le avec la même tâche, à cause de quoi, dans le cas de habdler, nous nous sommes retrouvés avec une interface inactive pendant 10 secondes.


Afficher le code
 Thread thread = new Thread() { @Override public void run() { try { sendMsgUsingBroadcast("Thread started"); long initTime = System.currentTimeMillis(); boolean timeElapsed = false; while(!timeElapsed){ if(System.currentTimeMillis() - initTime > 10000 ){ timeElapsed = true; } } sendMsgUsingBroadcast("Thread stopped"); } catch (Exception e) { sendMsgUsingBroadcast("Thread error " + e.toString()); } } }; thread.start(); 

Créé, chargé - et l'interface fonctionne! L'exécution du thread se produit en parallèle.
La question suivante est de savoir comment contrôler le flux. Il est important de comprendre qu'un thread ne sera terminé que lorsque tout son code source sera exécuté. Vous ne pouvez pas simplement prendre et tuer le flux. Il est temps de penser à shared_prefs et d'appliquer la variable pour la synchronisation: laissez la variable running = true être définie avec le début du flux. Dans chacune de ses itérations, le thread vérifie si l'exécution de == true est en cours d'exécution, et sinon, termine son exécution.


Afficher le code
 Thread thread = new Thread() { @Override public void run() { try { sendMsgUsingBroadcast("Thread started"); SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE); while (true) { if (!prefs.getBoolean("running", false)) { break; } else { try { Thread.sleep(100); } catch (Exception e) {} } } sendMsgUsingBroadcast("Thread stopped"); } catch (Exception e) { sendMsgUsingBroadcast("Thread error " + e.toString()); } } }; thread.start(); 

Nous avons donc franchi quelques étapes et nous connaissons désormais non seulement le service, mais aussi le gestionnaire et le flux. Il semblerait que ces outils soient suffisants pour commencer à écrire une application qui a l'une des fonctions de notification utilisateur régulière. Mais ils ont une caractéristique: tous sont unis par le fait que nous ne perdons pas le contrôle pendant une seconde, et sommes toujours obligés d'être dans une sorte de cycle sans fin qui dort la plupart du temps et se réveille parfois pour vérifier si quelques conditions sont remplies pour comprendre - pour déduire - pour déduire - pour déduire avis de l'utilisateur ou dormir à nouveau pendant une longue période. Cela ajoute un mal de tête: que se passe-t-il si notre service ou application tue l'optimiseur de batterie, et pourquoi consommons-nous autant de ressources de smartphone pour un petit coup de sifflet pas si nécessaire? , , , - , ?


: AlarmManager


AlarmManager.



, — sendBroadcast Action, BroadcastReceiver!


BroadcastReceiver, , , :


 public class MyAlarmServiceReceiver extends BroadcastReceiver { private String CHANNEL_ID = "MyNotificationsChannel"; @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "MyAlarmServiceReceiver onReceive", Toast.LENGTH_SHORT).show(); setNotify("Notify from AlarmManager", context); } } 

AlarmManager Receiver 15 :


 AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); Intent intent = new Intent(AlarmActivity.this, MyAlarmServiceReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 1); calendar.set(Calendar.MINUTE, 10); //alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, 1 * 60 * 1000, pendingIntent); // not repeating - just one run, if needed alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent); 

: 15 .


( ) .




Logcat Reader , .
( ) , .


 String LOG_TAG = "MYDEMOAPP"; Log.i(LOG_TAG, "Test info log"); Log.d(LOG_TAG, "Test debug log"); Log.e(LOG_TAG, "Test error log"); 

, , MainActivity, , .


Code source




Conclusion


, Android. , , , : , . , , - Android-.

Source: https://habr.com/ru/post/fr483878/


All Articles