MVC,MVP,MVVM的區(qū)別
-
MVC
軟件可以分為三部分 視圖(View):用戶界面 控制器(Controller):業(yè)務(wù)邏輯 模型(Model):數(shù)據(jù)保存 各部分之間的通信方式如下: View傳送指令到Controller Controller完成業(yè)務(wù)邏輯后,要求Model改變狀態(tài) Model將新的數(shù)據(jù)發(fā)送到View,用戶得到反饋 Tips:所有的通信都是單向的。 互動(dòng)模式 接受用戶指令時(shí),MVC可以分為兩種方式。一種是通過View接受指令,傳遞給Controller。 另一種是直接通過Controller接受指令
-
MVP
MVP模式將Controller改名為Presenter,同時(shí)改變了通信方向。 各部分之間的通信,都是雙向的 View和Model不發(fā)生聯(lián)系,都通過Presenter傳遞 View非常薄,不部署任何業(yè)務(wù)邏輯,稱為"被動(dòng)視圖"(Passive View),即沒有任何主動(dòng)性,而Presenter非常厚,所有邏輯都部署在那里。
-
MVVM
MVVM模式將Presenter改名為ViewModel,基本上與MVP模式完全一致。 唯一的區(qū)別是,它采用雙向綁定(data-binding):View的變動(dòng),自動(dòng)反映在ViewModel,反之亦然。
請(qǐng)解釋下在單線程模型中Message,Handler,Message Queue,Looper之間的關(guān)系
Handler是Android官方給我們提供的一套更新UI線程的機(jī)制,也是一套消息處理機(jī)制,可以通過Handler來(lái)處理消息,更新UI等。
拿主線程來(lái)說(shuō),主線程啟動(dòng)時(shí)會(huì)調(diào)用Looper.prepare()方法,會(huì)初始化一個(gè)Looper,放入Threadlocal中,接著調(diào)用Looper.loop()不斷遍歷Message Queue,
Handler的創(chuàng)建依賴與當(dāng)前線程中的Looper,如果當(dāng)前線程沒有Looper則必須調(diào)用Looper.prepare()。Handler , sendMessage到MessageQueue,Looper不斷
從MessageQueue中取出消息,回調(diào)handleMessage方法。
面試題:有沒有用過自定義View?
答:有用過,一般指定View都需要進(jìn)行這幾個(gè)步驟,首先可以自定義一些自己的屬性,在res/values/attrs.xml里面定義,然后在layout中使用,在View中通過context.obtainStyledAttributes(attrs,R.styleable.自定義屬性的名字)進(jìn)行獲取。
然后在測(cè)量onMeasure,一般通過他的三個(gè)模式(EXACTLY,AT_MODE,,UNSPECIFIED)進(jìn)行測(cè)量,調(diào)用setMeasuredDimension進(jìn)行傳入設(shè)置的值。
接著如果是ViewGroupt 的話我們還需要設(shè)置下子View的位置,一般是通過requestLayout去觸發(fā)onLayout的方法的。
最后在onDraw里面通過Canvas的一些方法進(jìn)行繪制。
如果需要進(jìn)行觸摸事件的話,一般需要有實(shí)現(xiàn)onTouchEvent事件,注意,如果需要多點(diǎn)觸摸,需要實(shí)現(xiàn)ACTION_POINTER_DOWN和ACTION_POINTER_UP進(jìn)行處理。
怎么啟動(dòng)Service?
答:有兩種啟動(dòng)方式,一種是通過startService進(jìn)行啟動(dòng),這個(gè)時(shí)候Service跟啟動(dòng)的Activity沒有關(guān)聯(lián),只有當(dāng)調(diào)用stopService的
時(shí)候才會(huì)結(jié)束Service,他的生命周期是:onCreate->onStartCommand->Service Run ->stopService->onDestory();
如果是通過bindService啟動(dòng)的,那么這個(gè)Service就跟啟動(dòng)他的進(jìn)程有關(guān)了,這個(gè)時(shí)候如果啟動(dòng)他的進(jìn)程銷毀了,那么這個(gè)Service也緊
跟著銷毀了或者直接調(diào)用unBindService,
生命周期是:onCreate->onBindService->Service Run->unBindService->onDestory.
面試題:廣播的動(dòng)態(tài)注冊(cè)和靜態(tài)注冊(cè)有什么區(qū)別?
靜態(tài)注冊(cè):在AndroidManifest.xml文件中進(jìn)行注冊(cè),當(dāng)App退出后,Receiver仍然可以接收到廣播并且進(jìn)行相應(yīng)的處理
動(dòng)態(tài)注冊(cè):在代碼中動(dòng)態(tài)注冊(cè),當(dāng)App退出后,也就沒辦法再接受廣播了。
面試題:動(dòng)畫有哪些類型,動(dòng)畫的區(qū)別?
在Android3.0以前,動(dòng)畫有兩種類型,一種是補(bǔ)間動(dòng)畫,即tween,他指的是通過自身的變形達(dá)到的效果,比如說(shuō)透明度的變化,放大縮小等,
還有一種是幀動(dòng)畫,即Frame,是通過一針一針的對(duì)圖片進(jìn)行連貫起來(lái)播放的,Android3.0的時(shí)候定義了一個(gè)屬性動(dòng)畫,即
PropertyAnimation,指的是控件的真實(shí)移動(dòng),就是不斷的改變某些屬性的值進(jìn)行的。具體可以通過實(shí)現(xiàn)ValueAnimator等類進(jìn)行實(shí)現(xiàn)。
如果后臺(tái)的Activity由于某原因被系統(tǒng)回收了,如何在被系統(tǒng)回收之前保存當(dāng)前狀態(tài)?
重寫onSaveInstanceState()方法,在此方法中保存需要保存的數(shù)據(jù),該方法將會(huì)在activity被回收之前調(diào)用。通過重寫
onRestoreInstanceState()方法可以從中提取保存好的數(shù)據(jù)
描述一下android的系統(tǒng)架構(gòu)
android系統(tǒng)架構(gòu)分從下往上為linux 內(nèi)核層、運(yùn)行庫(kù)、應(yīng)用程序框架層、和應(yīng)用程序?qū)印?
簡(jiǎn)述 android 應(yīng)用程序結(jié)構(gòu)是哪些
1)main code
2) unit test
3)mianifest
4)res->drawable,drawable-xxhdpi,layout,value,mipmap
mipmap 是一種很早就有的技術(shù)了,翻譯過來(lái)就是紋理映射技術(shù).
google建議只把啟動(dòng)圖片放入。
5)lib
6)color
什么是ANR 如何避免它?
1). KeyDispatchTimeout(5 seconds) --主要類型按鍵或觸摸事件在特定時(shí)間內(nèi)無(wú)響應(yīng)
2). BroadcastTimeout(10 seconds) --BroadcastReceiver在特定時(shí)間內(nèi)無(wú)法處理完成
3). ServiceTimeout(20 seconds) --小概率類型 Service在特定的時(shí)間內(nèi)無(wú)法處理完成
答:ANR:Application Not Responding。在Android中,活動(dòng)管理器和窗口管理器這兩個(gè)系統(tǒng)服務(wù)負(fù)責(zé)監(jiān)視應(yīng)用程序的響應(yīng),當(dāng)用戶
操作的在5s內(nèi)應(yīng)用程序沒能做出反應(yīng),BroadcastReceiver在10秒內(nèi)沒有執(zhí)行完畢,就會(huì)出現(xiàn)應(yīng)用程序無(wú)響應(yīng)對(duì)話框,這既是ANR。
避免方法:Activity應(yīng)該在它的關(guān)鍵生命周期方法(如onCreate()和onResume())里盡可能少的去做創(chuàng)建操作。潛在的耗時(shí)操作,例如
網(wǎng)絡(luò)或數(shù)據(jù)庫(kù)操作,或者高耗時(shí)的計(jì)算如改變位圖尺寸,應(yīng)該在子線程里(或者異步方式)來(lái)完成。主線程應(yīng)該為子線程提供一個(gè)
Handler,以便完成時(shí)能夠提交給主線程。
1)避免在activity里面做耗時(shí)操作,oncreate & onresume
2)避免在onReceiver里面做過多操作
3)避免在Intent Receiver里啟動(dòng)一個(gè)Activity,因?yàn)樗鼤?huì)創(chuàng)建一個(gè)新的畫面,并從當(dāng)前用戶正在運(yùn)行的程序上搶奪焦點(diǎn)。
4)盡量使用handler來(lái)處理UI thread & workthread的交互。
如何避免 OOM 異常
首先OOM是什么?
當(dāng)程序需要申請(qǐng)一段“大”內(nèi)存,但是虛擬機(jī)沒有辦法及時(shí)的給到,即使做了GC操作以后
這就會(huì)拋出 OutOfMemoryException 也就是OOM
Android的OOM怎么樣?
如何避免OOM
減少內(nèi)存對(duì)象的占用(減少bitmap的內(nèi)存占用)
內(nèi)存對(duì)象的重復(fù)利用(listView的復(fù)用)
ListView的優(yōu)化方案
1、如果自定義適配器,那么在getView方法中要考慮方法傳進(jìn)來(lái)的參數(shù)contentView是否為null,如果為null就創(chuàng)建contentView并返回,
如果不為null則直接使用。在這個(gè)方法中盡可能少創(chuàng)建view。
2、給contentView設(shè)置tag(setTag()),傳入一個(gè)viewHolder對(duì)象,用于緩存要顯示的數(shù)據(jù),可以達(dá)到圖像數(shù)據(jù)異步加載的效果。
3、如果listview需要顯示的item很多,就要考慮分頁(yè)加載。比如一共要顯示100條或者更多的時(shí)候,我們可以考慮先加載20條,等用戶拉到列
表底部的時(shí)候再去加載接下來(lái)的20條。
屏幕適配的方式:
xxxdpi, wrap_content,match_parent. 獲取屏幕大小,做處理。
dp來(lái)適配屏幕,sp來(lái)確定字體大小
weight,這是權(quán)重的適配。
AndroidAutoLayout和百分百布局
AsyncTask原理
AsyncTask是對(duì)Handler與線程池的封裝
BroadcastReciver和EventBus區(qū)別
EventBus又是什么鬼呢?EventBus是一個(gè)發(fā)布 / 訂閱的事件總線。
EventBus通過反射來(lái)調(diào)用訂閱
事件分發(fā)機(jī)制
我們知道事件攔截的順序,父ViewGroup先接收到攔截,再傳遞給子ViewGroup 或子View。事件的處理順序是,子ViewGroup 或子View先處理,若子ViewGroup處理了,父ViewGroup就不用處理,若子ViewGroup未處理,則傳給父ViewGroup處理。
其實(shí)當(dāng)你觸摸到控件時(shí),就會(huì)調(diào)用dispatchTouchEvent方法,以前面點(diǎn)擊Button為例,當(dāng)我們點(diǎn)擊該按鈕時(shí),應(yīng)用會(huì)去Button的類中尋找該方法,發(fā)現(xiàn)沒有,就會(huì)繼續(xù)往上找,找到容納該button的TextView時(shí),發(fā)現(xiàn)該類中仍沒有該方法,最后繼續(xù)往上尋找,找到View這時(shí)發(fā)現(xiàn)了該方法:
dispatchTouchEvent->onInterceptTouchEvent->onTouchEvent
首先我們必須了解三個(gè)方法
1.dispatchTouchEvent
2interceptTouchEvent
3.TouchEvent
下面先介紹第一個(gè):dispatchTouchEvent
public boolean diapatchTouchEvent
當(dāng)返回true //執(zhí)行自己
返回 false //執(zhí)行 onTouchEvent
默認(rèn) 是執(zhí)行 interceptTouchEvent
public boolean interceptToouchEvent
當(dāng)返回 true //由他自己的OnTouchEvent處理
返回 false //繼續(xù)分發(fā)
public boolean OnTouchEvent
返回 true 自己處理
返回 false 繼續(xù)傳遞分發(fā)
默認(rèn)繼續(xù)傳遞
在view類中有dispatchTouchEvent和onTouchEvent兩個(gè)方法
而ViewGroup是繼承View,包含這兩個(gè)方法,并且還包含onInterceptTouchEvent方法
從用戶點(diǎn)擊屏幕開始觸發(fā)一個(gè)系列的點(diǎn)擊事件時(shí),事件真正的傳遞流程是:Activity(PhoneWindow)->DecorView->ViewGroup->View,在到達(dá)ViewGroup之前還有一個(gè)DecorView,事件是從Activity傳過來(lái)的,但這些東西其實(shí)和ViewGroup的原理是一樣的,Activity能看做一個(gè)大的ViewGroup,當(dāng)它的DecorView包含的所有子View沒有人能夠消耗事件的時(shí)候(這樣說(shuō)有漏洞,大家懂我的意思就行了)最后還是會(huì)交給Activity處理。
-
如何不被攔截
如果ViewGroup的onInterceptTouchEvent(ev) 當(dāng)ACTION_MOVE時(shí)return true ,即攔截了子View的MOVE以及UP事件;
此時(shí)子View希望依然能夠響應(yīng)MOVE和UP時(shí)該咋辦呢?
Android給我們提供了一個(gè)方法:requestDisallowInterceptTouchEvent(boolean) 用于設(shè)置是否允許攔截,我們?cè)谧覸iew的dispatchTouchEvent中直接這么寫:
前面我們了解到了我們的View是樹形結(jié)構(gòu)的,基于這樣的結(jié)構(gòu),我們的事件可以進(jìn)行有序的分發(fā)。
事件收集之后最先傳遞給 Activity, 然后依次向下傳遞,大致如下:
Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View
這樣的事件分發(fā)機(jī)制邏輯非常清晰,可是,你是否注意到一個(gè)問題?如果最后分發(fā)到View,如果這個(gè)View也沒有處理事件怎么辦,就這樣讓事件浪費(fèi)掉?
當(dāng)然不會(huì)啦,如果沒有任何View消費(fèi)掉事件,那么這個(gè)事件會(huì)按照反方向回傳,最終傳回給Activity,如果最后 Activity 也沒有處理,本次事件才會(huì)被拋棄:
Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View