<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                #### 8.3.3 Toast的Window創建過程 Toast和Dialog不同,它的工作過程就稍顯復雜。首先Toast也是基于Window來實現的,但是由于Toast具有定時取消這一功能,所以系統采用了Handler。在Toast的內部有兩類IPC過程,第一類是Toast訪問NotificationManagerService,第二類是Notification-ManagerService回調Toast里的TN接口。關于IPC的一些知識,請讀者參考第2章的相關內容。為了便于描述,下面將NotificationManagerService簡稱為NMS。 Toast屬于系統Window,它內部的視圖由兩種方式指定,一種是系統默認的樣式,另一種是通過setView方法來指定一個自定義View,不管如何,它們都對應Toast的一個View類型的內部成員mNextView。Toast提供了show和cancel分別用于顯示和隱藏Toast,它們的內部是一個IPC過程,show方法和cancel方法的實現如下: public void show() { if (mNextView == null) { throw new RuntimeException("setView must have been called"); } INotificationManager service = getService(); String pkg = mContext.getOpPackageName(); TN tn = mTN; tn.mNextView = mNextView; try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } } public void cancel() { mTN.hide(); try { getService().cancelToast(mContext.getPackageName(), mTN); } catch (RemoteException e) { // Empty } } 從上面的代碼可以看到,顯示和隱藏Toast都需要通過NMS來實現,由于NMS運行在系統的進程中,所以只能通過遠程調用的方式來顯示和隱藏Toast。需要注意的是TN這個類,它是一個Binder類,在Toast和NMS進行IPC的過程中,當NMS處理Toast的顯示或隱藏請求時會跨進程回調TN中的方法,這個時候由于TN運行在Binder線程池中,所以需要通過Handler將其切換到當前線程中。這里的當前線程是指發送Toast請求所在的線程。注意,由于這里使用了Handler,所以這意味著Toast無法在沒有Looper的線程中彈出,這是因為Handler需要使用Looper才能完成切換線程的功能,關于Handler和Looper的具體介紹請參看第10章。 首先看Toast的顯示過程,它調用了NMS中的enqueueToast方法,如下所示。 INotificationManager service = getService(); String pkg = mContext.getOpPackageName(); TN tn = mTN; tn.mNextView = mNextView; try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } NMS的enqueueToast方法的第一個參數表示當前應用的包名,第二個參數tn表示遠程回調,第三個參數表示Toast的時長。enqueueToast首先將Toast請求封裝為ToastRecord對象并將其添加到一個名為mToastQueue的隊列中。mToastQueue其實是一個ArrayList。對于非系統應用來說,mToastQueue中最多能同時存在50個ToastRecord,這樣做是為了防止DOS(Denial of Service)。如果不這么做,試想一下,如果我們通過大量的循環去連續彈出Toast,這將會導致其他應用沒有機會彈出Toast,那么對于其他應用的Toast請求,系統的行為就是拒絕服務,這就是拒絕服務攻擊的含義,這種手段常用于網絡攻擊中。 // Limit the number of toasts that any given package except the android // package can enqueue. Prevents DOS attacks and deals with leaks. 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 already posted " + count + " toasts. Not showing more. Package=" + pkg); return; } } } } 正常情況下,一個應用不可能達到上限,當ToastRecord被添加到mToastQueue中后,NMS就會通過showNextToastLocked方法來顯示當前的Toast。下面的代碼很好理解,需要注意的是,Toast的顯示是由ToastRecord的callback來完成的,這個callback實際上就是Toast中的TN對象的遠程Binder,通過callback來訪問TN中的方法是需要跨進程來完成的,最終被調用的TN中的方法會運行在發起Toast請求的應用的Binder線程池中。 void showNextToastLocked() { ToastRecord record = mToastQueue.get(0); while (record ! = null) { if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record. callback); try { record.callback.show(); scheduleTimeoutLocked(record); return; } catch (RemoteException e) { Slog.w(TAG, "Object died trying to show notification " + record. callback + " in package " + record.pkg); // remove it from the list and let the process die int index = mToastQueue.indexOf(record); if (index >= 0) { mToastQueue.remove(index); } keepProcessAliveLocked(record.pid); if (mToastQueue.size() > 0) { record = mToastQueue.get(0); } else { record = null; } } } } Toast顯示以后,NMS還會通過scheduleTimeoutLocked方法來發送一個延時消息,具體的延時取決于Toast的時長,如下所示。 private void scheduleTimeoutLocked(ToastRecord r) { mHandler.removeCallbacksAndMessages(r); Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; mHandler.sendMessageDelayed(m, delay); } 在上面的代碼中,LONG_DELAY是3.5s,而SHORT_DELAY是2s。延遲相應的時間后,NMS會通過cancelToastLocked方法來隱藏Toast并將其從mToastQueue中移除,這個時候如果mToastQueue中還有其他Toast,那么NMS就繼續顯示其他Toast。 Toast的隱藏也是通過ToastRecord的callback來完成的,這同樣也是一次IPC過程,它的工作方式和Toast的顯示過程是類似的,如下所示。 try { record.callback.hide(); } catch (RemoteException e) { Slog.w(TAG, "Object died trying to hide notification " + record.callback + " in package " + record.pkg); // don't worry about this, we're about to remove it from // the list anyway } 通過上面的分析,大家知道Toast的顯示和影響過程實際上是通過Toast中的TN這個類來實現的,它有兩個方法show和hide,分別對應Toast的顯示和隱藏。由于這兩個方法是被NMS以跨進程的方式調用的,因此它們運行在Binder線程池中。為了將執行環境切換到Toast請求所在的線程,在它們的內部使用了Handler,如下所示。 /** * schedule handleShow into the right thread */ @Override public void show() { if (localLOGV) Log.v(TAG, "SHOW: " + this); mHandler.post(mShow); } /** * schedule handleHide into the right thread */ @Override public void hide() { if (localLOGV) Log.v(TAG, "HIDE: " + this); mHandler.post(mHide); } 上述代碼中,mShow和mHide是兩個Runnable,它們內部分別調用了handleShow和handleHide方法。由此可見,handleShow和handleHide才是真正完成顯示和隱藏Toast的地方。TN的handleShow中會將Toast的視圖添加到Window中,如下所示。 mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); mWM.addView(mView, mParams) 而NT的handleHide中會將Toast的視圖從Window中移除,如下所示。 if (mView.getParent() ! = null) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } 到這里Toast的Window的創建過程已經分析完了,相信讀者對Toast的工作過程有了一個更加全面的理解了。除了上面已經提到的Activity、Dialog和Toast以外,PopupWindow、菜單以及狀態欄等都是通過Window來實現的,這里就不一一介紹了,讀者可以找自己感興趣的內容來分析。 本章的意義在于讓讀者對Window有一個更加清晰的認識,同時能夠深刻理解Window和View的依賴關系,這有助于理解其他更深層次的概念,比如SurfaceFlinger。通過本章讀者應該知道,任何View都是附屬在一個Window上面的,那么這里問一個問題:一個應用中到底有多少個Window呢?相信讀者都已經清楚了。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看