1. 熟練掌握J(rèn)ava技術(shù)攻柠,熟悉面向?qū)ο笏枷氡惩煜こS迷O(shè)計(jì)模式
熟練掌握J(rèn)ava技術(shù)系宜,熟悉面向?qū)ο笏枷肫锿瑁煜こS迷O(shè)計(jì)模式
面向?qū)ο笏枷? 繼承, 封裝, 多態(tài)
設(shè)計(jì)模式:
六大原則
- 單一職責(zé)(Single Responsibility Principle)
對(duì)于單一職責(zé)原則俺叭,我的建議是接口一定要做到單一職責(zé)恭取,類(lèi)的設(shè)計(jì)盡量做到只有一個(gè)原因引起變化。
- 里氏替換原則(Liskov Substitution Principle)
所有引用基類(lèi)的地方必須能透明地使用其子類(lèi)的對(duì)象熄守。 - 迪米特法則(Law of Demeter,LoD)
一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有最少的了解蜈垮。通俗地講,一個(gè)類(lèi)應(yīng)該對(duì)自己需要耦合或調(diào)用的類(lèi)知道得最少 - 接口隔離原則(Interface Segregation Principle)
- 接口要盡量小
- 接口要高內(nèi)聚
- 定制服務(wù)
- 接口設(shè)計(jì)是有限度的
- 依賴(lài)倒置原則(Dependence Inversion Principle,DIP)
- 高層模塊不應(yīng)該依賴(lài)低層模塊裕照,兩者都應(yīng)該依賴(lài)其抽象攒发;
- 抽象不應(yīng)該依賴(lài)細(xì)節(jié);
- 細(xì)節(jié)應(yīng)該依賴(lài)抽象
- 開(kāi)閉原則(Open Closed Principle)
開(kāi)閉原則是一個(gè)終極目標(biāo)晋南,任何人包括大師級(jí)人物都無(wú)法百分之百做到惠猿,但朝這個(gè)方向努力,可以非常顯著地改善一個(gè)系統(tǒng)的架構(gòu)搬俊,真正做到“擁抱變化”紊扬。
把這 6 個(gè)原則的首字母(里氏替換原則和迪米特法則的首字母重復(fù)蜒茄,只取一個(gè))聯(lián)合起來(lái)就是SOLID(穩(wěn)定的)唉擂,其代表的含義也就是把這6個(gè)原則結(jié)合使用的好處:建立穩(wěn)定、靈活檀葛、健壯的設(shè)計(jì)玩祟,而開(kāi)閉原則又是重中之重,是最基礎(chǔ)的原則屿聋,是其他 5 大原則的精神領(lǐng)袖空扎。
常用設(shè)計(jì)模式
- 單例模式
- 迭代器模式
- 適配器模式
- 策略模式
- 代理模式
2. Android 四大組件 和 Fragment的使用;
3. Android中的數(shù)據(jù)存儲(chǔ)(文件, 網(wǎng)絡(luò), 數(shù)據(jù)庫(kù)存儲(chǔ))
4. 熟悉掌握Android中常用的UI元素, 動(dòng)畫(huà), 樣式
動(dòng)畫(huà)
android 3.0 新增屬性動(dòng)畫(huà)(Object, Value)
android 5.0 新增矢量圖動(dòng)畫(huà)
通常定義一個(gè) AnimatedVectorDrawable 需要以下三個(gè)xml文件:
1.vector drawable 本身:res/drawable/中定義一個(gè)有<vector>元素的xml文件,參考上面對(duì)VectorDrawable的定義润讥。
2.vector drawable 的動(dòng)畫(huà)文件(Animated vector drawable):res/drawable/中定義一個(gè)有<animated-vector>元素的xml文件转锈。
3.一個(gè)或者多個(gè)屬性動(dòng)畫(huà)文件:res/drawable/中定義一個(gè)有<objectAnimator>元素的xml文件。
自定義 View
- 完全自定義 View
- 繼承已有 View, 重寫(xiě)部分功能
- 繼承 ViewGroup
步驟
- 自定義屬性的聲明和獲取
- 測(cè)量 onMeasure
- 布局 onLayout(ViewGroup)
- 繪制 onDraw
- onTouchEvent
- onInterceptTouchEvent(ViewGroup) 是否攔截該手勢(shì)
5. 消息機(jī)制和多線程的使用
消息機(jī)制和多線程的使用
UI線程的消息循環(huán)是在 ActivityThread 方法中創(chuàng)建的, 該函數(shù)為 Android 應(yīng)用程序的入口.
在 Android 應(yīng)用啟動(dòng)的時(shí)候, 會(huì)默認(rèn)有一個(gè)主線程(UI線程), 這個(gè)線程會(huì)關(guān)聯(lián)一個(gè)消息隊(duì)列, 所有操作都會(huì)被封裝成消息交給主線程來(lái)處理.
Looper 總結(jié):
- 通過(guò) Looper.prepare 來(lái)創(chuàng)建 looper 對(duì)象(消息隊(duì)列封裝在 Looper 對(duì)象中), 并且保存在 sThreadLocal 中楚殿。
- 消息循環(huán)的建立通過(guò) Looper.loop() 方法, loop 方法實(shí)際上是建立一個(gè)死循環(huán), 不斷從消息隊(duì)列中取出消息撮慨。
Handler 將消息發(fā)送給消息隊(duì)列, 消息隊(duì)列又將消息分發(fā)給 Handler 來(lái)處理。
Message 的兩個(gè)重要成員變量
Hander target;
Runnable callback;
不管是 post 一個(gè)Runnable(包裝成Message, callback設(shè)置為runnable成員變量)還是sendMessage, 都會(huì)調(diào)用sendMessageDelayed(msg, time)方法, Handler 最終將消息追加到 MessageQueue中, 而 Looper 不斷取出從MessageQueue 中讀取消息, 并調(diào)用 Handler的dispathMessage 來(lái)處理消息.
dispathMessage 方法是一個(gè)分發(fā)方法, 如果 Runnable 類(lèi)型的 callback 變量不為空, 則 callback.run 中執(zhí)行更新UI的代碼, 否則會(huì)走到 handMessage 這個(gè)分支.
Handle r構(gòu)造方法中通過(guò) Looper.myLooper 來(lái)獲取 Looper 對(duì)象.
//1. 成員變量mLooper的獲得
mLooper = Looper.myLooper();
//1. Looper.myLooper的源碼
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
安卓中多線程的實(shí)現(xiàn)
使用 JavaSE中線程 和 線程池,
安卓為開(kāi)發(fā)者封裝了一些常用的類(lèi) AsyncTask, HandlerThread.
線程池的優(yōu)點(diǎn)
- 重用存在的線程, 減少對(duì)象創(chuàng)建,銷(xiāo)毀的開(kāi)銷(xiāo)
- 有效控制最大的并發(fā)線程數(shù), 提高系統(tǒng)資源的使用率, 同時(shí)避免過(guò)多資源競(jìng)爭(zhēng), 避免堵塞
- 提供定時(shí)執(zhí)行, 單線程, 并發(fā)數(shù)控制等功能
在android平臺(tái), 由于資源有限, 最常用的就是通過(guò)Executors.newFixedThreadPool(int size)來(lái)啟動(dòng)固定數(shù)量的線程池.
AsyncTask 的原理
onPreExecute
doInBackground
onProgressUpdate
onPostExecute
概括來(lái)說(shuō), 調(diào)用 execute 方法后, execute方法先調(diào)用 onPreExecute 方法,
然后由 ThreadPoolExecutors 實(shí)例 sExecutor 執(zhí)行一個(gè) FutureTask 任務(wù), 這個(gè)過(guò)程中 doInBackground 將被調(diào)用, 如果在 doInBackground 中調(diào)用了publicProgress 方法, 則通過(guò) sHandler 發(fā)送一條MESSAGE_POST_PROGRESS 消息, 更新進(jìn)度;
如果遇到異常, 則發(fā)送一條 MESSAGE_POST_CANCEL消息, sHandler處理消息時(shí), onCancelled 方法會(huì)被調(diào)用;
如果執(zhí)行成功, 則發(fā)送一條 MESSAGE_POST_RESULT 的消息, sHandler處理消息時(shí)會(huì)調(diào)用 onPostExectute(result)方法, 讓用戶得以在 UI 線程處理結(jié)果.
總的來(lái)說(shuō), AsyncTask 的本質(zhì)是使用線程池技術(shù) 和 Handler 封裝, 減少了處理問(wèn)題的復(fù)雜度, 提高了開(kāi)發(fā)效率脆粥。
HandlerThread 的使用
6. 網(wǎng)絡(luò)通信機(jī)制及常用數(shù)據(jù)傳輸協(xié)議砌溺;
HTTP網(wǎng)絡(luò)請(qǐng)求原理
HTTP 是一種應(yīng)用層協(xié)議, 通過(guò) TCP 實(shí)現(xiàn)了可靠的數(shù)據(jù)傳輸, 能夠保證可靠的數(shù)據(jù)傳輸.
消息的交互流程有如下幾步:
- 客戶端執(zhí)行網(wǎng)絡(luò)請(qǐng)求, 從URL解析出服務(wù)器的主機(jī)名
- 將服務(wù)器的主機(jī)名轉(zhuǎn)換為服務(wù)器的IP地址;
- 將端口號(hào)從URL中解析出來(lái)
- 建立一條客戶端與Web服務(wù)器的TCP連接;
- 客戶端通過(guò)輸出流向服務(wù)器發(fā)送一條HTTP請(qǐng)求
- 服務(wù)器向客戶端回送一條HTTP響應(yīng)報(bào)文
- 客戶端從輸入流獲取報(bào)文
- 客戶端解析報(bào)文, 關(guān)閉連接
- 客戶端將結(jié)果顯示在UI上
HTTP的請(qǐng)求方式(7種)
get
post
put
delete
trace
options
head
Android中執(zhí)行網(wǎng)絡(luò)請(qǐng)求
- 全面支持 HTTP 協(xié)議的HttpClient(在android2.3以前), 在android6.0 中該庫(kù)已被移除
- 最佳選擇 HttpURLConnection
網(wǎng)絡(luò)框架的簡(jiǎn)單實(shí)現(xiàn)
7. Android中的屏幕適配
8. Android中的布局優(yōu)化, 內(nèi)存優(yōu)化
布局優(yōu)化
- 減少視圖層級(jí)
- 通過(guò)工具分析視圖層級(jí), 優(yōu)先相對(duì)布局, 約束布局
- merge標(biāo)簽, 去處理子布局的根視圖和父布局是同一類(lèi)型的情況
- 延遲加載的ViewStub
通過(guò)這個(gè)不可見(jiàn)的和能在運(yùn)行期間延遲加載目標(biāo)視圖的, 寬高都為0的View.
內(nèi)存優(yōu)化
檢查自身可以內(nèi)存
每個(gè)app都有heap限制, 可以通過(guò)調(diào)用getMemory來(lái)獲取可用heap大小-
知曉內(nèi)存的開(kāi)支情況
- 使用枚舉通常會(huì)比使用靜態(tài)常量要消耗兩倍以上的內(nèi)存,在Android開(kāi)發(fā)當(dāng)中我們應(yīng)當(dāng)盡可能地不使用枚舉变隔。
- 任何一個(gè)Java類(lèi)规伐,包括內(nèi)部類(lèi)、匿名類(lèi)匣缘,都要占用大概500字節(jié)的內(nèi)存空間猖闪。
- 任何一個(gè)類(lèi)的實(shí)例要消耗12-16字節(jié)的內(nèi)存開(kāi)支鲜棠,因此頻繁創(chuàng)建實(shí)例也是會(huì)一定程序上影響內(nèi)存的。
- 在使用 HashMap 時(shí)萧朝,即使你只設(shè)置了一個(gè)基本數(shù)據(jù)類(lèi)型的鍵岔留,比如說(shuō)int,但是也會(huì)按照對(duì)象的大小來(lái)分配內(nèi)存检柬,大概是 32 字節(jié)献联,而不是 4 字節(jié)。因此最好的辦法就是像上面所說(shuō)的一樣何址,使用優(yōu)化過(guò)的數(shù)據(jù)集合里逆。
注意內(nèi)存的開(kāi)銷(xiāo), 使用專(zhuān)門(mén)給為 android 優(yōu)化過(guò)的數(shù)據(jù)容器 SparseArray, SparseBoolArray, LongSparseArray, 比 HashMap 消耗更少的內(nèi)存.通常的HashMap 的實(shí)現(xiàn)方式更加消耗內(nèi)存,因?yàn)樗枰粋€(gè)額外的實(shí)例對(duì)象來(lái)記錄 Mapping 操作用爪。另外原押,SparseArray 更加高效在于他們避免了對(duì) key 與value 的 autobox 自動(dòng)裝箱,并且避免了裝箱后的解箱偎血。
棄用枚舉類(lèi)型而使用加上 IntDef, StringDef 注解修飾的全局常量-
bitmap 的優(yōu)化
- 千萬(wàn)不要去加載不需要的分辨率, 會(huì)占用我們相當(dāng)多寶貴的內(nèi)存
- 圖片的色彩格式, 來(lái)壓縮圖片質(zhì)量
ARGB_8888 代表32位ARGB位圖
ARGB_4444 代表16位ARGB位圖
RGB_565 代表8位RGB位圖 - 使用成熟的圖片框架Picasso, ImageLoader
當(dāng)內(nèi)存緊張時(shí)釋放內(nèi)存
onTrimMemory()方法還有很多種其它類(lèi)型的回調(diào)诸衔,可以在手機(jī)內(nèi)存降低的時(shí)候及時(shí)通知我們。我們應(yīng)該根據(jù)回調(diào)中傳入的級(jí)別來(lái)去決定如何釋放應(yīng)用程序的資源:善用 service 資源
系統(tǒng)會(huì)傾向于將這個(gè) Service 所依賴(lài)的進(jìn)程進(jìn)行保留. 因?yàn)閟ervice的運(yùn)行代價(jià)很高. 例如使用 IntentService 處理一些單一短時(shí)間任務(wù), 這種 Service 的最大特點(diǎn)就是當(dāng)后臺(tái)任務(wù)執(zhí)行結(jié)束后會(huì)自動(dòng)停止颇玷,從而極大程度上避免了Service 內(nèi)存泄漏的可能性笨农。為序列化的數(shù)據(jù)使用 nano protobufs
盡量避免使用依賴(lài)注入框架
謹(jǐn)慎使用 external libraries
關(guān)注 lint 工具所提出的建議
使用 ProGuard 來(lái)剔除不需要的代碼
能夠通過(guò)移除不需要的代碼,重命名類(lèi)帖渠,域與方法等方對(duì)代碼進(jìn)行壓縮谒亦,優(yōu)化與混淆。使用 ProGuard可以是的你的代碼更加緊湊空郊,這樣能夠使用更少mapped代碼所需要的RAM份招。對(duì)最終的 APK 使用 zipalign
使用多進(jìn)程
一個(gè)典型的例子是創(chuàng)建一個(gè)可以長(zhǎng)時(shí)間后臺(tái)播放的Music Player。如果整個(gè)app運(yùn)行在一個(gè)進(jìn)程中狞甚,當(dāng)后臺(tái)播放的時(shí)候锁摔,前臺(tái)的那些UI資源也沒(méi)有辦法得到釋放。類(lèi)似這樣的 app 可以切分成2個(gè)進(jìn)程:一個(gè)用來(lái)操作UI哼审,另外一個(gè)用來(lái)后臺(tái)的Service.
你可以通過(guò)在manifest文件中聲明’android:process’屬性來(lái)實(shí)現(xiàn)某個(gè)組件運(yùn)行在另外一個(gè)進(jìn)程的操作谐腰。謹(jǐn)慎使用抽象編程
許多程序員都喜歡各種使用抽象來(lái)編程,認(rèn)為這是一種很好的編程習(xí)慣棺蛛。當(dāng)然怔蚌,這一點(diǎn)不可否認(rèn),因?yàn)榈某橄蟮木幊谭椒ǜ用嫦驅(qū)ο笈陨蓿以诖a的維護(hù)和可擴(kuò)展性方面都會(huì)有所提高桦踊。但是,在 Android 上使用抽象會(huì)帶來(lái)額外的內(nèi)存開(kāi)支终畅,因?yàn)槌橄蟮木幊谭椒ㄐ枰帉?xiě)額外的代碼籍胯,雖然這些代碼根本執(zhí)行不到竟闪,但是卻也要映射到內(nèi)存當(dāng)中,不僅占用了更多的內(nèi)存杖狼,在執(zhí)行效率方面也會(huì)有所降低炼蛤。當(dāng)然這里我并不是提倡大家完全不使用抽象編程,而是謹(jǐn)慎使用抽象編程蝶涩,不要認(rèn)為這是一種很酷的編程方式而去肆意使用它理朋,只在你認(rèn)為有必要的情況下才去使用。
9. Android中的單元測(cè)試
優(yōu)點(diǎn)
- 為代碼提供保障
- 優(yōu)化設(shè)計(jì), 編寫(xiě)單元測(cè)試從調(diào)用者角度觀察, 迫使設(shè)計(jì)者吧程序設(shè)計(jì)成易于調(diào)試和可測(cè)試, 并且消除軟件中的耦合.
- 文檔記錄, 是一種展示函數(shù)或者類(lèi)使用的最佳文檔
- 具有回歸性, 編寫(xiě)完成后可以隨時(shí)快速測(cè)試.
JUnit簡(jiǎn)介
基于Java語(yǔ)言的單元測(cè)試框架.
開(kāi)發(fā)人員一般需要新建一個(gè)TestCase的類(lèi), 然后在該測(cè)試類(lèi)中添加測(cè)試函數(shù).
需要注意的是, 每個(gè)測(cè)試方法, TestCase之間并沒(méi)有關(guān)聯(lián), 它們的執(zhí)行順序也不一定是代碼中的執(zhí)行順序, 因此, 測(cè)試方法不要存在依賴(lài)性.
測(cè)試哪些條件
- 邊界條件
是單元測(cè)試需要重要測(cè)試的地方 - 覆蓋執(zhí)行路徑
模擬所需的功能模塊
- 手動(dòng)mock對(duì)象
- 使用Mockito庫(kù)
Android中單元測(cè)試
Google在Junit的基礎(chǔ)上進(jìn)行拓展, 使之能在Android上運(yùn)行測(cè)試實(shí)例, Android平臺(tái)下所有的測(cè)試類(lèi)都是InstrumentationTestCase的子類(lèi), 它的內(nèi)部封裝了Instrumentation對(duì)四大組件進(jìn)行操作, 而InstrumentationTestCase繼承在Junit的TestCase.
- 需要Context的測(cè)試用例
AndroidTestCase - AcitivityUnitTestCase<T>
和 ActivityInstrumentationTestCase2<T> - 測(cè)試Service, 繼承自ServiceTestCase<T>
- 測(cè)試ContentPrivider, 繼承自ContentPrividerTestCase2<T>
10. 網(wǎng)絡(luò)框架Volley, 圖片處理Picasso等;
第一部分Request
第二部分RequestQueue消息隊(duì)列, 維護(hù)了提交我給網(wǎng)絡(luò)框架的請(qǐng)求隊(duì)列, 并根據(jù)對(duì)應(yīng)規(guī)則進(jìn)行排序, 該隊(duì)列使用的線程安全的PriorityBlockingQueue, 所以支持并發(fā)訪問(wèn).
第三部分NetWorkExecutor, 也就是網(wǎng)絡(luò)的執(zhí)行者, 該Exectuor繼承自Thread, 在run方法中循環(huán)訪問(wèn)請(qǐng)求隊(duì)列, 從請(qǐng)求隊(duì)列中獲取網(wǎng)絡(luò)請(qǐng)求, 請(qǐng)求完成后提交給UI線程
第四部分Response及其投遞類(lèi), 使用ResponseDelivery來(lái)封裝Response的投遞, 保證Response在UI線程中執(zhí)行, Response會(huì)根據(jù)用戶的不同需求返回特定的類(lèi)型.
Picasso
Picasso不僅實(shí)現(xiàn)了圖片異步加載的功能绿聘,還解決了android中加載圖片時(shí)需要解決的一些常見(jiàn)問(wèn)題:
1.在adapter中需要取消已經(jīng)不在視野范圍的ImageView圖片資源的加載嗽上,否則會(huì)導(dǎo)致圖片錯(cuò)位,Picasso已經(jīng)解決了這個(gè)問(wèn)題熄攘。
2.使用復(fù)雜的圖片壓縮轉(zhuǎn)換來(lái)盡可能的減少內(nèi)存消耗
3.自帶內(nèi)存和硬盤(pán)二級(jí)緩存功能
Cache兽愤,緩存類(lèi)
Lrucache,主要是get和set方法挪圾,存儲(chǔ)的結(jié)構(gòu)采用了LinkedHashMap浅萧,這種map內(nèi)部實(shí)現(xiàn)了lru算法(Least Recently Used 近期最少使用算法)。
Request哲思,操作封裝類(lèi)
所有對(duì)圖形的操作都會(huì)記錄在這里洼畅,供之后圖形的創(chuàng)建使用
Action
Action 代表了一個(gè)具體的加載任務(wù),主要用于圖片加載后的結(jié)果回調(diào)也殖,有兩個(gè)抽象方法土思,complete 和error务热,也就是當(dāng)圖片解析為bitmap后用戶希望做什么忆嗜。最簡(jiǎn)單的就是將 bitmap 設(shè)置給 imageview,失敗了就將錯(cuò)誤通過(guò)回調(diào)通知到上層崎岂。
ImageViewAction 實(shí)現(xiàn)了Action捆毫,在complete中將 bitmap 和 imageview 組成了一個(gè) PicassoDrawable,里面會(huì)實(shí)現(xiàn)淡出的動(dòng)畫(huà)效果冲甘。
BitmapHunter
BitmapHunter 是一個(gè) Runnable绩卤,其中有一個(gè) decode 的抽象方法,用于子類(lèi)實(shí)現(xiàn)不同類(lèi)型資源的解析江醇。
在 bitmaphunter 成功得到 bitmap 后濒憋,就是通過(guò) dispatcher 將結(jié)果傳遞出去的,當(dāng)然讓 bitmaphunter 執(zhí)行也要通過(guò) Dispatcher陶夜。
Dispatcher 內(nèi)有一個(gè) HandlerThread凛驮,所有的請(qǐng)求都會(huì)通過(guò)這個(gè) thread 轉(zhuǎn)換,也就是請(qǐng)求也是異步的条辟,這樣應(yīng)該是為了Ui線程更加流暢黔夭,同時(shí)保證請(qǐng)求的順序宏胯,因?yàn)?handler 的消息隊(duì)列。外部調(diào)用的是 dispatchXXX 方法本姥,然后通過(guò)handler將請(qǐng)求轉(zhuǎn)換到對(duì)應(yīng)的 performXXX 方法肩袍。例如生成 Action 以后就會(huì)調(diào)用 dispather 的 dispatchSubmit()來(lái) 請(qǐng)求執(zhí)行,
handler接到消息后轉(zhuǎn)換到 performSubmit 方法
這里將通過(guò) action 得到具體的 BitmapHunder婚惫,然后交給 ExecutorService 執(zhí)行氛赐。
下面是 Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView)的過(guò)程
public static Picasso with(Context context) {
if (singleton == null) {
singleton = new Builder(context).build();
}
return singleton;
}
public Picasso build() {
Context context = this.context;
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
if (cache == null) {
cache = new LruCache(context);
}
if (service == null) {
service = new PicassoExecutorService();
}
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}
Stats stats = new Stats(cache);
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER,
downloader, cache, stats);
return new Picasso(context, dispatcher, cache, listener,
transformer, stats, debugging);
}
在 Picasso.with() 的時(shí)候會(huì)將執(zhí)行所需的所有必備元素創(chuàng)建出來(lái),如緩存cache先舷、執(zhí)行 executorService鹰祸、調(diào)度 dispatch 等,在load()時(shí)創(chuàng)建Request密浑,在into()中創(chuàng)建action蛙婴、bitmapHunter,并最終交給 dispatcher 執(zhí)行尔破。
11. 目前流行的 MVP 模式構(gòu)建應(yīng)用
全稱(chēng) Model View Presenter
MVP 讓 UI 界面和數(shù)據(jù)分離, 解除 View 和 Model 直接的耦合街图。
MVP(Model-View-Presenter)是MVC的演化版本,MVP的角色定義如下懒构。* Model:主要提供數(shù)據(jù)的存取功能餐济。Presenter需要通過(guò)Model層來(lái)存儲(chǔ)、獲取數(shù)據(jù)胆剧。
- View:負(fù)責(zé)處理用戶事件和視圖部分的展示絮姆。在Android中,它可能是Activity秩霍、Fragment類(lèi)或者是某個(gè)View控件篙悯。
- Presenter:作為View和Model之間溝通的橋梁,它從Model層檢索數(shù)據(jù)后返回給View層铃绒,使得View和Model之間沒(méi)有耦合鸽照。
在MVP里(見(jiàn)圖10-2),Presenter完全將Model和View進(jìn)行了分離颠悬,主要的程序邏輯在Presenter里實(shí)現(xiàn)矮燎。而且,Presenter與具體的View是沒(méi)有直接關(guān)聯(lián)的赔癌,而是通過(guò)定義好的接口進(jìn)行交互诞外,從而使得在變更View時(shí)可以保持Presenter的不變,這點(diǎn)符合面向接口編程的特點(diǎn)灾票。View只應(yīng)該有簡(jiǎn)單的Set/Get方法峡谊,以及用戶輸入和設(shè)置界面顯示的內(nèi)容,除此之外就不應(yīng)該有更多的內(nèi)容。絕不允許View直接訪問(wèn)Model靖苇,這就是其與MVC的很大不同之處席噩。
public interface BasePresenter {
void start();
}
public interface BaseView<T> {
void setPresenter(T presenter);
}
/**
* This specifies the contract between the view and the presenter.
*/
public interface StatisticsContract {
interface View extends BaseView<Presenter> {
void setProgressIndicator(boolean active);
void showStatistics(int numberOfIncompleteTasks, int numberOfCompletedTasks);
void showLoadingStatisticsError();
boolean isActive();
}
interface Presenter extends BasePresenter {
}
}
/**
* Listens to user actions from the UI ({@link TaskDetailFragment}), retrieves the data and updates
* the UI as required.
*/
public class TaskDetailPresenter implements TaskDetailContract.Presenter {
private final TasksRepository mTasksRepository;
private final TaskDetailContract.View mTaskDetailView;
@Nullable
private String mTaskId;
public TaskDetailPresenter(@Nullable String taskId,
@NonNull TasksRepository tasksRepository,
@NonNull TaskDetailContract.View taskDetailView) {
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");
mTaskDetailView.setPresenter(this);
}
@Override
public void start() {
openTask();
}
....
/**
* Main UI for the task detail screen.
*/
public class TaskDetailFragment extends Fragment implements TaskDetailContract.View {
```java
@Override
public void onResume() {
super.onResume();
mPresenter.start();
}
一些View層的操作
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_EDIT_TASK) {
// If the task was edited successfully, go back to the list.
if (resultCode == Activity.RESULT_OK) {
getActivity().finish();
}
}
}
@Override
public void showEditTask(@NonNull String taskId) {
Intent intent = new Intent(getContext(), AddEditTaskActivity.class);
intent.putExtra(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID, taskId);
startActivityForResult(intent, REQUEST_EDIT_TASK);
}
這種情況下給 present
// Fragment#onCreateView中
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.editTask();
}
});
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_delete:
mPresenter.deleteTask();
return true;
}
return false;
}