天天看點

Android面試題集錦(二)

2016.7.22更新...........................................................................

(33):Activity間通過Intent傳遞資料大小有沒有限制?

        有的,大小為40k;

(34):Android中可以從主界面點選圖示進入程式,也可以從一個程式中跳轉過去,兩者有何差別呢?

        從主界面點選圖示進去,預設開啟的是<action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" />的Activity,但是從程式跳轉到的Activity可以是任意的,隻要你的Intent比對了某一Activity,這個Activity就會被顯示出來的;也就是說通過主界面圖示啟動的話,相對比較單一,隻能啟動指定啟動的那個Activity,程式跳轉啟動的話更加靈活,隻要滿足的比對條件就可以啟動,但他們本質上是相同的;

(35):Android中的5大布局

        RelativeLayout(相對布局)、LinearLayout(線性布局)、FrameLayout(幀布局)、AbsoluteLayout(絕對布局)、TableLayout(表格布局)

(36):同一個應用中的不同Activity可以放到不同的Task任務棧中麼?

        可以的,Activity中的android:taskAffinity屬性就是為我們的目前Activity指定宿主棧也就是任務棧的,通常情況下如果我們不設定的話,屬性值預設就是Manifest包名,我們可以在程式中通過Intent設定将要跳轉的Activity所屬的任務棧,具體來說就是讓Intent包含FLAG_ACTIVITY_NEW_TASK标記;

(37):DVM程序、Linux程序和應用程式程序是同一概念嗎?

        DVM程序是Dalvik虛拟機程序,他是Linux中的一個程序,是以DVM程序和Linux程序是一個程序;每個Android應用程式都運作在自己的程序中,而應用程式的這個程序是DVM配置設定出來的,确切的講他擁有的是DVM虛拟機的執行個體,每來一個應用程式都會通過DVM啟動時候開啟的Zygote程序fork一個DVM執行個體也就是子程序出來給了新的應用程式,系統會給他配置設定一個單獨的Linux UID,因而可以認為他們是同一個概念;從這裡也可以看出來,每個應用實際上是作為一個獨立的Linux程序存在的,可以防止虛拟機奔潰的情況下所有程式都關閉;

(38):DVM和JVM的簡單差別

        (1):Davlik是基于寄存器的,他将局部變量放在65536個可用寄存器中的任何一個,而JVM是基于棧的,他把局部變量放到棧中;

        (2):java虛拟機運作的是java位元組碼,Dalvik虛拟機運作的是其專有的檔案格式Dex(Dalvik Executable);

        (3):DVM主要負責的是程序隔離和線程隔離,因為每個應用程式在底層都會對應一個Dalvik虛拟機執行個體;

        (4):因為所有的Android應用的線程都對應一個Linux線程,是以DVM可以借助作業系統的線程排程和管理機制;

        (5):對于每個應用程式的虛拟機執行個體是怎麼得到的呢?是通過稱為孵化器的Zygote特殊虛拟機程序完成的,他再系統啟動的時候被建立,當我們開啟新的應用程式的時候會通過他來fork一個新的虛拟機執行個體出來分給新的應用程式;

(39):Activity之間傳遞資料的方式

        (1):使用Bundle封裝資料,通過Intent傳遞

Bundle bundle = new Bundle();//建立Bundle
				Book book = new Book("Android開發藝術探索", 79);
				bundle.putParcelable("book", book);
				Intent intent = new Intent(MainActivity.this, OtherActivity.class);//建立Intent對象
				intent.putExtras(bundle);//将攜帶有資料的Bundle對象附加到Intent裡面
				startActivity(intent);
           

        通過将需要傳遞的資料封裝到Bundle裡面,随後将該Bundle添加到Intent裡面就可以發送出去了,有一點需要注意的是封裝到Bundle中的資料必須是可以序列化的對象,也就是必須是基本資料類型或者實作了Serialiable或者實作了Parcelable接口的對象才可以;

        (2):當然你可以直接使用Intent的一系列put方法進行資料傳遞,不需要Bundle進行封裝;

        (3):使用Intent傳遞對象有個缺點就是對象必須是可以序列化的,如果遇到不可以序列化的對象該怎麼辦呢?解決方法就是使用靜态變量的方式來傳遞;

        (4):通過Linux提供的剪切闆來進行傳遞,一個Activity擷取剪切闆對象之後往剪切闆裡面寫入資料,另一個Activity擷取剪切闆對象從裡面讀出資料;

        (5):通過自定義繼承自Application的MyApplication對象,并且在該對象裡面寫需要傳遞的資料,這相當于是通過全局變量的方式進行傳遞了,因為整個系統中隻有一個MyApplication對象,為了通過這種方式實作傳遞,需要在Manifest中的<application>标簽下設定我們自定義的Application是該應用的Application,具體做法是加入:android:name = ".MyApplication";

2016.7.23更新...........................................................................

(40):談談你對Context的認識

        我們知道Android程式和java程式最大的差別就在于,在java中,你隻需要一個main函數作為入口就可以跑起來了,Android程式是需要一個完整的工程環境的,并且Activity、Service、Broadcast等系統元件并不是像普通的java對象一樣随便new一下就能建立執行個體的,而是需要各自的運作環境上下文,即這裡的Context,可以這麼講,Context是維持Android程式中個元件能夠正常工作的核心功能類,包含了應用程式的環境資訊,通過他我們可以獲得應用程式的資源和類,Context的繼承結構圖是這樣的:

Android面試題集錦(二)

                                                      圖檔來自于:http://write.blog.csdn.net/postedit/51992250

        Context是抽象類,有兩個類直接繼承自他:ContextWrapper以及ContextImpl,從名字上可以看出ContextWrapper是上下文功能的封裝類,而ContextImpl是上下文功能的實作類,也就是說關于Context内方法的真正實作都是在ContextImpl裡面了,而ContextWrapper又有三個實作類,Application/Service/ContextThremeWrapper,而我們常見的Activity又是ContextThemeWrapper的子類,也就是Application/Service/Activity都是ContextWrapper的子類了;一個應用程式中Context的數量 = Application的數量+Service的數量+Activity的數量,又因為一個應用程式中Application隻有一個,是以實際上應用程式中Context的數量 = Service的數量+Activity的數量+1;

        通常我們可以在Activity或者Service中通過getApplication()獲得Application執行個體,但是在BroadcastReceiver中卻不行,原因在于getApplication方法是Activity和Service自己添加的,并不是Context中本身就有的,如果我們想在BroadcastReceiber中獲得Application執行個體的話,可以通過getApplicationContext方法,也就是說getApplication的适用範圍不如getApplicationContext;

        使用Application的時候需要注意的是,它本身已經是全局唯一的了,這點是由系統來保證的,如果我們想要自己實作一個類似于單例模式的Application類,不僅沒必要,而且經常會出錯,原因在于你寫單例的時候通常是将構造函數寫成private類型,并且對外提供一個public方法來獲得這個執行個體,而執行個體的獲得是通過new的方式實作的,前面已經說過Android中并不能随便new一個上下文對象的子類就能抛棄程式來,他是需要Android環境支撐的,你隻是new了一個執行個體出來,但這個執行個體其實是跟Android沒什麼關系的,而系統在建立Application的時候是會有目前環境資訊的,這點需要注意一下; 

(41):Android中Service和Thread的差別

        (1):Service是Android四大元件之一,建立他是依賴于Android環境的,并不是随随便便new一個就可以,預設情況下,Local Service是運作在主線程中的,而Remote Service是運作在獨立程序的主線程中的,是以不能在Service中進行一些耗時操作,要想在Service中做些耗時操作隻能在它裡面建立線程實作;而Thread并不是Android所獨有的,java中本身就有,他并不是運作在主線程中的;

        (2):Service的優先級較高,系統在資源不足的時候首先殺死的是Activity以及Thread,随後才可能殺死Service;

        兩者根本不是一個級别的東西,Service是四大元件,而Thread僅僅就是用來處理異步任務的類而已,它可以在Application/Activity/Service中建立;

        在使用Thread的時候,我們一般是在Application/Activity/Service裡面建立,在他們生命周期結束的時候如果我們不需要這個Thread的話,要記住記得将線程停止掉,因為如果不在目前環境下停止的話,這個Thread将沒法停止;

(42):Activity中onSaveInstanceState被執行的場景有哪些呢?

        (1):當使用者按下HOME鍵的時候

        (2):長按HOME鍵開啟别的應用程式時

        (3):鎖屏時

        (4):從一個Activity切換到新的Activity時

        (5):螢幕方向發生切換時

(43):Service和Activity之間怎麼通信?

        (1):通過Binder來實作,雖然Binder在絕大部分情況下是用于程序間通信的,但是他也同樣可以用于程序内部的通信,為了通過Binder實作Service與Activity之間的通信,我們需要做一下幾件事:

        首先,在Service内部建立一個繼承自Binder的内部類,并且通過onBind方法傳回這個Binder類的執行個體;接着在Activity中建立一個ServiceConnection的匿名内部類,并且重寫裡面的onServiceConnected以及onServiceDisconnected方法,在Activity中調用了bindService方法之後,我們便可以在onServiceConnected方法中擷取到Service端onBind方法傳回的Binder對象了,接着我們對這個Binder對象進行向下轉型,得到我們自定義類型的那個Binder執行個體,有了這個執行個體便可以調用執行個體裡面的方法進行适當的操作了;

        (2):通過Broadcast來實作,在當Service端需要和Activity進行通信的時候,發出一條廣播,我們在Activity端注冊該廣播便可以接收到發出的廣播内容,進而進行界面視圖方面的變化了;

2016.7.24更新...........................................................................

(44):IntentService和Service的差別

        (1):Service是運作在主線程中的,是以不能在他裡面做耗時操作,否則可能出現ANR異常;而IntentService預設裡面會建立一個HnadlerThread類型的子線程來處理Intent請求,該子線程内部存在MessageQueue消息隊列以及Looper對象;

        (2):IntentService在我們的任務執行結束之後會自動調用stopSelf結束掉,但是Service需要我們手動結束;

        (3):IntentService對Intent請求的處理是串行執行的,因為每來一個Intent請求隻是放到IntentService内部的MessageQueue隊列裡面的,每次Looper從裡面取出來一個執行,直到MessageQueue中不存在消息為止,便結束IntentService;

(45):造成記憶體洩漏的原因有哪些?

        (1):類的靜态變量持有大資料對象

        靜态變量長期持有大資料對象的應用,阻止垃圾回收,因為靜态變量的生命周期是整個應用程式;

        (2):非靜态内部類或者匿名内部類中存在引用外部類的靜态執行個體

        非靜态内部類或者匿名内部類中的靜态變量持有一個外部類執行個體的引用,會長期維持着外部類的引用,阻止被回收掉,原因在于非靜态内部類或者匿名内部類預設情況下是會隐式的持有一個他們外部類的引用的,又由于如果在内部類中存在引用外部類的靜态執行個體的話,這個執行個體的生命周期是整個應用程式的,也就導緻了整個外部類的生命周期是整個應用程式,如果外部類是Activity的話,假如我們已經确定該Activity不再需要了,但是因為非靜态内部類或者匿名内部類的引用導緻不會被GC回收,解決措施是将非靜态内部類或者匿名内部類設定成static類型,這樣他們内部就不會預設持有外部類的引用了,同時可以将其用到的關于外部類的對象設定成弱引用方式;

        (3):資源對象未關閉

        資源對象比如Cursor遊标,File對象,Bitmap資源,往往用到了緩沖,我們在不需要的時候一定要記得及時關閉他們,以便他們的緩沖及時被回收,如果我們僅僅隻是将他們的引用設定成null而不關閉他們,往往會造成記憶體洩漏,是以在使用這些對象的時候一定注意最後手動調用他們的close方法關閉掉,一般這個關閉操作是出現在try catch finally語句的finally部分的,在關閉之後将這些對象指派為null即可;

        (4):單例模式造成的記憶體洩漏

        在Android中使用單例模式的時候,由于單例模式的靜态特性往往會導緻該單例的生命周期和應用的生命周期一樣長,而我們在建立單例對象的時候經常要傳入Context上下文,我們都知道Android中的Context有三種,如果傳入的是Application的Context,自然沒什麼問題,因為這個Context本身的作用域就是整個應用;但是如果傳入的是Activity的Context就有可能造成記憶體洩漏了,因為在Activity退出的時候,由于他的Context被單例對象所持有着,那麼造成了他不會被回收掉,出現記憶體洩漏;

        解決方法是:在Android中建立單例對象的時候,如果要涉及到傳入Context上下文,最好傳入Application的Context,因為Application的Context并不是萬能的,對于Dialog而言,他是隻能出現在Activity中的,Application可以通過getApplicationContext獲得,最好不要通過getApplication,因為該方法僅适合于Activity和Service不适合于BroadcastReceiver等其他元件中;

        (5):盡量避免使用static成員變量

        因為将一個變量聲明為static的話,意味着其生命周期将和應用程式是一樣的,這會帶來一系列問題,比如你的App程序加入設計成是常駐記憶體的,即使你切換到背景,由static所持有的對象資源将不會釋放,而系統的管理機制會将占用記憶體較大的背景程序優先回收掉,這回導緻你的應用程式經常性的被回收;

        (6):Handler造成的記憶體洩漏

        因為Handler的生命周期和Activity是不一緻的,是以經常會帶來記憶體洩漏的問題,比如有這樣一種情形,我們在Activity中定義了一個繼承自Handler的非靜态内部類,并且通過他發送了一條消息,該消息會在10分鐘之後傳回目前時間,接着我們将Activity退出了,但是此時Activity并不會被GC回收掉的,原因在于我們的消息任務還在MessageQueue中排隊,那麼Handler是無法釋放的,而Handler本身又持有外部類Activity的引用,那麼也就導緻了Activity沒有釋放了,造成記憶體洩漏,解決措施和第(2)點一樣,也是将非靜态内部類設定成static類型的,同時為了高效率的回收,我們可以将所引用的外部類的執行個體在内部類中設定成WeakReference類型,也就是弱引用類型,此外為了防止Looper對象的記憶體洩漏,我們可以在Activity銷毀的時候調用removeCallbackAndMessages方法,移出MessageQueue裡面的所有消息;

        (7):注冊監聽器之後沒有解注冊

        Android中存在許多需要register與unregister的監聽器,我們需要在适當的時候及時unregister那些監聽器,自己手動add的listener一定要記得及時remove這個listener;

        (8):一些不良的程式設計習慣

        比如在使用Bitmap的時候沒有調用recycle方法,對于Bitmap對象在不使用的時候,我們應該首先使用recycle方法釋放掉記憶體然後再設定他為null,如果直接設定null的話隻是将java層面的對象吧釋放掉了,但是Bitmap的操作是要用到C層面的東西的,recycle的作用就是釋放掉C層面的;在比如構造ListView的Adapter時候,沒有使用緩存的convertView,每次都是建立新的convertView;

(46):避免OOM異常的措施有哪些呢?

        (1):使用更加輕量級的資料結構

        可以考慮使用ArrayMap/SparseArray來代替HashMap,原因在于通常的HashMap更加占用記憶體,因為他需要一段空間來存儲我們的Mapping操作,而很多情況下這些空間是不會得到充分利用的,ArrayMap采用兩個數組來實作,一個數組用于存儲key值hash之後的順序清單,另一個數組存儲按key的順序記錄的key-value值,當你想要獲得某個value值的時候,ArrayMap會計算輸入key轉換之後的hash值,然後對通過二分查找的方式尋找到這個hash值在hash值數組中的位置index,有了這個index我們便可以在另外一個數組中直接通路到需要的鍵值對了,如果第二個數組鍵值對中的key和目前輸入的key不一緻,則發生了碰撞沖突,為了解決這個問題,我們會以該key為中心,上下去查找比對,直到找到比對的值為止;而對于SparseArray來說,他的高效性展現在他避免了對key和value的自動裝箱操作,并且避免了裝箱之後的解箱操作;

        (2):盡量在Android中使用Enum枚舉類型,原因在于枚舉類型在運作的時候會帶來額外的記憶體占用;

        (3):減少Bitmap對象的記憶體占用

        Bitmap是極容易消耗記憶體的家夥,通常我們可以這麼做:使用它的inSampleSize縮放比例屬性,在圖檔加載到記憶體之前,我們首先擷取到圖檔大小,計算出适當的縮放比例出來,避免不必要的大圖加載占用記憶體操作;其次我們可以使用适當的解碼格式,ARGB_8888/ARGB_4444/RGB_565解碼方式不同,每個像素點占用的位元組數就不同;

        (4):使用更小的圖檔

        盡量使用更小的圖檔不僅僅可以減少記憶體的使用,還可以避免出現大量的InflationException,假設有一張很大的圖檔被XML檔案直接引用,很有可能在初始化的時候就會導緻記憶體不足而引發InflationException

        (5):盡量對記憶體中的對象進行複用,具體實作措施有以下幾種:

        重用系統自帶的資源,比如字元串/顔色/圖檔/動畫等;

        注意在ListView/GridView等出現大量重複子元件的視圖中對ConvertView的複用;

        使用LRU對Bitmap圖檔進行緩存;

        使用inBitmap的進階特性提高Android系統在Bitmap配置設定與釋放執行效率上的提升,使用inBitmap可以告訴Bitmap解碼器嘗試使用已經存在的記憶體區域,新解碼的Bitmap會嘗試使用之前那張Bitmap在heap中所使用的記憶體區域,而不是取記憶體重新申請一塊區域來存放Bitmap,這樣的話,即使有上千張圖檔,也隻會占用螢幕所能顯示圖檔數量的記憶體大小,但是使用inBitmap有兩個限制條件,一是新申請的Bitmap大小必須小于或者等于已經指派的Bitmap大小;二是新申請的Bitmap解碼方式必須與舊Bitmap的解碼方式相同,大家都是ARGB_8888就都是,我們可以建立一個包含多種典型可重用的Bitmap對象池,這樣的話,後續的Bitmap就都能找到合适的"模闆"去複用了;

        (6):避免在onDraw方法中執行對象的建立

        因為onDraw方法的調用頻率比較高,一定要注意避免在這個方法裡面進行建立對象的操作,因為他會迅速增加記憶體的使用,容易引起頻繁的GC操作,甚至可能出現記憶體的抖動現象;

        (7):StringBuilder的使用

        在大量使用到字元串拼接操作的地方可以用StringBuilder來代替“+”,但是有一點需要注意的就是,StringBuilder是非線程安全的;

        (8):注意單例對象中不合理的持有其他對象這種情況,因為單例的生命周期是和應用保持一緻的,使用不合理很容易會導緻持有對象的洩漏;

(47):在Activity中使用AsyncTask造成的記憶體洩漏問題怎麼解決?

        我們一般使用AsyncTask都是在Activity中通過建立一個非靜态内部類實作的,但這種方式在上面的記憶體洩漏部分已經提到過可能會造成記憶體洩漏,原因就在于非靜态内部類預設是會持有外部類的引用的,如果我們的AsyncTask任務内部存在靜态變量引用了外部Activity的一些對象的話,會導緻Activity即使已經銷毀了,但是他所占用的記憶體空間并沒有釋放,為此我們可以采用以下方式來解決:

        (1):如果在AsyncTask内部要引用Activity對象執行個體的話,我們可以使用弱引用的方式持有Activity對象;

        (2):重新AsyncTask的onCancel方法,在它裡面實作一些關閉資源的操作;

        (3):在Activity退出的時候,調用AsyncTask的onCancel方法,結束掉其占有的資源,這樣的話便保證了Activity和AsyncTask生命周期的同步;

(48):顯式Intent與隐式Intent的差別

        顯式Intent指的是明确指定目标元件名稱的Intent,隐式Intent指的是沒有明确指定目标元件名稱的Intent,對于隐式Intent當在建立一個Activity的時候,我們通過設定的intent-filter标簽來比對目前Activity是不是Intent想要的Activity,在intent-filter裡面會有三方面的内容:action/category/data;

(49):Android中常見的動畫有哪些呢?

        Android中常見的動畫有三類:View動畫、幀動畫、屬性動畫;

        View動畫:包括四大類型,平移、縮放、旋轉、透明度,但該動畫并不會改變View屬性的值,隻會改變View的位置,也就是說一個按鈕在動畫之後雖然不在原來的位置了,但是原來的位置還是可以出發點選事件的;

        幀動畫:類似于放電影,一幀一幀的切換圖檔以達到動畫的效果,這種動畫一定要注意OOM異常的發生;

        屬性動畫:3.0開始出現的,他作用的對象不僅僅是View,可以是任意的Object對象,并且是通過反射不斷的改變Object的屬性,動畫結束之後View的屬性是實實在在發生變化的;

(50):不使用動畫如何實作一個動态的View?

        開啟一定Timer定時器,每隔一段時間通過反射修改View對象的某個屬性,而後調用invalidate方法進行重繪即可,其實就是屬性動畫的實作原理。

(51):invalidate與postInvalidate的差別?

        invalidate和postInvalidate方法都是用于視圖重繪的,後者最終也會調用前者,兩者最大的差別在于一個可以用于子線程,一個隻能用于主線程;

        Android的UI操作并不是線程安全的,invalidate隻能用在主線程中,如果現在子線程中使用invalidate的話,我們需要在主線程中建立一個Handler對象,并且利用該Handler将繪制過程切換到主線程上面;

        而postInvalidate是可以用在子線程中的,其實他也是通過Handler來實作切換到主線程的操作的,隻是系統給你做了封裝而已,最後調用的還是invalidate方法;

(52):Fragment裡面可以嵌套Fragment嗎?會帶來什麼問題呢?

        Fragment裡面是可以嵌套Fragment的,但是可能在使用的使用你會發現有如下問題:

        (1):假如Activity中嵌套有Fragment,而Fragment中嵌套有子Fragment,Fragment可以通過ChildFragmentManager來管理這些子Fragment,子Fragment可以通過getParentFragment獲得目前Fragment的宿主Fragment,但是當你第一次從Activity啟動Fragment,再去啟動Fragment的子Fragment的時候,是存在指向Activity的變量的,當你退出Fragment之後回到Activity,然後再次進入Fragment,你會發現抛出類似于Fragment的ID或者Tag重複的異常,原因在于Fragment被detached之後都會被reset掉,但是他并不會對ChildFragmentManager中的Fragment做reset,這樣導緻你下次開啟之前開啟過的Fragment的時候會發現ChildFragment池中其實已經存在該Fragment的異常,解決措施就是在你調用detached方法reset Fragment的時候同時将ChildFragmentManager中的Fragment也reset掉;

        (2):當我們從一個Activity啟動一個Fragment,接着在Fragment中執行個體化一些子Fragment,在子Fragment中有傳回的啟動另外一個Activity,即通過startActivityForResult的方式啟動,可以發現在子Fragment中收不到onActivityResult的值,如果在子Fragment中通過getActivity.startActivityForResult的方式啟動Activity,則隻有Activity可以收到onActivityResult,如果是以getParentFragment.startActivityForResult的方式啟動的話,那麼隻有Activity以及父Fragment可以收到,子Fragment還是收不到;解決措施是通過getParentFragment.startActivityForResult先在父Fragment中獲得傳回值,然後通過父Fragemnt傳遞給他的子Fragment;

(53):如何在Activity中動态的添加Fragment

        首先應該知道的就是Activity是通過FragmentManager來管理添加或者移出目前Activity的Fragment對象的,是以首先我們應該獲得FragmentManager對象,有了這個對象之後我們就可以利用該對象的replace方法來講布局中某一塊地方替換成我們想要填充的Fragment了,但是有個注意點就是,為了防止在修改Activity界面Fragment的時候出現錯誤,Android對整個添加或者移出操作進行了事務處理,是以在使用replace或者remove方法之前,你應該調用FragmentManager的beginTransaction方法開啟事務,在replace或者remove方法之後調用FragmentManager的commit方法送出事務;還有一點就是動态添加Fragment的時候,Activity布局檔案中的标簽是<FrameLayout>而靜态添加的話,布局檔案中的标簽是<fragment>;

(54):Android中的MVC模式與MVP模式

        MVC:Model(模型層)、View(視圖層)、Control(控制層)

        View層一般通過XML進行界面的描述

        Control層主要是由Activity實作的,是以我們應該盡量少的在Activity中進行業務代碼的編寫,而應該通過Activity交割給Model業務邏輯層來進行處理,這也就是為什麼Activity中要設定5s來判斷目前Activity是不是沒響應的原因了,因為系統根本不建議你在Activity做過多的事;

        Model層就是我們進行業務邏輯代碼編寫的地方,這一層你可以進行操作資料庫,通路網絡等操作,将結果傳回給Activity,通過Activity調用顯示界面的方法顯示在View上面;

        通過在Activity層調用模型層的方法,具體的資料庫操作,網絡通路等操作是由模型層實作的,接着模型層以回調的方式傳回資料給Activity,Activity将這些資料顯示到View上面,這樣避免了Activity部分代碼國語複雜,大部分操作都由Model層來處理了;

        MVP:Model(模型層)、View(視圖層)、Presenter(主導器)

        MVP模式是對MVC模式的進一步解耦,在這種模式中首先會對View層以及Model層各自抽象出來一個接口層,我們稱之為IView與IModel,而Presenter層可以認為是用來分擔Activity任務的,也即Activity中再抽出一個Presenter層來作為紐帶,在這個紐帶中通過IView接口把資料送給View,通過IModel接口通知Model來進行資料庫操作等等,而Activity層隻需要定義一個Presenter對象,利用該對象進行業務處理過程即可;

(55):dp,dip,dpi,px,sp各指的是什麼呢?

        dp與dip(device independent pixels)是一個意思,指的是裝置獨立像素,與裝置螢幕有關系;

        dpi(dot per inch)螢幕像素密度,指的是每英寸多少像素

        px(pixels)像素

        sp(scaled pixels)主要用于字型大小的

繼續閱讀