Toast source code implementation
Toast entrance
When we use the toast prompts in the application, we generally make a simple code call, as shown below:
[Java] View Plaincopyprint? View code tablets on the code derived to my code tablet
Toast.maketext (context, msg, toast.length_short) .show ();
MakeText is the entrance of Toast. We understand the implementation of Toast from the source code of MakeText. The source code is as follows (Frameworks/Base/Core/Java/Android/Widget/Toast.java):
Public Static Toast Maketext (Context Context, CharSequence Text, Int Duration) {Toast Result = New Toast (Context); Layoutinflator Inflating = (layoutinflater ) Context.getSystemService (context.Layout_inflate_Service); view v = Inflate.inflate (com.android. Internet.R.Layout.transient_notification, NULL); TextView TV = (TextView) v.findViewByid (com.android.internal.R.MESSAGE); TV.Settext (Te XT); Result.mnextView = v; Result.mduration = Duration; Return Result;}From the source code of MakeText, we can see that the layout file of Toast is transient_notification.xml, located in Frameworks/Base/Core/Res/Layout/Transition_notification.xml:
<? xml version = "1.0" encoding = "UTF-8"?> <linearlayout xmlns: Android = "http://schemas.android.com/apk/android" android: layout_width = "MATCH_PARENT" Android: layout_height = "MATCH_PARENT" Android: Orientation = "Vertical" Android: Background = "? Android: Attr/ToastframeBackground"> <textView android: ID = "@andd ROID: ID/Message "Android: layout_width =" wrap_content "android: layout_height =" wrap_content "android: layout_weight =" 1 "Android: layout_gravity =" Center_horizontal "android: textapperance ="@style/textAPERANCE.TOAST "" Android: TextColor = "@COLOR/BRIGHT_FOREGROUND_DARK" Android: ShadowColor = "#BB000000" Android: Shadowradius = "2.75" /> < /linearlayout>
The layout file of the system Toast is very simple, that is, a TextView is placed in the linearlayout of the vertical layout. Next, we continue to follow the Show () method to study the display code implementation after the layout is formed:
public void show () {if (mnextView == NULL) {Throw New Runtimeexception ("SetView Must Have Been Called"); CE (); string pkg = mcontext.getpackagename (); tn tn = mtn; tn .mnextView = MnextView; TRY {Service.enqueuetoast (PKG, TN, MDuration);} Catch (RemoteException E) {// Empty}} There are two points in the show method that we need to pay attention to. (1) What is TN? (2) The role of InotificationManager service. With these two problems, continue our toast source code.
TN source code
Many questions can find the answer through the source code. The key is whether you have the patience and persistence that matches you. The implementation of the MTN in the constructor of Toast, the source code is as follows:
Public Toast (Context Context) {mcontext = context; mtn = new tn (); mtn.my = context.getResources (). GetDimensionPixelsize (com.android.Internal.r. dimen.toast_y_offset); mtn.mgravity = context.getResources () .getInteger (com.android.internal.R.Iinteger.config_toastDefaultGravity);}Next, we start from the source code of the TN class to explore the role of TN. The TN source code is as follows:
Private Static Class Tn Extends ItransiteNotification.stub {Final Runnable MSHOW = New Runnable () {@Override Public void Run () {Handless ();}; FINAL RUNNABLE MHIDE = New Runnable () {@Override Public Void Run () { handlehide (); //d't do this in handlehide () BeCAUSE It is Also Invoked by Handleshow () MnextView = Null;}; s mparams = New WindowManager.Layoutparams (); Final Handler Mhandler = New Handler (); int Mgravity; Int mx, my; flow mhorizontalMargin; flow mVIEW MVIEW; view mnextView; WindowManager mwm; tn () {//XXX th is Should Be Changed to use a dialog, with a theme.toast // Defined that sets up the layout params appropriately. Final WindowManager.Layoutparams Params = mparams; _Content; Params.width = WindowManager.Layoutparams.wrap_Content; Params.Format = Pixelformat.translocent; Params.windowanimations = COM .android.Internal.R. Style.animation_Toast; Params.Type = WindowManager.Layoutparams.type_toast; Params.Settital ("Toast"); Params.flags = WindowMana Ger.Layoutparams.flag_Keep_Screen_on | WindowManager.Layoutparams.flag_not_focusable | WindowManager.Layoutparams. Flag_not_touch; /// M: [Alps00517576] Support Multi-User Params.privateFlags = WindowManager.layoutparams.private_Flag_For_ALL_USERS; } / ** * schedule handleshow into the right thread * / @Override Public Void Show () {if (Locallogv) log .v (tag, "show:" + this); mhandler.post (mshow);} / ** * schedule handlehide insto the right thread * / @Override Public Void Hide () {if (LOCALLOGV ) Log.v (tag (tag "Hide:" + this); mhandler.post (mhide);} Public void handleshow () {if (localLogv) log.v (tag, "Handle Show:" + This + "mview =" + mview + " xtview = " + mnextView); if (mView! = MnextView) {// Remove the Old View If NECESSARY HANDLEHIDE (); mview = mnextView; Context Context = mview.getConton ext (). GetApplicationContext (); if (context == null) {context = mView.getContext ();} mwm = (windowManager) context.getSystemService (context.window_service); // We can resolve the gravity here by usin g the local for getting // The layout Direction final configuration config = mView.getContext () .getResources (). GetConfiguration (); Final int Gravity = Gravity.getabsolutegravity (mgravity, config.getLayoutDirection ()); mparams.grav. ITY = Gravity; if ((Gravity & Gravity.Horizontal_gravity_Mask) == Gravity.fill_horizontal) { mparams.Horizontalweight = 1.0F;} if (Gravity & Gravity.Vertical_gravity_Mask) == Gravity.fill_vertical) {mparams.verticalweight = 1.0 f;} mparams.x = mx; mparams.y = my; mparams.verticalMargin = mverticalMargin; mparams.HorizontalMargin = mhorizontalMargin; if (mview.getparent ()! = Null) {if (localLogv) log.v (tag, "remov!" + mview + " + this); mwm.re moveView (mview);} if (localLogv) log.v (tag, "add!" + mview + "in" + this); mwm.addview (mview, mparams); Trysendac senseEvent ();}}}} DaccessibilityEvent () {AccessibilityManager AccessibilityManager = AccessibilityManager. GetinStance (mview.getContext ()); if (! AccessibilityManager.isenabled ()) {Return;} // Treat Toasts As Notifications SINCE The Use TO // A Transient Piece of Information to the User AccessibilityEvent Event = AccessibilityEvent.obtain.obtain ; ) .getPackAgename ()); mview.dispatchpopultAccessibilityEvent (event); accessibilityManager.Sendac sense (event);} Public void handlehide () {if (localLogv) log.v (tag, "handle hide:" + this + "mview =" + mview); if (mview! = null) {// not: checking paint () just to mak. E Sure the view has // ben added ... I have seen cases where we get here where when // the view isn't yet added, so let's try not to crash. If (mview.getparent ()! = Null ) {if ; Through the source code, we can obviously see the inheritance relationship. The TN class inherits from the iTransientNotification.stub for inter -process communication. It is assumed that readers have the basis of communication between Android process (not well -known to learn a series of blogs of Luo Shengyang's communication on Binder process communication). Since TN is used for inter -process communication, it is easy for us to think that the specific role of the TN class should be the callback object of the Toast class. Other processes operate the display and disappearance of Toast by calling the specific objects of the TN class.
The TN class is inherited from iTransiteNotification.stub, itransientnotification.aidl is located in Frameworks/Base/Core/Java/APP/iTransientnotification.Aidl, the source code is as follows: as follows:
package android.app; / ** @Hide* / Oneway Interface ItRansientNotification {void show (); void hide ();}ITransientnotification defines two methods show () and hide (), and their specific implementation is in the TN class. The implementation of the TN class is:
/ ** * schedule handleshow into the right thread */ @override public void show () {if (localLogv) log.v (tag, "show:" + this); ;} /*** Schedule handlehide into the right Thread */ @Override Public Void Hide () {if (LOCALLOGV) log.v (tag, "hide:" + this); mhandler.post (mhide);}Here we can know that TOAST ’s show and hide method implementation is based on the Handler mechanism. The Handler implementation in the TN class is:
FINAL HANDL MHANDLER = New Handler ();
Moreover, we did not find any Looper.perpare () and Looper.Loop () methods in the TN class. It shows that Mhandler calls the Looper object of the current thread. Therefore, when we are in the main thread (that is, in the UI thread), we can call the Toast.MakeText method at will, because the Android system helps us to realize the Looper initialization of the main thread.但是,如果你想在子线程中调用Toast.makeText方法,就必须先进行Looper初始化了,不然就会报出java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() Essence The learning of the handler mechanism can refer to a blog I wrote before: http://blog.csdn.net/wzy_1988/article/details/38346637.
Next, continue to follow the implementation of MSHOW and MHIDE, both of which are runnable.
FINAL RUNNABLE MSHOW = New Runnable () {@Override Public Void Run () {handleshow ();}}; oid run () {handlehide (); //don'T Do this in handlehide () Becauuse it is also invoked by handleshow () mnextView = null;}}; It can be seen that the real implementation of Show and HIDE is to call Handleshow () and Handlehide () methods, respectively. Let's look at the specific implementation of handleshow ():
public void handleshow () {if (mview! = mnextView) {// Remove the Old View If NECESSARY HANDLEHIDE (); mView = mnextView; context context = mview.g etContext (). GetApplicationContext (); if (context == null) {context = mView.getContext ();} mwm = (windowManager) context.getSystemService (context.window_service); // We can resolve the gravity here by usin g the local for getting // The layout Direction final configuration config = mView.getContext () .getResources (). GetConfiguration (); Final int Gravity = Gravity.getabsolutegravity (mgravity, config.getLayoutDirection ()); mparams.grav. ITY = Gravity; if ((Gravity & Gravity.Horizontal_gravity_Mask) == Gravity.fill_horizontal) { mparams.Horizontalweight = 1.0F;} if (Gravity & Gravity.Vertical_gravity_Mask) == Gravity.fill_vertical) {mparams.verticalweight = 1.0 f;} mparams.x = mx; mparams.y = my; mparams.verticalMargin = mverticalMargin; mparams.HorizontalMargin = mhorizontalMargin; if (mView.getparent ()! = Null) {mwm.removeView (mview);} mwm.addview (mView, Mparams); Trysendac cessibilityEvent ();}} From the source code, we know that Toast is loaded in adDView through WindowManager. Therefore, the Hide method is naturally WindowManager to call the RemoveView method to remove the Toast view.
To sum up, by analyzing the source code of the TN class, we know that the TN class is a callback object, and other processes call the Show and Hide method of the TN class to control the display and disappearance of this toast.
NotificationManagerService
Back to the SHOW method of the Toast class, we can see that the getService is called here to get the InotificationManager service. The source code is as follows:
Private Static InotificationManager SSERVICE; Static Private InotificationManager Getservice () {if (SSERVICE! = Null) {Return SSERVICE;} SSERVICE = in OTIFINAGER.STUB.ASINTERFACE (ServiceManager.getService ("Notification"); Return SSREVICE;} After getting the InotificationManager service, the ENQueuetoast method was called to put the current Toast into the system's Toast queue. The parameters of the pass are PKG, TN and MDuration. In other words, we use Toast.maketext (context, msg, toast.Length_Show) .Show () to present a Toast. This toast is not immediately displayed on the current Window, but first enters the toast queue of the system, and then The system calls the Show and Hide method of the callback object TN to display and hide the toast.
The specific implementation class of the INOFITICANAGER interface here is the NotificationManageRSERVICE class, located in Frameworks/Base/Services/Java/COM/Server/NotificationManagerVice.java.
First of all, let's analyze the functions of Toast's entry into the enqueuetoast. The source code is as follows:
Public Void ENQEUETOAST (String PKG, iTransientnotification Callback, Int Duration) {// PackageName is NULL or TN class as NULL. allback == null) {return;} // (1) Determine whether it is toast Final Boolean issuesToast = iScalLersystem () || ("Android" .equals (pkg)); // Determine whether the pkg of the current toast is not allowed to occur in the system. Service has a hashset Data structure, the package name IF (enable_blockd_toasts &&TOASTS &&! NoteNotificationop (PKG, Binder.GetCallinguid ()) &&! {if (! Issystemtoast) {Return;}} synchronized (mtoastQueue) { int CallingPid = binder.getCalllingPid (); Long callingid = binder.ClearCallingIdity (); Try {ToastRecord Record; // (2) View whether the Toast has already Int INDEX = i ndexoftoastLocked (pkg, callback); // If toast In the queue, we only need to update the display time if (index> = 0) {record = mtoastQueue.get (index); record.update (duration);} else {// non -system toast, each PKG is in At present, the total number of Toast in MTOASTQUEUE cannot exceed max_package_notifications if (! Issystemtoast) {int count = 0; final int n = mtoastQueue.size (); for (int i = 0; I < N; I ++) {final toastrecord r = mtoastQueue.get (i); if (R.PKG.Equals (PKG)) {count ++; if (count> = max_package_notifications) {slog.e (tag, "Package has almedy posted" + Count + "Toasts. Not Showing More . Package = " + pkg); Return;}}} // encapsulates toast to ToastRecord objects and puts it in MTOASTQueue. astqueue.add (record); index = mtoastQueue.size () - 1; // (3) Set the current Toast process as the front desk procedural KeepProcessaliveLocked (callingpid);} // (4) If the index is 0, it means that the current entry team is at the team. Call the ShownextToastLocked method directly to display if (index == 0) {shownextToastLocked ();} Finally {binder.ReastoreCallingIdity (Callingid);}}}}} It can be seen that I made a brief comment on the above code. The code is relatively simple, but there are 4 points of note code that requires us to discuss further.
(1) Determine whether it is the system toast. If the package name of the current toast is "Android", it is the system toast, otherwise you can also call the iScalLersystem () method to judge. The implementation source code of this method is:
Boolean IsuidSystem (int UID) {Final Int Appid = Userhandle.getAppid (UID); Return (Appid == PROCESS.System_UID || Appid == | uID == 0);} Boolean isCallersystem () {Return isUidSystem (Binder.getCallinguid ());} The source code of iScallersystem is relatively simple, that is, to determine whether the UID of the current Toast process is one of the System_UID, 0, Phone_uid, if it is, it is the system toast; if not, it is not the system toast.
Whether it is a system toast, read through the source code below, it can be seen that there are two main advantages:
The system Toast can definitely enter the system toast queue, and it will not be stopped by the blacklist.
The system Toast does not have the quantity limit in the system Toast queue, while the Toast sent by ordinary PKG has the quantity limit in the system Toast queue.
(2) See if the Toast to be entrusted to the team is already in the system toast queue. This is achieved by comparing PKG and Callback. The specific source code is shown below:
Private Int IndexOFTOASTLOCKED (String PKG, iTransientNotification Callback) {iBinder Cbak = callback.asbinder (); ArrayList <TOASTRECORD> List = mtoastq UEUE; int Len = list.size (); for (int i = 0; i <len; i ++) {ToastRecord R = List.get (i); if (R.PKG.Equals (PKG) && R.CallBack.asbinder () == Cbak) {Return I;}} Return -1;} Through the above code, we can draw a conclusion that as long as the Toast's PKG name is consistent with the TN object, the system considers these toast as the same toast.
(3) Set the current Toast process as the front desk processing. The source code is shown below:
Private Void KeepProcessaliveLocked (int Pid) {int toastCount = 0; // toasts from this pid arraylist <TOASTRECORD> List = MTOASTQUEUE; e (); for (int i = 0; i <n; i ++) { TOASTRECORD R = list.get (i); if (r.pid == pid) {toastCount ++;}} Try {mam.setProcessForeground (mForegrounttoken, PID, ToastCount> 0);} Cat. ch (remoteException E) {// Shouldn ' t happen.}} The mam = activityManagernative.getDefault () here calls the setProcessForeground method to place the current PID process into the front desk processing to ensure that it will not be killed systematically. This also explains why when we Finish currently activity, Toast can also be displayed because the current process is still executed.
(4) When the index is 0, the toast of the queue head is displayed. The source code is as follows:
Private Void ShownextToastLocked () {// Get the toastRecord toastRecord Record = mtoastQueue.get (0); While (Record! = NULL) {TRY {// calls the Syllar recovery object in TOAST. How method shows the TOAST. callback.show (); scheduletimeoutLocked (record); Return;} Catch (RemoteException E) {slog.w (tag, "object died to show notification" + Record.callback + "in Package" + Record.pkg); / / Remove it from the list and let the procese int index = mtoastQueue.indexof (record); if (index> = 0) {mtoastQueue.remove (index);} essaliveLocked (record.pid); if (mtoastQueue.size )> 0) {record = mtoastQueue.get (0);} else {record = null;}}}}The Callback object of Toast is the TN object. Next, let's take a look, why the system toast's display time can only be 2s or 3.5s, the key is the implementation of the ScheduletimeOutLocked method. The principle is that after calling the Show method of TN to display Toast, you need to call the ScheduletMelockLocked method to disappear toast. (If you have any questions: not to say that the HIDE method of the TN object to disappear Toast, why call the scheduletimeOutLocked method here to disappear toast? Because as soon as the HIDE method of the TN class is executed, the Toast disappears immediately, and we usually have our office.使用的Toast都会在当前Activity停留几秒。如何实现停留几秒呢?原理就是scheduleTimeoutLocked发送MESSAGE_TIMEOUT消息去调用tn对象的hide方法,但是这个消息会有一个delay延迟,这里也是用了Handler消息机制)。
Private Static Final Int Long_delay = 3500; // 3.5 Seconds Private Static Final Int Short_Dlay = 2000; // 2 Seconds Private Void d r) {mhandler.removeCallBacksandMessages (R); Message m = Message.obtain (mHandler, Message_timeout, R); Long Delay = R.Duration == Toast.Length_long? Long_dlay: short_dlay; mhandler.SendmessageDlayed (m, delay);} First of all, we saw that this here did not send the message_timeout message directly, but there was a delay of delay. The time of delay from "Long Delay = R.Duration == Toast.Length_long? Long_dlay: short_dlay;" It can only see that it can only be 2s or 3.5s, which also explains why the system of the system toast can only be 2S Or 3.5s. It is useless to pass a duration in the toast.maketext method at will.
Next, let's take a look at how MESSAGE_TIMEOUT messages are processed in Workerhandler. The type of Mhandler object is workerhandler, the source code is as follows:
Private Final Class Workerhandler Extends Handler {@Override Public Void Handlemessage (MESSAGE MSG) {Switch (msg.what) {Case Message_timeout: Handleti Meout (toastrecord) msg.obj; break;}}It can be seen that the message processing of the workrhandler to the Message_Timeout type is to call the handlertimeout method. Then we continue to track the source code of handletimeout:
Private Void Handletimeout (ToastRecord Record) {Synchronized (mtoastQueue) {int index = IndexoftoAstLocked (record.pkg, record.callback); if (IND ex> = 0) {CanceltoastLocked (INDEX);}}}In the handletimeout code, first determine whether the ToastRecord object that needs to be disappeared is in the queue. If in the queue, call the CanceltoastLocked (index) method. The truth is about to appear in front of us, continue to track the source code:
Private Void CancelToastLocked (int index) {ToastRecord Record = mtoastQueue.get (index); Try {recoverback.hide ();} Catch (RemoteException E ) {//d't worry about this, we're about to remove it from // The list anyway} mtoastQueue.remove (index); KeepProcessaliveLocked (record.pid); if (mtoastQueue.size ()> 0) {// show the next one. f the callback fails, this will remove // it from the list, so do donforms that the list has't Changed // after this point. ShownextToastLocked (); Haha, see here, the HIDE method of our callback object has also been called, and it is also removed from MTOASTQUEUE from MtoastQueue. At this point, the complete display and disappearance of Toast is over.