<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>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                #### 1.SampleWindow的實現 在這一節里將編寫一個最簡單的Java程序SampleWindow,僅使用WMS的接口創建并渲染一個動畫窗口。此程序將拋開Activity、Wallpaper等UI架構的復雜性,直接了當地揭示WMS的客戶端如何申請、渲染并注銷自己的窗口。同時這也初步地反應了WMS的工作方式。 這個例子很簡單,只有三個文件: - SampleWindow.java 主程序源代碼。 - Android.mk 編譯腳本。 - sw.sh 啟動器。 分別看一下這三個文件的實現: **SampleWindow.java::SampleWindow** ``` package understanding.wms.samplewindow; ...... public class SampleWindow { publicstatic void main(String[] args) { try { //SampleWindow.Run()是這個程序的主入口 new SampleWindow().Run(); } catch (Exception e) { e.printStackTrace(); } } //IWindowSession 是客戶端向WMS請求窗口操作的中間代理,并且是進程唯一的 IWindowSession mSession = null; //InputChannel 是窗口接收用戶輸入事件的管道。在第5章中將對其進行詳細的探討 InputChannel mInputChannel = new InputChannel(); // 下面的三個Rect保存了窗口的布局結果。其中mFrame表示了窗口在屏幕上的位置與尺寸 // 在4.4中將詳細介紹它們的作用以及計算原理 RectmInsets = new Rect(); RectmFrame = new Rect(); RectmVisibleInsets = new Rect(); Configuration mConfig = new Configuration(); // 窗口的Surface,在此Surface上進行的繪制都將在此窗口上顯示出來 SurfacemSurface = new Surface(); // 用于在窗口上進行繪圖的畫刷 PaintmPaint = new Paint(); // 添加窗口所需的令牌,在4.2節將會對其進行介紹 IBindermToken = new Binder(); // 一個窗口對象,本例演示了如何將此窗口添加到WMS中,并在其上進行繪制操作 MyWindowmWindow = new MyWindow(); //WindowManager.LayoutParams定義了窗口的布局屬性,包括位置、尺寸以及窗口類型等 LayoutParams mLp = new LayoutParams(); Choreographer mChoreographer = null; //InputHandler 用于從InputChannel接收按鍵事件做出響應 InputHandler mInputHandler = null; booleanmContinueAnime = true; publicvoid Run() throws Exception{ Looper.prepare(); // 獲取WMS服務 IWindowManager wms = IWindowManager.Stub.asInterface( ServiceManager.getService(Context.WINDOW_SERVICE)); // 通過WindowManagerGlobal獲取進程唯一的IWindowSession實例。它將用于向WMS // 發送請求。注意這個函數在較早的Android版本(如4.1)位于ViewRootImpl類中 mSession= WindowManagerGlobal.getWindowSession(Looper.myLooper()); // 獲取屏幕分辨率 IDisplayManager dm = IDisplayManager.Stub.asInterface( ServiceManager.getService(Context.DISPLAY_SERVICE)); DisplayInfo di = dm.getDisplayInfo(Display.DEFAULT_DISPLAY); Point scrnSize = new Point(di.appWidth, di.appHeight); // 初始化WindowManager.LayoutParams initLayoutParams(scrnSize); // 將新窗口添加到WMS installWindow(wms); // 初始化Choreographer的實例,此實例為線程唯一。這個類的用法與Handler // 類似,不過它總是在VSYC同步時回調,所以比Handler更適合做動畫的循環器[1] mChoreographer= Choreographer.getInstance(); // 開始處理第一幀的動畫 scheduleNextFrame(); // 當前線程陷入消息循環,直到Looper.quit() Looper.loop(); // 標記不要繼續繪制動畫幀 mContinueAnime= false; // 卸載當前Window uninstallWindow(wms); } publicvoid initLayoutParams(Point screenSize) { // 標記即將安裝的窗口類型為SYSTEM_ALERT,這將使得窗口的ZOrder順序比較靠前 mLp.type = LayoutParams.TYPE_SYSTEM_ALERT; mLp.setTitle("SampleWindow"); // 設定窗口的左上角坐標以及高度和寬度 mLp.gravity = Gravity.LEFT | Gravity.TOP; mLp.x = screenSize.x / 4; mLp.y = screenSize.y / 4; mLp.width = screenSize.x / 2; mLp.height = screenSize.y / 2; // 和輸入事件相關的Flag,希望當輸入事件發生在此窗口之外時,其他窗口也可以接受輸入事件 mLp.flags = mLp.flags | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; } publicvoid installWindow(IWindowManager wms) throws Exception { // 首先向WMS聲明一個Token,任何一個Window都需要隸屬與一個特定類型的Token wms.addWindowToken(mToken,WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); // 設置窗口所隸屬的Token mLp.token = mToken; // 通過IWindowSession將窗口安裝進WMS,注意,此時僅僅是安裝到WMS,本例的Window // 目前仍然沒有有效的Surface。不過,經過這個調用后,mInputChannel已經可以用來接受 // 輸入事件了 mSession.add(mWindow,0, mLp, View.VISIBLE, mInsets, mInputChannel); /*通過IWindowSession要求WMS對本窗口進行重新布局,經過這個操作后,WMS將會為窗口 創建一塊用于繪制的Surface并保存在參數mSurface中。同時,這個Surface被WMS放置在 LayoutParams所指定的位置上 */ mSession.relayout(mWindow,0, mLp, mLp.width, mLp.height, View.VISIBLE, 0, mFrame, mInsets,mVisibleInsets, mConfig, mSurface); if(!mSurface.isValid()) { thrownew RuntimeException("Failed creating Surface."); } // 基于WMS返回的InputChannel創建一個Handler,用于監聽輸入事件 //mInputHandler一旦被創建,就已經在監聽輸入事件了 mInputHandler= new InputHandler(mInputChannel, Looper.myLooper()); } publicvoid uninstallWindow(IWindowManager wms) throws Exception { // 從WMS處卸載窗口 mSession.remove(mWindow); // 從WMS處移除之前添加的Token wms.removeWindowToken(mToken); } publicvoid scheduleNextFrame() { // 要求在顯示系統刷新下一幀時回調mFrameRender,注意,只回調一次 mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION , mFrameRender, null); } // 這個Runnable對象用以在窗口上描繪一幀 publicRunnable mFrameRender = new Runnable() { @Override publicvoid run() { try{ // 獲取當期時間戳 long time = mChoreographer.getFrameTime() % 1000; // 繪圖 if (mSurface.isValid()) { Canvas canvas = mSurface.lockCanvas(null); canvas.drawColor(Color.DKGRAY); canvas.drawRect(2 * mLp.width * time / 1000 - mLp.width, 0, 2 *mLp.width * time / 1000, mLp.height,mPaint); mSurface.unlockCanvasAndPost(canvas); mSession.finishDrawing(mWindow); } if(mContinueAnime) scheduleNextFrame(); } catch (Exception e) { e.printStackTrace(); } } }; // 定義一個類繼承InputEventReceiver,用以在其onInputEvent()函數中接收窗口的輸入事件 classInputHandler extends InputEventReceiver { Looper mLooper = null; publicInputHandler(InputChannel inputChannel, Looper looper) { super(inputChannel,looper); mLooper= looper; } @Override publicvoid onInputEvent(InputEvent event) { if(event instanceof MotionEvent) { MotionEvent me = (MotionEvent)event; if (me.getAction() ==MotionEvent.ACTION_UP) { // 退出程序 mLooper.quit(); } } super.onInputEvent(event); } } // 實現一個繼承自IWindow.Stub的類MyWindow。 classMyWindow extends IWindow.Stub { // 保持默認的實現即可 } } ``` 由于此程序使用了大量的隱藏API(即SDK中沒有定義這些API),因此需要放在Android源碼環境中進行編譯它。對應的Android.mk如下: **Android.mk** ``` LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_MODULE_TAGS := optional LOCAL_MODULE := samplewindow include $(BUILD_JAVA_LIBRARY) ``` 將這兩個文件放在$TOP/frameworks/base/cmds/samplewindow/下,然后用make或mm命令進行編譯。最終生成的結果是samplewindow.jar,文件位置在out/target/<ProductName>/system/framework/下。將該文件通過adb push到手機的/system/framework/下。 >[info] **提示**:讀者可使用Android4.2模擬器來運行此程序。 然而,samplewindow.jar不是一個可執行程序,。故,需借助Android的app\_process工具來加載并執行它。筆者編寫了一個腳本做為啟動器: **sw.sh** ``` base=/system export CLASSPATH=$base/framework/samplewindow.jar exec app_process $base/binunderstanding.wms.samplewindow.SampleWindow "$@" ``` **注意** app\_process其實就是大名鼎鼎的zygote。不過,只有使用--zygote參數啟動時它才會給改名為zygote\[2\],否則就像java –jar命令一樣,運行指定類的main靜態函數。 在手機中執行該腳本,其運行結果是一個灰色的方塊不斷地從屏幕左側移動到右側,如圖4-1所示。 :-: ![](http://img.blog.csdn.net/20150814130201736?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖 4-1 SampleWindow在手機中的運行效果 #### 2.初識窗口的創建、繪制與銷毀 SampleWindow的這段代碼雖然簡單,但是卻很好地提煉了一個窗口的創建、繪制以及銷毀的過程。注意,本例沒有使用任何 WMS以外的系統服務,也沒有使用Android系統四大組件的框架,也就是說,如果你愿意,可以利用WMS實現自己的UI與應用程序框架,這樣就可以衍生出一個新的平臺了。 總結在客戶端創建一個窗口的步驟: - 獲取IWindowSession和WMS實例。客戶端可以通過IWindowSession向WMS發送請求。 - 創建并初始化WindowManager.LayoutParams。注意這里是WindowManager下的LayoutParams,它繼承自ViewGroup.LayoutParams類,并擴展了一些窗口相關的屬性。其中最重要的是type屬性。這個屬性描述了窗口的類型,而窗口類型正是WMS對多個窗口進行ZOrder排序的依據。 - 向WMS添加一個窗口令牌(WindowToken)。本章后續將分析窗口令牌的概念,目前讀者只要知道,窗口令牌描述了一個顯示行為,并且WMS要求每一個窗口必須隸屬于某一個顯示令牌。 - 向WMS添加一個窗口。必須在LayoutParams中指明此窗口所隸屬于的窗口令牌,否則在某些情況下添加操作會失敗。在SampleWindow中,不設置令牌也可成功?完成添加操作,因為窗口的類型被設為TYPE\_SYSTEM\_ALERT,它是系統窗口的一種。而對于系統窗口,WMS會自動為其創建顯示令牌,故無需客戶端操心。此話題將會在后文進行更具體的討論。 - 向WMS申請對窗口進行重新布局(relayout)。所謂的重新布局,就是根據窗口新的屬性去調整其Surface相關的屬性,或者重新創建一個Surface(例如窗口尺寸變化導致之前的Surface不滿足要求)。向WMS添加一個窗口之后,其僅僅是將它在WMS中進行了注冊而已。只有經過重新布局之后,窗口才擁有WMS為其分配的畫布。有了畫布,窗口之后就可以隨時進行繪制工作了。 而窗口的繪制過程如下: - 通過Surface.lock()函數獲取可以在其上作畫的Canvas實例。 - 使用Canvas實例進行作畫。 - 通過Surface.unlockCanvasAndPost()函數提交繪制結果。 * * * * * **提示** 關于Surface的原理與使用方法,請參考《卷 I》第8章“深入理解Surface系統”。 * * * * * 這是對Surface作畫的標準方法。在客戶端也可以通過OpenGL進行作畫,不過這超出了本書的討論范圍。另外,在SampleWindow例子中使用了Choreographer類進行了動畫幀的安排。Choreographer意為編舞指導,是Jelly Bean新增的一個工具類。其用法與Handler的post()函數非Z且不會再顯示新的窗口,則需要從WMS將之前添加的顯示令牌一并刪除。 #### 3.窗口的概念 在SampleWindow例子中,有一個名為mWindow(類型為IWindow)的變量。讀者可能會理所當然地認為它就是窗口了。其實這種認識并不完全正確。IWindow繼承自Binder,并且其Bn端位于應用程序一側(在例子中IWindow的實現類MyWindow就繼承自IWindow.Stub),于是其在WMS一側只能作為一個回調,以及起到窗口Id的作用。 那么,窗口的本質是什么呢? 是進行繪制所使用的畫布:Surface。 當一塊Surface顯示在屏幕上時,就是用戶所看到的窗口了。客戶端向WMS添加一個窗口的過程,其實就是WMS為其分配一塊Surface的過程,一塊塊Surface在WMS的管理之下有序地排布在屏幕上,Android才得以呈現出多姿多彩的界面來。所以從這個意義上來講,WindowManagerService被稱之為SurfaceManagerService也說得通的。 于是,根據對Surface的操作類型可以將Android的顯示系統分為三個層次,如圖4-2所示。 :-: ![](http://img.blog.csdn.net/20150814130227326?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖 4-2 Android顯示系統的三個層次 在圖4-2中: - 第一個層次是UI框架層,其工作為在Surface上繪制UI元素以及響應輸入事件。 - 第二個層次為WMS,其主要工作在于管理Surface的分配、層級順序等。 - 第三層為SurfaceFlinger,負責將多個Surface混合并輸出。 經過這個例子的介紹,相信大家對WMS的功能有了一個初步的了解。接下來,我們要進入WMS的內部,通過其啟動過程一窺它的構成。
                  <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>

                              哎呀哎呀视频在线观看