Implémentation du code source de toast
Entrée de toast
Lorsque nous utilisons les invites de toast dans l'application, nous faisons généralement un appel de code simple, comme indiqué ci-dessous:
[Java] Voir PlainCopyPrint?
Toast.makeText (contexte, msg, toast.length_short) .show ();
MAKETEXT est l'entrée de Toast. Le code source est le suivant (frameworks / base / core / java / android / widget / toast.java):
Public Static Toast makeText (contexte de contexte, texte de char à carter, durée int) {Toast Result = new Toast (context); LayoutinLator gonflante = (Layoutinflater) Internet.r.layout.Transient_Notification, null); Durée; résultat de retour;}À partir du code source de maketext, nous pouvons voir que le fichier de mise en page de Toast est transitoire_notification.xml, situé dans des frameworks / base / core / res / disposition / transition_notification.xml:
<? Match_parent "Android: orientation =" vertical "Android: background ="? "wrap_content" Android: Layout_weight = "1" Android: Layout_gravity = "Center_Horizontal" Android: TextAspave Shadowradius = "2,75" /> </ linearlayout>
Le fichier de mise en page du toast système est très simple, c'est-à-dire qu'un TextView est placé dans le linéaire de la disposition verticale. Ensuite, nous continuons à suivre la méthode Show () pour étudier l'implémentation du code d'affichage après la formation de la mise en page:
public void show () {if (mnextView == Null) {New RuntimeException ("setView doit avoir été appelé"); CE (); ; Il y a deux points dans la méthode des spectacles auxquels nous devons faire attention. (1) Qu'est-ce que TN? (2) Le rôle du service Inotification deManager. Avec ces deux problèmes, continuez notre code source de toast.
Code source TN
De nombreuses questions peuvent trouver la réponse via le code source. L'implémentation du MTN dans le constructeur du pain grillé, le code source est le suivant:
Public Toast (contexte de contexte) {MCONTTAUX = contexte; () .getInteger (com.android.internal.r.iinteger.config_toastdefaultgravity);}Ensuite, nous commençons par le code source de la classe TN pour explorer le rôle de TN. Le code source TN est le suivant:
La classe statique privée TN étend iTransItenotification.Stub {final runnable mshow = new Runnable () {@Override public void run () {Handless ();}; (); ; Propriétaire de la fenêtre finale. WindowManager.layoutParams.type_toast; Params.PrivateFlags = WindowManager.layoutParams.private_flag_for_all_users; .Post (mshow);} / ** * Planifier la poignée de poignée INSTO Le fil droit * / @Override public void hide () {if (locallogv) log.v (tag (tag "hide:" + this); mhandler.post ( mHide);} public void handleshow () {if (locallogv) log.v (tag, "manche show:" + this + "mview =" + mview + "xtView =" + mnextView); {// Supprimez l'ancienne vue si nécessaire HandleHide (); (WindowManager) context.getSystemService (context.window_service); Final Int Gravity = Gravity.getabsoluteGravity (mgravity, config.getLayoutDirection (); (Gravity & Gravity.vertical_gravity_mask) == Gravity.fill_vertical) {Mparams.verticalweight = 1.0 f;} Mparams.x = mx; getParent ()! Ajouter! "+ mview +" dans "+ this); mwm.addView (mview, mparams); trySendac senseevent ();}}} daccessibilityEvent () {AccessibilityManager accessibilityManager = AccessibilitéManager. GetInstance (mview.getContext (); (! AccessibilityManager.IsEnabled ()) {return;} // Traitez les toasts comme des notifications puisque l'utilisation de // une information transitoire à l'utilisateur AccessibilityEvent = accessoireevent.obtain.obtain;) .getPackageName ()); DispatchPopultAccessabilityEvent (événement); accessoirementManager.Sendac Sense (événement);} public void handlehide () {if (locallogv) log.v (tag, "manche:" + this + "mview =" + mview); = null) {// pas: vérifier la peinture () juste pour faire. Essayez de ne pas planter. Grâce au code source, nous pouvons évidemment voir la relation d'héritage. On suppose que les lecteurs ont la base de la communication entre le processus Android (pas bien connu pour apprendre une série de blogs de la communication de Luo Shengyang sur la communication du processus de liant). Étant donné que TN est utilisé pour la communication inter-process, il est facile pour nous de penser que le rôle spécifique de la classe TN devrait être l'objet de rappel de la classe de toast. la classe TN.
La classe TN est héritée de iTransItenotification.Stub, iTransIentNotification.Aidl est située dans Frameworks / Base / Core / Java / App / iTransIentNotification.aidl, le code source est le suivant: comme suit:
package Android.App;
ITransIentNotification définit deux méthodes Show () et Hide (), et leur implémentation spécifique est dans la classe TN. L'implémentation de la classe TN est:
/ ** * Planifiez les poignées dans le fil droit * / @Override public void show () {if (locallogv) log.v (tag, "show:" + this);} / *** schéma handlehide dans le fil de file droit du fil droit * / @Override public void hide () {if (locallogv) log.v (tag, "hide:" + this); mhandler.post (mhide);}Ici, nous pouvons savoir que la mise en œuvre de la méthode des émissions et masqués de Toast est basée sur le mécanisme du gestionnaire. L'implémentation du gestionnaire dans la classe TN est:
Final handl mhandler = new handler ();
De plus, nous n'avons trouvé aucune méthode Looper.perpare () et Looper.loop () dans la classe TN. Il montre que MHandler appelle l'objet LOOPER du thread actuel. Par conséquent, lorsque nous sommes dans le thread principal (c'est-à-dire dans le thread d'interface utilisateur), nous pouvons appeler la méthode Toast.maketext à volonté, car le système Android nous aide à réaliser l'initialisation de Looper du thread principal.但是 , 如果你想在子线程中调用 Toast.makeText 方法 , 就必须先进行 LOOPER 初始化了 , 不然就会报出 Java.lang.runtimeException: Impossible de créer un gestionnaire à l'intérieur qui n'a pas appelé looper.prepare () Essence L'apprentissage du mécanisme du gestionnaire peut se référer à un blog que j'ai écrit avant: http://blog.csdn.net/wzy_1988/article/details/38346637.
Ensuite, continuez à suivre la mise en œuvre de Mshow et MHide, qui sont tous deux possibles.
Final runnable mshow = new runnable () {@Override public void run () {handlesHow ();}}; handlesHow () mNextView = null;}}; On peut voir que la véritable implémentation de Show and Hide consiste à appeler des méthodes HandlesHow () et HandleHide (), respectivement. Examinons la mise en œuvre spécifique de HandlesHow ():
public void handleshow () {if (mview! = mNextVie ) {context = mview.getContext ();} mwm = (windowManager) context.getSystemService (context.window_service); .getcontext () .getResources (). .Fill_horizontal) {Mparams.horizontalweight = 1.0f;} if (Gravity & Gravity.vertical_gravity_mask) == Gravity.fill_vertical) {Mparams.verticalweight = 1.0 f;} Mparams.x = mx; = MVERTICALAGE; À partir du code source, nous savons que Toast est chargé dans AddView via WindowManager. Par conséquent, la méthode de masquer est naturellement WindowManager pour appeler la méthode de supprimer pour supprimer la vue de toast.
Pour résumer, en analysant le code source de la classe TN, nous savons que la classe TN est un objet de rappel, et d'autres processus appellent la méthode Show et Masquer de la classe TN pour contrôler l'affichage et la disparition de ce toast.
NotificationManagerservice
Retour à la méthode Show de la classe Toast, nous pouvons voir que le Getservice est appelé ici pour obtenir le service InotificationManager.
Inotification statique privée SService; inotification privée statique getService () {if (sService! = Null) {return sService;} sService = dans otifinager.stub.asinterface (ServiceManager.getService ("notification"); return ssrevice;} Après avoir obtenu le service InotificationManager, la méthode EnqueUetoast a été appelée pour mettre le toast actuel dans la file d'attente de toast du système. Les paramètres du PASS sont PKG, TN et Mduration. En d'autres termes, nous utilisons Toast.makeText (contextuel, msg, toast.length_show) .show () pour présenter un toast. Le système appelle la méthode Show et Masquer de l'objet de rappel TN pour afficher et masquer le toast.
La classe d'implémentation spécifique de l'interface InofiCanager ici est la classe NotificationManagerservice, située dans Frameworks / Base / Services / Java / Com / Server / NotificationManagerVice.java.
Tout d'abord, analysons les fonctions de l'entrée de Toast dans l'Enqueuetoast.
Public void enqeUetoast (String pkg, itransientNotification rappel, int Durée) {// packageName est null ou tn class comme null. ) || ("Android". Equals (PKG)); (Pkg, binder.getCallinguid ()) &&! {If (! Issystemtoast) {return;}} synchronisé (mtoastqueue) {int CallingPid = Binder.getCallingPid (); // (2) Affichez si le toast a déjà Int Index = i nDExoftoastLocked (pkg, callback); get (index); record.update (durée);} else {// non-toast, chaque pkg est actuellement, le nombre total de toast dans mtoastqueue ne peut pas dépasser max_package_notifications if (! issystemtoast) {int county = 0; final int n = mtoastqueue.size (); + +; ToasterCord Objets et le place dans MtOastQueue. ) Si l'index est 0, cela signifie que l'équipe d'entrée actuelle est dans l'équipe. }}} On peut voir que j'ai fait un bref commentaire sur le code ci-dessus. Le code est relativement simple, mais il y a 4 points de code de note qui nous oblige à discuter davantage.
(1) Déterminez s'il s'agit du toast système. Si le nom du package du toast actuel est "Android", c'est le toast système, sinon vous pouvez également appeler la méthode isCallersystem () à juger. Le code source d'implémentation de cette méthode est:
Boolean ISUIDSystem (int uid) {final int appid = userHandle.getAppid (uid); ());} Le code source d'IsCallersystem est relativement simple, c'est-à-dire pour déterminer si l'UID du processus de toast actuel est l'un des System_UID, 0, Phone_UID, si c'est le cas, c'est le toast système; griller.
Qu'il s'agisse d'un toast système, lisez le code source ci-dessous, on peut voir qu'il existe deux avantages principaux:
Le toast système peut certainement entrer dans la file d'attente de toast système, et il ne sera pas arrêté par la liste noire.
Le toast système n'a pas la limite de quantité dans la file d'attente de toast système, tandis que le toast envoyé par PKG ordinaire a la limite de quantité dans la file de toast système.
(2) Voir si le toast à configurer à l'équipe est déjà dans la file d'attente du toast système. Ceci est réalisé en comparant le PKG et le rappel.
Private indexoftoastlocked (String pkg, itransientNotification rappeler) {ibinder cbak = callback.asbinder (); ++) {toastecord r = list.get (i); Grâce au code ci-dessus, nous pouvons tirer une conclusion que tant que le nom PKG du toast est cohérent avec l'objet TN, le système considère ces toasts comme le même toast.
(3) Définissez le processus de toast actuel comme traitement de la réception. Le code source est illustré ci-dessous:
Private void keepprocessalivellocked (int pid) {int toastCount = 0; list.get (i); if (r.pid == pid) {toastCount ++;}} try {mam.setProcessForeground (mforegrountToken, pid, toastCount> 0);} cat. 'ne se produit pas.}} Le mam = activitymanagernative.getDefault () appelle ici la méthode SetProcessForeground pour placer le processus PID actuel dans le traitement de la réception pour s'assurer qu'il ne sera pas tué systématiquement. Cela explique également pourquoi lorsque nous terminons actuellement l'activité, le toast peut également être affiché car le processus actuel est toujours exécuté.
(4) Lorsque l'index est 0, le toast de la tête de file d'attente s'affiche. Le code source est le suivant:
VOID PRIVÉEDEXTTOASTLOCKED () {// Obtenez le ToasterCord Record = MtOastQueue.get (0); (); La liste et le PROCEse int index = mtoastqueue.indexof (enregistrement); = mtoastqueue.get (0);} else {enregistre = null;}}}}L'objet de rappel de Toast est l'objet TN. Ensuite, jetons un coup d'œil à la raison pour laquelle l'heure d'affichage du toast du système ne peut être que 2s ou 3,5s, la clé est la mise en œuvre de la méthode ScheduleTimeoutClockée. Le principe est qu'après avoir appelé la méthode Show de TN pour afficher des toasts, vous devez appeler la méthode ScheduleTMellocked pour disparaître. (Si vous avez des questions: ne pas dire que la méthode de masquer de l'objet TN à disparaître du toast, pourquoi appeler la méthode de scheduletime , et nous avons généralement notre bureau. 使用的 Toast 都会在当前 Activité 停留几秒。如何实现停留几秒呢?原理就是 ScheduleTimeoutClocked 发送 message_timeout 消息去调用 tn 对象的 cache 方法 , 但是这个消息会有一个 délai 延迟 , 这里也是用了 Handler 消息机制)。
Final statique privé Long_delay = 3500; Long Delay = R.Duration == Toast.Length_Long?
Tout d'abord, nous avons vu que cela ici n'a pas envoyé le message Message_timeout directement, mais il y avait un retard de retard. Le temps de retard de "Long Delay = R.Duration == Toast.Length_Long? Long_dlay: Short_Dlay;" 2s ou 3,5s. Il est inutile de passer une durée dans la méthode Toast.makeText à volonté.
Ensuite, jetons un coup d'œil à la façon dont les messages Message_timeout sont traités dans WorkerHandler. Le type d'objet Mhandler est WorkerHandler, le code source est le suivant:
Classe finale privée WorkerHandler étend Handler {@Override public void handlessage (message msg) {switch (msg.what) {case message_timeout: handleti meout (toastecord) msg.obj;}}}On peut voir que le traitement des messages de WorkrHandler dans le type Message_timeout est d'appeler la méthode HandlerTimeout.
Private void handleTimeout (toastecord enregistre) {synchronisé (mtoastQueue) {int index = indexoftoastLocked (enregistre.pkg, enregistrement.callback);Dans le code HandleTimeout, déterminez d'abord si l'objet ToasterCord qui doit être disparu est dans la file d'attente. La vérité est sur le point d'apparaître devant nous, continue de suivre le code source:
Private void canceltoastlocked (int index) {toastercord record = mtoastqueue.get (index); // La liste de toute façon} mtoastqueue.remove (index); La liste, il en va de même pour que la liste n'ait pas changé // après ce point. Haha, voir ici, la méthode Hide de notre objet de rappel a également été appelée, et elle est également supprimée de Mtoastqueue de Mtoastqueue. À ce stade, l'affichage complet et la disparition du pain grillé sont terminés.