基于安卓的移動課堂應用開發(fā)_第1頁
基于安卓的移動課堂應用開發(fā)_第2頁
基于安卓的移動課堂應用開發(fā)_第3頁
基于安卓的移動課堂應用開發(fā)_第4頁
基于安卓的移動課堂應用開發(fā)_第5頁
已閱讀5頁,還剩29頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

本科畢業(yè)設計(論文)基于安卓的移動課堂應用開發(fā)學院信息工程學院 專業(yè)通信工程 年級班別2012級()班 學號 學生姓名 指導教師原玲2016年5月

基于安卓的移動課堂應用開發(fā)基于安卓的移動課堂應用開發(fā)姚瑤瑤信息工程學院摘要隨著計算機技術的飛速發(fā)展,一方面,各種各樣的類庫、框架層出不窮;另一方面,用戶對于軟件質量和功能的要求也越來越高。編程工作并不像有些人聲稱的那樣,門檻變得越來越低,相反,由于系統(tǒng)變得越來越復雜,對編程人員來說,要求反而變高了。既然系統(tǒng)復雜度的增加不可避免,作為一個開發(fā)者,我們所能做的,便是尋求某些方法,以應對這復雜的系統(tǒng)、和步步緊逼的交付日期。作為軟件工程師們的經(jīng)驗結晶——設計模式,便是我們應對軟件復雜度的有力武器。即便需求總是在改變,但恰如其分的軟件設計,將賦予軟件足夠的彈性,去應用合理的變化。而作為設計模式一個核心的原則——模塊化,則有助于我們分解復雜度。復雜的系統(tǒng),經(jīng)過足夠的合理的抽象、封裝,形成一個個正交的子系統(tǒng),使得開發(fā)人員能夠在同一時間只集中注意于某一個局部,而不是同時處理龐大的細節(jié)。本文根據(jù)軟件工程的一系列原則對安卓平臺的移動課堂應用進行設計,并對Android應用中常用的幾種設計模式進行比較,說明各個模式的優(yōu)劣以及設計我們的方案時所作的權衡。關鍵詞:設計模式,軟件復雜度,模塊化AbstractAsthefast-developingofcomputerscience,allkindsofsoftwareframework,librarieswereborntoeaseourdeveloplife.Butattheotherhand,ouruserswantoursoftwaretoprovidemoreandmorefunctionalitiesandexpectthatit’sstable.Thelifeofdevelopmentisnotbecomemucheasyassomebodyclaimedthatit’seasierandeasiersincetherearesomanytoolsthatwecanuse.Butthankstothecomplexityofthesystem,ourdeveloper’sskillbecomesverydemanding.Sincethecomplexityisunavoidable,wehighlyneedatooltohackwiththelargesystemandcomfortourclientsoftheincomingdeliverytime.Designpattern,asthecrystallizationoftheexperienceofourdevelopers,isanextremelypowerfularmforustofightwiththecomplexityofthesystem.Althoughtherequirementisalwayschanging,awell-designedarchitecturewillgiveoursoftwaretheabilitytocopewithsomereasonablerequirementchange.Modularity,asthecoreprincipleofdesignpattern,canhelpustodecomposethecomplexity.Usingsomereasonableandsufficientabstractionandencapsulation,thelargesystemcanbedecomposedtoseveralorthogonalsub-systems,andourdeveloperscannowdiveintosomepartbutdon'tcareothers.Inthispaper,IshowsthatthewaytodesignourAndroidmobilelearningappusingseveralbasicprinciplesofsoftwareengineering.Inaddition,IalsopresentacomparisonbetweenafewpopulardesignpatternswhiledevelopingAndroidappsandexplainourchoiceandwhychooseit..Keywords:DesignPattern,SoftwareComplexity,Modularity目錄TOC\o"1-4"\h\z\u1緒論 11.1背景 11.2什么是設計模式 11.3模式的作用 22問題 42.1一個維護噩夢 42.2必須解決的問題 53架構的選擇 73.1經(jīng)典的MVC模式 73.2一種變體——MVP模式 83.3更進一步——MVVM模式 103.4回歸簡樸——Document-View模式 113.5增強的Document-View 124框架的實現(xiàn) 144.1Document實例的創(chuàng)建 144.2View引用的保存 154.3處理配置改變引起的重復創(chuàng)建 154.4網(wǎng)絡框架 164.5總體架構 165應用的實現(xiàn) 185.1重新實現(xiàn)注冊頁面 185.2文件下載 196總結與展望 216.1總結 216.2展望 21參考文獻 22致謝 23附錄A一個簡單的注冊頁面實現(xiàn) 24附錄BGenericActivity 24附錄CAbstractDocument<ViewOps> 25附錄D避免Document的重復創(chuàng)建 26附錄E下載進度的發(fā)布 271緒論1.1背景從打孔的卡帶到匯編語言,從匯編語言到高級語言再到今天人氣高漲的人工智能,每每有新的技術出現(xiàn),總是有人預言程序員這個職業(yè)將死。然而,從六十年代高級編程語言出現(xiàn)到現(xiàn)在,程序員這個職位非但沒有消失,反倒越發(fā)的紅火。在編程所使用的工具越發(fā)強大的趨勢下,很多人忽略了更為重要的一點,應用的復雜度也在急劇攀升。人們對軟件的質量和能力有著越來越高的期望。所以,即便是有著越來越強大的工具,編程這個工作也不會消亡。相反,它帶來的是越來越大的挑戰(zhàn)。應用越來越復雜,一個簡單的商業(yè)應用,它的復雜度絕對可以輕易超過大多是程序員的智商。隔離復雜度,在軟件開發(fā)領域,也自然而然地成為一個不可獲取的技能。而它所使用的最大的工具,便是設計模式。模式有助于我們利用經(jīng)驗豐富的軟件工程師的智慧,它們記錄了軟件開發(fā)領域已經(jīng)得到充分證明的有效經(jīng)驗,可幫助推廣良好的設計經(jīng)驗。每個模式都一個特定的問題在特定的環(huán)境中反復出現(xiàn)的問題,并給出了一個解決方案。利用模式可以打造出具有特定特征的軟件架構。1.2什么是設計模式ChristopherAlexander說,“每個模式描述了一個在我們身邊不斷重復發(fā)生著的問題,已經(jīng)對該問題的解決方案的核心。如此一來,你就能一次又一次地使用該方案二不必做重復勞動”。盡管Alexander講的是建筑領域的模式,對于軟件建構,這個說法也同樣適用。一般而言,一個模式有4個部分:模式的名稱。用一兩個詞來描述模式的問題和效果。對一個模式命名,增加了我們的設計詞匯。模式名幫助我們思考,幫助我們與他人交流設計思想和設計結果。問題。問題描述了給定背景下反復出現(xiàn)的問題,以及在什么時候可以使用該模式。它闡述了問題的本質:必須解決的具體設計問題是什么?出來對問題的陳述,還可以輔以一系列的作用力的描述。作用力表示問題的各個方面對特定解決方案的影響。解決方案。解決方案描述了該如何解決上述重復出現(xiàn)的問題,或者說,如何平衡相關的作用力。它描述了設計的各個組件之間的關系和各自的職責。因為模式就像一個模板,它可用于解決一定條件下重復出現(xiàn)的不同問題,所以解決方案并不描述一個特定的設計或者實現(xiàn),而是提供一個更為抽象的概念,并對軟件設計進行描述。效果。效果描述了應用一個模式所能夠達到的效果已經(jīng)該模式所權衡的問題。它對于如何評價設計、選擇使用何種模式已經(jīng)選擇該模式的代價具有重要意義。模式的作用模式描述了在特定環(huán)境下反復出現(xiàn)的問題,并提供了該問題的一個解決方案。模式記錄了已經(jīng)得到充分驗證的既有的設計經(jīng)驗。模式并非刻意發(fā)明或創(chuàng)造出來的,而是工程師們根據(jù)其自身的經(jīng)驗提煉出來的。模式描述了超越類、實例和組件的抽象。由于模式是用于解決特定一類問題的通用的解決方案,這就決定了其不能描述具體的實現(xiàn),而必須在更高的抽象上描述問題。模式常常描述了多個組件、類或對象之間的協(xié)作方式和各自的職責。通過對象間的協(xié)作,從而解決該問題。模式提供了一種通用的語言,并讓大家對設計原則有一致的認識。精心選擇的模式名會成為廣外人知的設計語言的一部分,有助于對設計問題及其解決方案展開有效的討論。我們不需要對解決方案給出詳盡的描述,只要提及其模式名,熟悉該模式的人便能夠馬上明白程序的基本結構和特征。模式是一種記錄軟件架構的手段。模式可描述你在設計軟件系統(tǒng)時腦海中浮現(xiàn)的構思,在別人擴展和修改原始架構或修改系統(tǒng)的代碼時,這有助于避免違背這種構思。模式有助于創(chuàng)建具有指定特征的軟件。模式提供了功能行為骨架,有助于實現(xiàn)應用程序的功能。每一種模式都有描述了使用該模式所能夠達到的效果,這基本上就覺得了應用該模式的軟件會成長為什么樣。模式有助于打造復雜而異質的軟件。每個模式都描述了一組預定義的組件,這些組件扮演的角色以及它們之間的關系,可用于規(guī)范軟件結構的特定方面。模式猶如建筑構件,可用于打造更復雜的設計。模式有助于控制軟件的復雜度。每個模式都為其闡述的問題提供了經(jīng)過實踐考驗的解決之道:需要的組件類型、這些組件扮演的角色、應隱藏的細節(jié)、令人矚目的抽象,已經(jīng)各個部分的原理。通過明確各個組件的角色以及它們之間的關系,在軟件開發(fā)過程中,我們可以僅僅關注一個單獨的組件,而不需要花費多余的精力去考慮整個系統(tǒng)。2問題2.1一個維護噩夢考慮一下我們正在編寫的Android移動課堂應用?,F(xiàn)在,我們需要用戶最先看到的頁面——注冊。對于一個最簡單的注冊頁面,我們需要兩個輸入框,一個按鈕,待用戶輸入用戶名和密碼后,我們獲取對應的信息,然后發(fā)送至后臺服務器。然后,對于很多的新手,不出意外的,寫出的代碼(見附錄A)卻幾乎可以肯定會包含一系列的問題。首先,我們可以肯定的是,那段代碼的邏輯并沒有什么問題——在onCreate方法中初始化View并為按鈕注冊事件監(jiān)聽器。然后,由于Android并不允許在主線程中執(zhí)行網(wǎng)絡操作,我們新開了一個單獨的線程,用以將注冊信息發(fā)往服務器。但是,遺憾的是,這段代碼存在著非常嚴重的問題:Activity的引用泄露到的匿名內(nèi)部類的Runnable實例中,這幾乎可以肯定會發(fā)生內(nèi)存泄露,進而發(fā)生臭名昭著的OurOfMemoryError錯誤。為了解決問題1,我們可以將Runnable實現(xiàn)為靜態(tài)的內(nèi)部類,如此一來,便不會意外地將Activity的引用泄露出去。但是,如果一來,為了在服務器將注冊結果返回的時候做出反應,并對UI做出相應的改變,我們需要同時使用WeakReference和Handler。用戶每點擊按鈕一次,便新創(chuàng)建一個線程。這必將使應用經(jīng)受大量的線程創(chuàng)建和銷毀帶來的開銷。這將使得應用和整個系統(tǒng)運行緩慢,而我們的用戶,也將會由于無法忍受這體驗而永遠不再使用這款APP。也許,你聽說過Java的ExecutorFramework。沒錯,我們可以利用它減少線程創(chuàng)建、銷毀的開銷。但是,即便使用線程池,問題1和2的描述也依然適用。有經(jīng)驗的開發(fā)者會發(fā)現(xiàn)另外一個非常嚴重的問題。這里,應用的UI和邏輯混合在了一起。應用的邏輯通常不會發(fā)生太多的改變,而UI卻往往是改動之源。雖然改動UI不一定就會修改到邏輯,如果我們足夠小心,也可以保證在更新UI的同時,不會意外地給邏輯部分的代碼引入bug。但是,既然他們在同一個文件里,而你打開了那個文件進行修改,那么,就存在破壞的可能。最安全的做法是,將邏輯部分隔離開,同一時間,我們只關注UI或邏輯(所謂的隔離復雜度),這樣,在改變UI的時候,我們可以保證邏輯部分不會遭到破壞。同時,將UI和邏輯分離,對于應用對編寫也有非常大的益處。同一時間,我們需要關注的東西更少了。注意力的集中,意味著我們可以專注于更細的細節(jié)上,編寫出更好、更健壯的應用?,F(xiàn)在,跳出這么一個單獨的Activity,讓我們考慮一整個應用——我們的Android移動課堂應用。不出意外,幾乎每一個頁面,都會有網(wǎng)絡操作。換句話說,即便根據(jù)上面的討論,我們修復了附錄A中所展示的代碼的bug,采用這種方法,也必將存在大量的重復代碼。這嚴重得違反了DRY原則,必將導致一個維護的噩夢。2.2必須解決的問題前面我們說到,設計模式的組成的問題部分,描述的是一個特定背景下反復出現(xiàn)的問題,并且,對于該問題的描述不應該針對特定的應用,它應該在更為抽象的層級上描述一個反復出現(xiàn)的問題。上一小節(jié)我們討論對于一個特定的注冊頁面的面臨的問題。實際上,不只是那一個頁面,我們所開發(fā)的移動課堂APP,幾乎所有的頁面都存在著相似的問題。更進一步講,所有需要和服務器進行通信的APP都將面對這同一個問題。但凡是一個較長時間的操作,例如網(wǎng)絡,我們都要小心避免泄露Activity的引用。但是,我們也不想在每個命令對象中使用WeakReference。因為,這所有的弱引用,都是為了同一個目的而存在——保存Activity的引用。既然使用他們的目的是一致的,那么,我們就完全有理由用一個復用的組件來完成這一功能。在上面我們的提到了UI和邏輯分離的原則。雖然“分離”二字很簡單,并且做起來,也似乎不難做到將應用的UI和邏輯分開。但是,我們要做的不僅僅是分離,而是一致地分離。我們希望整個應用有一致的架構,我們希望底層組件能夠得到盡可能多的復用。也就是說,對于類似的頁面,我們希望可以有一種全局性的架構,不管UI的內(nèi)容和類型,它都能夠勝任。對于UI和邏輯分離的問題,我們希望不僅僅是通過口頭或者文檔的形式進行約束。如果一個對框架的使用存在錯誤的用法,那么,錯誤就一定會發(fā)生。所以,我們希望在選定了框架之后,能夠從語法層面約束使用者。我們需要有強制性的措施,以避免用戶出錯。同時,對于UI和邏輯分離的問題,我們需要引導用戶以分離的思維去考慮應用的設計,并使用框架實現(xiàn)強制用戶進行分離??墒?,世上并沒有銀彈。盡管我們希望構件一個全局性的架構,以期提供整體的一致性。其實我們并沒有辦法實現(xiàn)一個架構,使得它能夠適用于所有的場景。但是,我們能夠做到的是,盡可能地讓其可擴展。只要擁有足夠的擴展性,它就能夠適應更多的應用場景,也就能夠解決更多的問題。更進一步,如果在這所有問題中存在著一個子集,它們需要一種更為特化的解決方案,我們可以對原有的基礎框架進行擴展,從而用這個新的特化的框架來決定這些問題。此外,我們也希望它足夠地輕量,它僅僅解決最為核心的、一直重復著的問題。而不試圖提供過多無謂的功能。一個好的設計,是已經(jīng)不能夠再減少任何東西。3架構的選擇3.1經(jīng)典的MVC模式提起UI和邏輯的分離,相信很多人最先想到的便是MVC(Model-View-Controller)模式了。從Smalltalk時代開始,MVC便被用來創(chuàng)建用戶界面。在MVC模式里,共包含三種類型的對象,模型Model是應用對象,視圖View用于向用戶展示,而控制器Controller定義了View對于用戶輸入的響應方式。不使用MVC時,用戶界面設計往往將這些對象混雜在一起,不利于提供設計的靈活性和復用性。MVC通過同時使用觀察者模式和策略模式分離視圖和模型,同時提供對不同UI邏輯的支持。同時建立一個“訂閱/通知”協(xié)議,視圖和模型彼此分離。視圖必須保證它的顯示正確地反映了模型的狀態(tài)。一定模型發(fā)送了變化,它也將通知視圖,從而,視圖可以以此作為契機,更新對應的UI。模型的變更傳播機制確保應用程序的數(shù)據(jù)發(fā)生變化時,所有的觀察者將在正確的時間得到通知。這讓所有依賴于模型的視圖和控制器得以同步。這種方法還可以讓你為一個模型提供不同的多個視圖的表現(xiàn)形式,為能夠為一個模型創(chuàng)建新的視圖而無需重寫模型。在運行階段,可同時打開多個視圖(共享一個model),還可以動態(tài)地打開和關閉視圖。圖3.1MVC模式而對于View和Controller,則是策略模式的一個例子。一個策略表示一個算法族的一個對象。通過定義一個算法族,我們可以動態(tài)或靜態(tài)地替換對應的策略。而策略的使用者——這里的視圖,則完全不受影響。通過將模型、視圖和控制器分離,可更換模型的視圖和控制器,甚至在運行階段都可以替換用戶界面。但是,對于當今的移動開發(fā),MVC模式又有一些顯著的不足。首先,MVC三個組件存在著一個環(huán)狀的依賴關系。也就是說,一個組件接口的變化,必將同時影響另外兩個組件。不僅如此,環(huán)狀依賴也提高了單元測試的難度。同一時間,如果我們想對一個組件進行測試,必須提供兩個mock對象。此外,視圖和控制器的關系過于緊密??刂破骱鸵晥D雖然分開了,但關系緊密,這導致無法分別重用它們。不僅如此,視圖和控制器與模型也是緊耦合的。視圖和控制器都直接調用模型,這意味著修改模型的接口將破壞視圖和控制器的代碼。3.2一種變體——MVP模式由于上述MVC模式的一系列問題,加之其誘人的優(yōu)點,于是,便有了它的一個變體——MVP模式。MVP作為一個面向用戶界面設計的架構模式,被刻意設計,以充分利用一些自動化單元測試工具并且改善表現(xiàn)邏輯分離的問題。圖3.2MVP模式 在MVP模式中,模型Model組件提供了一個定義用于展示的數(shù)據(jù)的接口,并根據(jù)用戶的動作做出相應的處理邏輯。這里的Model和MVC中的Model并沒有什么差異。而表現(xiàn)者Presenter則作為一個中間人的角色而存在,所有的表現(xiàn)邏輯都存在于表現(xiàn)者Presenter中。它從Model中取得數(shù)據(jù),并對它進行格式化,然后在View中展示。所以,Presenter也因此有了另一個名稱——超級控制器。對于最后的View組件,這里我們稱它為被動視圖。視圖并不主動處理UI的更新和用戶動作,它所做的,就是非常單一的功能——展示用戶界面。視圖的更新由Presenter復制,對于用戶的動作,它也是直接轉發(fā)至Presenter中。 實際上,對于視圖所處理的邏輯的多少,每個不同的實現(xiàn)都不盡相同。在一個極端,視圖是一個完全的被動式的存在,將所有的用戶操作都轉發(fā)至Presenter中。在這種情況下,當用戶觸發(fā)View組件的一個事件回調時,它不會做任何的處理,而僅僅是調用presenter的一個沒有參數(shù)也沒有返回值的方法。然后,Presenter通過View組件的接口,取出必需的數(shù)據(jù)。最后,Presenter操作Model組件,得到結果后又相應地更新UI。而在另一方面,某些實現(xiàn)卻運行View組件處理某些特定的事件、操作或命令。這通常更適用于基于web的架構,這種情況下,View組件通常運行在用戶的瀏覽器中,所以,對于一些交互命令,View組件是一個更好的處理場所。 MVP模式與MVC模式最大的不同在于,MVP并不存在環(huán)狀依賴,取而代之,MVP是鏈狀的依賴關系。這使得MVP更加地適合于單元測試。同時,由于Presenter的存在,即便Model的接口發(fā)生變化,我們也只需要修改Presenter——我們的View組件,依然可以繼續(xù)使用而不需要做任何的修改。而對于MVC的優(yōu)點,在這里也同樣保持著。我們的UI和邏輯是顯著分離的,組件也可以復用。如果交換邏輯需要作出修改,也可以通過替換Presenter這個組件而得到所需的效果。同時,這里的Presenter也是作為一個策略而存在的,這意味著,我們可以在運行時動態(tài)地更換表現(xiàn)邏輯。 遺憾的是,MVP也有其缺點。前面我們說MVP是鏈狀依賴的時候,敏銳的你就應該意識到,我們的調用鏈過長了。即使是Model中的一個小小的改變,也要跨過千山萬水才能夠表現(xiàn)在UI上。也就是說,與MVC相比,對于性能很關心的應用,這個模式可能就不太適用了。所幸的是,這種情況發(fā)生的幾率實在太低,所以一般不會是太大的問題。而更為致命的是,作為超級控制器的Presenter,對于UI的所有更新都要經(jīng)手的它,實現(xiàn)中往往存在很多的盲轉發(fā)。大多數(shù)時候,我們根本不需要對數(shù)據(jù)進行格式化,只是從Model取出,然后直接應用于View組件的更新。編寫這么多的盲轉發(fā)代碼,可不會是一件愉快的事。3.3更進一步——MVVM模式前面我們提到MVP模式中,將數(shù)據(jù)顯示到View組件通常是一件比較無趣而繁瑣的工作。而MVVM(Model-View-ViewModel)的出現(xiàn),就是為了解決這么一個問題。MVVM同樣是對圖形用戶界面與應用邏輯的分離。它通過一種標記語言或GUI代碼,將UI與商業(yè)邏輯或后臺邏輯分離開。MVVM中的ViewModel組件是一個值轉換器;這意味著ViewModel的職責是從將Model類得來的數(shù)據(jù)對象進行轉化,使之更易于管理和表現(xiàn)。從這一點看,ViewModel比View組件更加的模型化,并處理著幾乎所有的視圖表現(xiàn)邏輯。ViewModel可以使用中間人模式進行實現(xiàn),它處理View所支持的所有對后端邏輯的訪問控制。Model-View-ViewModel也叫Model-View-Binder,因為對View組件的更新,我們通常通過一些自動化的工具進行,也就是所謂的databinding。圖3.3MVVM模式從組件的依賴關系上看,MVVM和MVP非常地相似,唯一的不同是,MVP里,我們程序員自己手動執(zhí)行了一個所謂databinding的動作。Model組件也叫domainmodel,與MVC和MVP中的Model一樣,這里用于表現(xiàn)業(yè)務邏輯,并提供應用數(shù)據(jù)的存取。View組件也是,作為一個用戶界面而存在。ViewModel是View組件的一個抽象表示,用于暴露其公開的接口。與MVC中的Controller和MVP中的Presenter不同的是,MVVP有一個binder。在ViewModel中,binder調節(jié)著View和ViewModel的通信。聲明式的對數(shù)據(jù)、命令和UI的綁定在MVVM模式中是隱式的。Binder將開發(fā)者從手工編寫大量模板代碼解放出來。毫無例外,MVVM也有其缺點。對簡單的應用而已,這樣的框架無意過于的厚重。而對于復雜的交互邏輯,靠程序(binder)進行自動的數(shù)據(jù)綁定又顯得過于的力不從心。使用MVVM時,我們得自行拿捏一個中庸之道。3.4回歸簡樸——Document-View模式MVC存在著環(huán)狀依賴,MVP調用鏈過長,MVVM對簡單應用過于復雜,對復雜應用支持又不足,那么,我們又應該選擇哪一個模式呢?回想前面我們討論過的問題,我們需要的是UI和邏輯分離,需要它足夠的輕量,并具有相當?shù)目蓴U展性。它只解決最為迫切,一直重復發(fā)生著的問題。那么,這樣的架構模式存不存在呢?答案是,存在,它就是所謂的Document-View模式。圖3.4Document-View模式Document-View也是屬于MVC變體的一種。這里的View,和MVC中的View并沒有什么顯著的差別,同樣的用于向用戶展示信息和提供交互。而Document則相當于Model和Controller的結合,或者說,MVP模式中的Model和Presenter。使用Document-View模式的最為著名的架構,應該就屬微軟的MFC框架了。默認情況下,MFC的項目生成向導會生成兩個類——Document類和View類。Document負責管理數(shù)據(jù)并更新View,View組件則用于向用戶展示數(shù)據(jù)和提供交互功能。這就是我們所需要的最基本的UI和應用邏輯分離。但是,如果僅僅是UI和邏輯分離,并不足以使我們選擇這么一個架構。選擇Document-View,還有著更多的理由。對于絕大多數(shù)的Android應用,一個Model僅僅對應一個View,我們并沒有不需要MVC同步更新多個View組件的功能。如果使用MVC,卻要因此忍受它帶來的環(huán)狀依賴。可以說,MVC并不適用于Android應用的開發(fā)。MVP有著過長的調用鏈,無論是從View到Model還是從Model到View,我們不想要這些額外的消耗。對于性能非常重要的應用而已,中間的Presenter是不能忍受的。此外,很多時候,中間的Presenter并沒有做太大有用的工作,它只是單純地將View和Model的調用相互轉發(fā)而已。對于MVVM,不使用他的原因和上面所述的它的缺點一樣。在簡單的應用表現(xiàn)不佳,復雜的應用卻有力不從心。最為重要的一點是,由于它們都是基于UI和邏輯分離的原則所設計的,四個模式都有其共通的地方。而對于最簡單的Document-View,它提供了最為基本的UI分離的支持,使用它,在合適的場合,我們可以輕易對它進行擴展,以增強為其他模式。假定我們現(xiàn)在的UI交換邏輯過于的復雜,Document-View已經(jīng)無法滿足我們的需求。此時,我們可以對Document-View進行擴展,使之成為一個MVP模式。需要注意的一點是,盡管此時已經(jīng)變化為MVP模式,但我們還是重用了低層Document-View框架的實現(xiàn)。圖3.5將Document-View擴展為MVP模式3.5增強的Document-View在前面我們描述模式的時候,我們通常都只是說“View”,“Model”等等。即便我們使用這樣的術語,但是,這并不意味著,各個組件是在和實體的類進行交談。這里“View”,并不特指某個具體的實現(xiàn),它完全可以是一個接口。目前Android的Native應用主要是使用Java進行開發(fā)的,利用Java的語言特性,我們完全可以做得更好。圖3.6增強的Document-View這里的I指的是Interface,并且,這里的interface并不指一般意義上的接口,而是指Java語言的一個語法“interface”。組件并不互相直接就進行通信,而是通過引用一句接口。通過針對接口進行編程,我們保留了對組件進行透明替換的能力。雙方通過接口進行引用,使得在利用對方提供的接口時,只能使用接口所提供的抽象,這大大減少了需要同時關注的問題,很好地隔離了復雜度。接口提供了一個語言級的支持,可以實現(xiàn)安全、透明的實現(xiàn)替換。我們并不關心View和Document的實際類型是什么,我們只知道,那個類實現(xiàn)了我們所需的接口。只有實現(xiàn)對應的接口,我們就可以對一個組件進行直接的替換,而另一個組件則完全不用更改。甚至,與MVC、MVP等提供的動態(tài)性一樣,我們也可以在運行時動態(tài)地選擇View和Document的實現(xiàn)。定義接口的過程中,可以迫使我們對組件所需要提供的功能進行細致的考慮。通過對業(yè)務進行分析并制定接口,可以避免在尚不清楚邏輯的情況下過快地進行編程。同時,它也可以幫助我們理清應用的邏輯。還應注意的是,接口的命名應該是描述式的,它表明所完成的功能,而不是如何完成該功能。當我們編寫實體類的時候,只需要實現(xiàn)對應的接口,此時不再需要理會另一個組件是如何實現(xiàn)的。4框架的實現(xiàn)4.1Document實例的創(chuàng)建Android的UI是基于所謂的Activity實現(xiàn)的,一個Activity就相當于一個窗口。所以,很自然的,Activity就是我們Document-View架構中View組件的實現(xiàn)者了。由于一個Android應用首先啟動的幾乎總是Activity,并且它是由AndroidRuntime啟動的,所以,剩下的我們需要關心的,表示何時實例化Document的實例,又在哪里進行實例化。每個Activity都對應有一個Document,一般情況下,在Activity::onCreate方法中對Document實例化后,我們將其引用放置于內(nèi)部的一個實例域中。而這一系列的步奏,無疑每個Activity都需要。此時,就是拿出繼承這把武器的時候了。通過使用泛型參數(shù),我們不再需要提供一個公共的Document或View接口,而是由Java編譯器幫助我們進行安全的類型轉換。也就是說,子類所提供的Document和View接口的類型得以保留。由于我們希望由超類統(tǒng)一對Document進行實例化,在此,我們不得不做出一些折衷。在我們的實現(xiàn)中,我們要求Document類具有一個默認構造函數(shù),從而可以利用Class::instance()方法進行實例的創(chuàng)建。具體的實現(xiàn)代碼見附錄B。與此同時,由于我們需要將View引用傳遞給Document,這里我們必須對Document的接口進行一定的約束。也就是說,Document接口有一個公共的祖先BasicDocumentOps接口。publicinterfaceBasicDocumentOps<ViewOps>{voidonCreate();voidonDestroy();voidsetView(ViewOpsview);} 通過增加一個setView方法,我們可以利用它設置View組件的引用。同時,我們也增加了兩個用于Activity聲明周期回調的方法。 使用該超類時,子類只需要回調超類的onCreateDocument方法,GenericActivity即會自動完成Document的創(chuàng)建工作: onCreateDocument(view,RegisterDocument.class);4.2View引用的保存前面我們提到了Activity的View組件的天然候選人,而在一開始我們就也提到了不小心保存Activity的引用會導致內(nèi)存泄露的問題。也就是說,對于傳遞給Document的實例,我們必須使用WeakReference將其保存起來,否則,將很容易發(fā)生內(nèi)存泄露。而這樣的一個處理措施,同樣是所有Document都必須具備的。由于有了上面創(chuàng)建Document時的經(jīng)驗,我們很容易就會想要使用基于繼承的方法來做。每次,這里我們確實用的是繼承,但由于接口由于抽象類。對于Document,我們只是給出一個骨架實現(xiàn),如果客戶需要,他可以完全不使用我們這里所創(chuàng)建的類型體系。具體的代碼見附錄C。理論上,我們好像可以依靠onDestroy回調來釋放view組件。而實際上,我們并不能這么做。因為我們沒有辦法強制客戶在不需要Document實例的時候,來調用外面的onDestroy方法。因此,為了防止內(nèi)存泄露,這里我們必須使用WeakReference。此外,由于view引用是通過setView方法而不是在構造函數(shù)中設置的,我們沒有辦法使用final域所提供的內(nèi)存可見性保證,所以這里我們將其設置為volatile,從而確保Document組件中的后臺線程能夠看到mReference域的變化。4.3處理配置改變引起的重復創(chuàng)建 默認情況下,當手機屏幕旋轉時,AndroidRuntime會自動銷毀當前的Activity并重建一個。此時,對于我們的Document-View架構來說,按照之前的實現(xiàn),勢必將會導致Document組件也重新創(chuàng)建。然而,一般情況下,這種創(chuàng)建確實完全不必要的。 為了避免Document組件的重復創(chuàng)建問題,我們可以利用Android的Fragment組件。只有設置fragment.setRetainInstance(true),當由于配置改變發(fā)生Activity的銷毀重建時,該fragment并不會被銷毀。然后,通過FragmentManager我們可以重新獲取該對象。只要我們將Document放置于Fragment中,便可以避免Document的重復創(chuàng)建。 實現(xiàn)代碼見附錄D。4.4網(wǎng)絡框架基于不重復制造輪子的原則,我們使用了OkHttp3作為網(wǎng)絡框架。OKHttp3提供了同步和異步兩種調用接口:使用同步接口時,客戶需要自己處理并發(fā)問題使用異步接口時,框架根據(jù)傳輸層協(xié)議的成功與否,調用相應的回調函數(shù)。由于我們并不想自己處理線程,并且其所提供的異步編程接口能夠滿足我們的需求,所以便直接使用了其異步編程接口。這里有一點值得一提的是,回調是在后臺線程執(zhí)行的。這個做法是必須的。當HTTP響應可讀時,回調函數(shù)即會執(zhí)行。這里的一個優(yōu)點是,我們可以對響應進行耗時較長的操作,而不會影響應用的響應度。缺點則是,我們無法直接將結果發(fā)布至UI線程。為了應對這個不足,可能的做法是:使用裝飾器模式,編寫一個裝飾器。裝飾器在這里所做的,便是將回調轉發(fā)至UI線程執(zhí)行。由于回調執(zhí)行時,讀取操作仍然會阻塞,所以這幾乎不會是什么好方法。應用適配器模式,我們重新定義一個回調接口,在后臺線程讀取完所有的數(shù)據(jù)后,再將數(shù)據(jù)傳遞至非阻塞接口。這個其實也是另一個網(wǎng)絡框架Volley的做法。但它有一個致命的缺點——不能用于數(shù)據(jù)量巨大的網(wǎng)絡請求,如文件下載。這個也是Volley沒有提供文件上傳、下載功能的原因。直接發(fā)布結果至View組件,由View組件自己進行線程同步。這個是我們所采用的方法。原因是,上面兩種做法都預先假定了View層不是線程安全的。這違反了信息隱藏的原則。在我們的Document-View架構中,兩種僅僅是通過接口引用對方,我們不希望對對方的實現(xiàn)有過多的猜測。它們只需要關系自己如何實現(xiàn)即可。此外,假定View組件不是線程安全的,這個做法本身也值得懷疑。假使某天我們的應用移植到了一個命令行界面,那我們還需要對UI做這么多的同步嗎?換句話說,只有View組件了解自己,只有它自己知道如何進行同步,所以,同步操作應該交由View組件自己去實現(xiàn)。4.5總體架構在OkHttp3的基礎上,整個應用實際上形成了一個分層架構。實際上,Document-View也僅僅是分層架構的一個特例而已。圖4.1整體架構5應用的實現(xiàn)利用我們前面所實現(xiàn)的Document-View框架,整個應用具體非常一致的結構。所以,這里僅僅是選取兩個比較典型的例子對實現(xiàn)部分進行說明。5.1重新實現(xiàn)注冊頁面首先,我們需要定義兩個Document-View的接口:interfaceRegisterViewOpsextendsOnDocumentFail{voidonRegisterSuccess(Stringemail,Stringpassword);voidonRegisterFail();voidonNetworkError();}interfaceRegisterDocumentOpsextendsBasicDocumentOps<RegisterViewOps>{voidonRegister(Stringname,Stringemail,Stringpassword);}這里需要注意的是,方法的名詞應該只與應用邏輯相關。我們絕對不應該對雙方的實現(xiàn)做出過多的假設。然后,在相應的事件發(fā)生時,調用對應的接口:publicvoidonClick(Viewv){getDocument().onRegister(…)}publicvoidonRegisterSuccess(finalStringemail,finalStringpassword){runOnUiThread(newRunnable(){publicvoidrun(){//dowhateveryouneeded}});}當用戶點擊“注冊”按鈕時,我們直接調用Document的onRegister方法,然后就只是靜靜地等待結果返回。當結果返回的時候,我們通過Activity的runOnUiThread方法進行線程的同步。而對于Document也是一樣的,它只需要實現(xiàn)兩個接口,其余的一概不需要過問:publicvoidonRegister(Stringname,Stringemail,Stringpassword){//posttheregisterinfousingOkHttp...}publicvoidonResponseSuccess(Responseresponse){RegisterViewOpsview=getView();if(view!=null){view.onRegisterSuccess();}}當結果放回時,Document便回調View組件的onRegisterSuccess()方法。通過使用我們所搭建的Document-View框架:這里我們不再需要自己顯式連接起Document和View組件不需要擔心由于配置改變引起的不必要的對象創(chuàng)建不會因為錯誤持有View引用而引發(fā)內(nèi)存泄露用戶界面與邏輯清晰分離使用框架的,幾乎不會產(chǎn)生什么重復的代碼5.2文件下載以模式語言的說法,,實現(xiàn)文件下載(文件上傳是類似的,這里便不再贅述)時,需要平衡一下幾個作用力:文件下載屬于耗時較長的操作,需要使用startedservice由于我們需要將下載進度體現(xiàn)在UI上,需要使用boundservice網(wǎng)絡速度的不可預知的,我們需要將進度“推”給View組件,而不能使用“拉”模型雖然Android的Service組件從語義上分為了startedservice和boundservice,并且有著各自不同的聲明周期,但是二者并不是互斥的。一個Service可以同時既是startedservice,也是boundservice。這正是我們所需要的。使用startedservice時,我們需要在客戶的請求執(zhí)行后,調用stopSelf()結束自己。由于可能同時有多個下載任務在進行,并且完成的順序是不確定的。我們需要一種機制,告訴我們,什么時候service應該停止。這里我們使用一個集合來保存當前正在下載的所有請求,文件下載完成時,通過判斷集合是否為空,即可決定是否應該停止自己。privatefinalObjectmLock=newObject();privatefinalSet<String>mDownloadingSet=newHashSet<>();publicvoidonResponseSuccess(Responseresponse){//...synchronized(mLock){mDownloadingSet.remove(mRemotePath);if(mDownloadingSet.isEmpty()){stopSelf();}}//...}由于網(wǎng)絡請求的回調是在后臺線程執(zhí)行的,并且可能同時有多個線程在執(zhí)行下載操作,我們需要對mDownloadingSet進行同步處理。為了將鎖對象封裝起來,這里我們使用了一個私有的鎖對象而不是直接使用Service實例。與此同時,我們將二者均聲明為final,以確保其內(nèi)存可見性。而對于操作的同步,則之間使用Java內(nèi)建的Monitor實現(xiàn)。為了將下載進度發(fā)布至View組件,這里我們需要了觀察者模式。通過跟蹤當前已下載的文件大小,我們可以得出下載的百分比。實現(xiàn)代碼見附錄E。6總結與展望6.1總結通過使用Document-View框架,整個應用有著非常一致的結構,這對于應用的維護和擴展是至關重要的。并且,由于框架實現(xiàn)非常的輕量,整個框架所實現(xiàn)的功能都有著高效的利用率。經(jīng)過大量實踐驗證的UI與邏輯分離的原則,使得應用的實現(xiàn)非常的輕松,多數(shù)頁面甚至編碼后可以一次運行便成功。但是,其實該框架實現(xiàn)也有著一些不足。由于Java的泛型實現(xiàn)需要向下兼容,引入了所謂的類型擦除。于是,當我們實現(xiàn)GenericActivity時,只得通過傳入一個Class<T>對象,并約定Document類的實現(xiàn)必須提供一個默認構造函數(shù)。雖然這保證了類型安全,但也就絕對談不上優(yōu)雅。再有一點,雖然我們約定在GenericActivity的子類必須回調超類的onCreateDocument方法,以實例化Document對象,但是,就目前語言所提供的工具而已,我們并能夠在語言層級約束客戶必須調用該方法。事實上,在應用編寫的過程中,我自己就曾有一次忘記調用該方法。好的設計總是有著一些折衷。為了類型安全和代碼的復用,我選擇了接受上述兩個缺點。6.2展望有些經(jīng)驗豐富的開發(fā)人員將模式視為一種思維工具,他們建議首先考慮這樣的模式,即并非可直接用于開發(fā)的應用程序的所屬領域。有時候,可將模式的重要理念推廣到另一個領域,進而得到全新的模式或原有模式的變種。除了編程語言,程序員的效率還依賴于庫、框架和一系列的中間件。合適的模式能夠幫助程序員理解并高效地使用它們。隨著越來越多的開發(fā)人員人數(shù)認識到模式的優(yōu)點,有希望涌現(xiàn)這樣的一系列模式——它們都是軟件經(jīng)驗的結晶。未來的框架文檔可能包含闡述如何有效使用框架的模式。參考文獻CayS.Horstmann,CaryCornel著,JAVA核心技術[M],卷一,卷二,第九版·英文版人民郵電出版社2013.07RetoMeier著,佘建偉、趙凱(譯),Android4高級編程[M](第3版)清華大學出版社2013.04.01JoshuaBloch著,楊春花、俞黎敏譯,EffectiveJava[M]中文版第2版機械工業(yè)出版社2009.01BrianGoetz,TimPeierls,JoshuaBloch,JosephBowbeer,DavidHolmes,DougLea著,童云蘭等譯,Java并發(fā)編程實戰(zhàn)[M],機械工業(yè)出版社2012.2ErichGamma,RichardHelm,RalphJohnson,JohnVlissides著,李英軍、馬曉星、蔡敏、劉建中等譯,設計模式:可復用面向對象軟件的基礎[M],機械工業(yè)出版社2000.9CraigLarman著,李洋、鄭?等譯,UML和模式應用[M]第三版,機械工業(yè)出版社2006.5任玉剛著,Android開發(fā)藝術探索[M],中國工信出版社,電子工業(yè)出版社2015FrankBuschmann,RegineMeunier,HansRohnert,PeterSommerlad,MichealStal著,Pattern-OrientedSoftwareArchitecture[M],Volume1–ASsytemofPatterns,JohnWiley&Sons,Ltd1996DouglasSchmidt,MichaelStal,HansRohnert,FrankBuschmann著,Pattern-OrientedSoftwareArchitecture[M],Volume2–PatternsforConcurrentandNetworkedObjects,JohnWiley&Sons,Ltd2000JamesGosling,BillJoy,GuySteele,GiladBrachaandAlexBuckley著,TheJavaLanguageSpecification[S],JavaSE8Edition.OracleAmerica,Inc.and/oritsaffiliates致謝本設計(論文)是在我的指導教師原玲副教授的親切關懷和悉心指導下完成的。他嚴肅的科學態(tài)度,嚴謹?shù)闹螌W精神,精益求精的工作作風,深深地感染和激勵著我。從題目的選擇到最終完成,原玲老師都始終給予我細心的指導和不懈的支持。附錄A一個簡單的注冊頁面實現(xiàn)publicclassRegisterActivityextendsActivityimplementsView.OnClickListener{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);//initview...}@OverridepublicvoidonClick(Viewv){//on"register"buttonclick//createanewthreadtoperformnetworkrequestnewThread(newRunnable(){@Overridepublicvoidrun(){//performnetworkrequest//andpublishresponsetotheUIthread}}).start();}}附錄BGenericActivitypublicabstractclassGenericActivity<ViewOps,DocumentOpsextendsBasicDocumentOps<ViewOps>>extendsAppCompatActivity{privateDocumentOpsmDocument;protectedvoidonCreateDocument(ViewOpsview,Class<?extendsDocumentOps>documentOpsClass){initDocument(documentOpsClass);

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論