android開發校招

J2EE 部分:

1.Switch能否用string做參數?

在 Java 7 之前, switch 只能支持 byte 、 short 、 char 、 int 或者其對應的封裝類以及 Enum 類型。在 Java 7 中, String 支持被加上了。

2. equals與==的區別:

==是判斷兩個變量或實例是不是指向同一個內存空間 equals是判斷兩個變量或實例所指向的內存空間的值是不是相同

3. Object有哪些公用方法?

  • 方法equals測試的是兩個對象是否相等
  • 方法clone進行對象拷貝
  • 方法getClass返回和當前對象相關的Class對象
  • 方法notify,notifyall,wait都是用來對給定對象進行線程同步的

5. 實際開發中軟引用或者弱引用的使用場景:

利用軟引用和弱引用解決OOM問題:用一個HashMap來保存圖片的路徑和相應圖片對象關聯的軟引用之間的映射關係,在內存不足時,JVM會自動回收這些緩存圖片對象所佔用的空間,從而有效地避免了OOM的問題 通過軟可及對象重獲方法實現Java對象的高速緩存:比如我們創建了一Employee的類,如果每次需要查詢一個僱員的信息。哪怕是幾秒中之前剛剛查詢過的,都要重新構建一個實例,這是需要消耗很多時間的。我們可以通過軟引用和 HashMap 的結合,先是保存引用方面:以軟引用的方式對一個Employee對象的實例進行引用並保存該引用到HashMap 上,key 爲此僱員的 id,value爲這個對象的軟引用,另一方面是取出引用,緩存中是否有該Employee實例的軟引用,如果有,從軟引用中取得。如果沒有軟引用,或者從軟引用中得到的實例是null,重新構建一個實例,並保存對這個新建實例的軟引用

6. Hashcode的作用,與 equal 有什麼區別

同樣用於鑑定2個對象是否相等的,java集合中有 list 和 set 兩類,其中 set不允許元素重複實現,那個這個不允許重複實現的方法,如果用 equal 去比較的話,如果存在1000個元素,你 new 一個新的元素出來,需要去調用1000次 equal 去逐個和他們比較是否是同一個對象,這樣會大大降低效率。hashcode實際上是返回對象的存儲地址,如果這個位置上沒有元素,就把元素直接存儲在上面,如果這個位置上已經存在元素,這個時候纔去調用equal方法與新元素進行比較,相同的話就不存了,散列到其他地址上

7. String、StringBuffer與StringBuilder的區別

String 類型和 StringBuffer 類型的主要性能區別其實在於 String 是不可變的對象 StringBuffer和StringBuilder底層是 char[]數組實現的 StringBuffer是線程安全的,而StringBuilder是線程不安全的

8. Override和Overload的含義去區別

Overload顧名思義是重新加載,它可以表現類的多態性,可以是函數裏面可以有相同的函數名但是參數名、返回值、類型不能相同;或者說可以改變參數、類型、返回值但是函數名字依然不變。 Override顧名思義就是ride(重寫)的意思,在子類繼承父類的時候子類中可以定義某方法與其父類有相同的名稱和參數,當子類在調用這一函數時自動調用子類的方法,而父類相當於被覆蓋(重寫)了。

9. 抽象類和接口的區別

一個類只能繼承單個類,但是可以實現多個接口 接口強調特定功能的實現,而抽象類強調所屬關係 抽象類中的所有方法並不一定要是抽象的,你可以選擇在抽象類中實現一些基本的方法。而接口要求所有的方法都必須是抽象的

10.解析XML的幾種方式的原理與特點:DOM、SAX、PULL

  • DOM:消耗內存:先把xml文檔都讀到內存中,然後再用DOM API來訪問樹形結構,並獲取數據。這個寫起來很簡單,但是很消耗內存。要是數據過大,手機不夠牛逼,可能手機直接死機
  • SAX:解析效率高,佔用內存少,基於事件驅動的:更加簡單地說就是對文檔進行順序掃描,當掃描到文檔(document)開始與結束、元素(element)開始與結束、文檔(document)結束等地方時通知事件處理函數,由事件處理函數做相應動作,然後繼續同樣的掃描,直至文檔結束。
  • SAX:與 SAX 類似,也是基於事件驅動,我們可以調用它的next()方法,來獲取下一個解析事件(就是開始文檔,結束文檔,開始標籤,結束標籤),當處於某個元素時可以調用XmlPullParser的getAttributte()方法來獲取屬性的值,也可調用它的nextText()獲取本節點的值。

    11.wait()和sleep()的區別

  • sleep來自Thread類,和wait來自Object類
  • 調用sleep()方法的過程中,線程不會釋放對象鎖。而 調用 wait 方法線程會釋放對象鎖
  • sleep睡眠後不出讓系統資源,wait讓出系統資源其他線程可以佔用CPU
  • sleep(milliseconds)需要指定一個睡眠時間,時間一到會自動喚醒

13.JAVA多態的實現原理

抽象的來講,多態的意思就是同一消息可以根據發送對象的不同而採用多種不同的行爲方式。(發送消息就是函數調用) 實現的原理是動態綁定,程序調用的方法在運行期才動態綁定,追溯源碼可以發現,JVM 通過參數的自動轉型來找到合適的辦法。

14.JAVA 垃圾回收與內存分配策略

14.1 垃圾回收是什麼?

就是釋放那些不再持有引用的對象的內存

14.2怎麼判斷一個對象是否需要收集?

  • 引用計數(最簡單古老的方法):指將資源(可以是對象、內存或磁盤空間等等)的被引用次數保存起來,當被引用次數變爲零時就將其釋放的過程
    • 對象引用遍歷(現在大多數 jvm 使用的方法):對象引用遍歷從一組對象開始,沿着整個對象圖上的每條鏈接,遞歸確定可到達(reachable)的對象。如果某對象不能從這些根對象的一個(至少一個)到達,則將它作爲垃圾收集
    • 引用計數缺陷:引用計數無法解決循環引用問題:假設對象A,B都已經被實例化,讓A=B,B=A,除此之外這兩個對象再無任何引用,此時計數器的值就永遠不可能爲0,但是引用計數器無法通知gc回收他們

14.3 Java的四種引用的區別

  • 強引用:如果一個對象具有強引用,它就不會被垃圾回收器回收。即使當前內存空間不足,JVM 也不會回收它,而是拋出 OutOfMemoryError 錯誤,使程序異常終止。如果想中斷強引用和某個對象之間的關聯,可以顯式地將引用賦值爲null,這樣一來的話,JVM在合適的時間就會回收該對象
  • 軟引用:在使用軟引用時,如果內存的空間足夠,軟引用就能繼續被使用,而不會被垃圾回收器回收,只有在內存不足時,軟引用纔會被垃圾回收器回收。
  • 弱引用:具有弱引用的對象擁有的生命週期更短暫。因爲當 JVM 進行垃圾回收,一旦發現弱引用對象,無論當前內存空間是否充足,都會將弱引用回收。不過由於垃圾回收器是一個優先級較低的線程,所以並不一定能迅速發現弱引用對象
  • 虛引用:顧名思義,就是形同虛設,如果一個對象僅持有虛引用,那麼它相當於沒有引用,在任何時候都可能被垃圾回收器回收。

14.4 介紹垃圾回收機制

  • 標記回收法:遍歷對象圖並且記錄可到達的對象,以便刪除不可到達的對象,一般使用單線程工作並且可能產生內存碎片
  • 標記-壓縮回收法:前期與第一種方法相同,只是多了一步,將所有的存活對象壓縮到內存的一端,這樣內存碎片就可以合成一大塊可再利用的內存區域,提高了內存利用率
  • 複製回收法:把現有內存空間分成兩部分,gc運行時,它把可到達對象複製到另一半空間,再清空正在使用的空間的全部對象。這種方法適用於短生存期的對象,持續複製長生存期的對象則導致效率降低。
  • 分代回收發:把內存空間分爲兩個或者多個域,如年輕代和老年代,年輕代的特點是對象會很快被回收,因此在年輕代使用效率比較高的算法。當一個對象經過幾次回收後依然存活,對象就會被放入稱爲老年的內存空間,老年代則採取標記-壓縮算法

14.5 JAVA 中堆和棧的區別

  • 基本數據類型比變量和對象的引用都是在棧分配的
  • 堆內存用來存放由new創建的對象和數組
  • 類變量(static修飾的變量),程序在一加載的時候就在堆中爲類變量分配內存,堆中的內存地址存放在棧中
  • 實例變量:當你使用java關鍵字new的時候,系統在堆中開闢並不一定是連續的空間分配給變量,是根據零散的堆內存地址,通過哈希算法換算爲一長串數字以表徵這個變量在堆中的"物理位置”,實例變量的生命週期--當實例變量的引用丟失後,將被GC(垃圾回收器)列入可回收“名單”中,但並不是馬上就釋放堆中內存
  • 局部變量: 由聲明在某方法,或某代碼段裏(比如for循環),執行到它的時候在棧中開闢內存,當局部變量一但脫離作用域,內存立即釋放

15. Java 集合系列問題

15.1 ArrayList、LinkedList、Vector的區別

  • ArrayList 和Vector底層是採用數組方式存儲數據,Vector由於使用了synchronized方法(線程安全)所以性能上比ArrayList要差
  • LinkedList使用雙向鏈表實現存儲,隨機存取比較慢
  • HashMap的底層源碼實現:當我們往HashMap中put元素的時候,先根據key的hashCode重新計算hash值,根據hash值得到這個元素在數組中的位置(即下標),如果數組該位置上已經存放有其他元素了,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放在鏈尾。如果數組該位置上沒有元素,就直接將該元素放到此數組中的該位置上。
  • Fail-Fast機制:在使用迭代器的過程中有其他線程修改了map,那麼將拋出ConcurrentModificationException,這就是所謂fail-fast機制。這一機制在源碼中的實現是通過modCount域,modCount顧名思義就是修改次數,對HashMap內容的修改都將增加這個值,那麼在迭代器初始化過程中會將這個值賦給迭代器的expectedModCount。在迭代過程中,判斷modCount跟expectedModCount是否相等,如果不相等就表示已經有其他線程修改了Map

    15.2 HashMap和 HashTable 的區別

    HashTable比較老,是基於Dictionary 類實現的,HashTable 則是基於 Map接口實現的 HashTable 是線程安全的, HashMap 則是線程不安全的 HashMap可以讓你將空值作爲一個表的條目的key或value

17.什麼事反射,在哪裏需要用到?

18. 什麼是線程池,線程池的作用是什麼

答:線程池的基本思想還是一種對象池的思想,開闢一塊內存空間,裏面存放了衆多(未死亡)的線程,池中線程執行調度由池管理器來處理。當有線程任務時,從池中取一個,執行完成後線程對象歸池,這樣可以避免反覆創建線程對象所帶來的性能開銷,節省了系統的資源。就好比原來去食堂打飯是每個人看誰搶的贏,誰先搶到誰先吃,有了線程吃之後,就是排好隊形,今天我跟你關係好,你先來吃飯。比如:一個應用要和網絡打交道,有很多步驟需要訪問網絡,爲了不阻塞主線程,每個步驟都創建個線程,在線程中和網絡交互,用線程池就變的簡單,線程池是對線程的一種封裝,讓線程用起來更加簡便,只需要創一個線程池,把這些步驟像任務一樣放進線程池,在程序銷燬時只要調用線程池的銷燬函數即可。

單個線程的弊端:a. 每次new Thread新建對象性能差b. 線程缺乏統一管理,可能無限制新建線程,相互之間競爭,及可能佔用過多系統資源導致死機或者OOM,c. 缺乏更多功能,如定時執行、定期執行、線程中斷。

java提供的四種線程池的好處在於:a. 重用存在的線程,減少對象創建、消亡的開銷,性能佳。b. 可有效控制最大併發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。c. 提供定時執行、定期執行、單線程、併發數控制等功能。

2、Java 線程池

Java通過Executors提供四種線程池,分別爲:

newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。

newFixedThreadPool 創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。

newScheduledThreadPool 創建一個定長線程池,支持定時及週期性任務執行。

newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。

(1). newCachedThreadPool

創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。線程池爲無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的線程,而不用每次新建線程。

(2). newFixedThreadPool

創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。

(3) newScheduledThreadPool

創建一個定長線程池,支持定時及週期性任務執行。ScheduledExecutorService比Timer更安全,功能更強大

(4)、newSingleThreadExecutor

創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行

Android部分(給出重點部分):

1. Activity 系列問題

1.1 繪製Activity生命週期流程圖

enter image description here

1.2 介紹下不同場景下Activity生命週期的變化過程

  • 啓動Activity: onCreate()--->onStart()--->onResume(),Activity進入運行狀態。
  • Activity退居後臺: 當前Activity轉到新的Activity界面或按Home鍵回到主屏: onPause()--->onStop(),進入停滯狀態。
  • Activity返回前臺: onRestart()--->onStart()--->onResume(),再次回到運行狀態。
  • Activity退居後臺,且系統內存不足, 系統會殺死這個後臺狀態的Activity,若再次回到這個Activity,則會走onCreate()-->onStart()--->onResume()
  • 鎖定屏與解鎖屏幕 只會調用onPause(),而不會調用onStop方法,開屏後則調用onResume()

1.3 內存不足時系統會殺掉後臺的Activity,若需要進行一些臨時狀態的保存,在哪個方法進行?

Activity的 onSaveInstanceState() 和 onRestoreInstanceState()並不是生命週期方法,它們不同於 onCreate()、onPause()等生命週期方法,它們並不一定會被觸發。當應用遇到意外情況(如:內存不足、用戶直接按Home鍵)由系統銷燬一個Activity,onSaveInstanceState() 會被調用。但是當用戶主動去銷燬一個Activity時,例如在應用中按返回鍵,onSaveInstanceState()就不會被調用。除非該activity是被用戶主動銷燬的,通常onSaveInstanceState()只適合用於保存一些臨時性的狀態,而onPause()適合用於數據的持久化保存。

1.4 onSaveInstanceState()被執行的場景有哪些:

系統不知道你按下HOME後要運行多少其他的程序,自然也不知道activity A是否會被銷燬,因此係統都會調用onSaveInstanceState(),讓用戶有機會保存某些非永久性的數據。以下幾種情況的分析都遵循該原則

  1. 當用戶按下HOME鍵時
  2. 長按HOME鍵,選擇運行其他的程序時
  3. 鎖屏時
  4. 從activity A中啓動一個新的activity時
  5. 屏幕方向切換時

1.5 介紹Activity的幾中啓動模式,並簡單說說自己的理解或者使用場景

2. Service系列問題

2.1 註冊Service需要注意什麼

Service還是運行在主線程當中的,所以如果需要執行一些複雜的邏輯操作,最好在服務的內部手動創建子線程進行處理,否則會出現UI線程被阻塞的問題

2.2 Service與Activity怎麼實現通信

方法一:

  1. 添加一個繼承Binder的內部類,並添加相應的邏輯方法
  2. 重寫Service的onBind方法,返回我們剛剛定義的那個內部類實例
  3. Activity中創建一個ServiceConnection的匿名內部類,並且重寫裏面的onServiceConnected方法和onServiceDisconnected方法,這兩個方法分別會在活動與服務成功綁定以及解除綁定的時候調用,在onServiceConnected方法中,我們可以得到一個剛纔那個service的binder對象,通過對這個binder對象進行向下轉型,得到我們那個自定義的Binder實例,有了這個實例,做可以調用這個實例裏面的具體方法進行需要的操作了

方法二 通過BroadCast(廣播)的形式 當我們的進度發生變化的時候我們發送一條廣播,然後在Activity的註冊廣播接收器,接收到廣播之後更新視圖

2.3 介紹源碼中binder機制

2.4 IntentService與Service的區別

IntentService是Service的子類,是一個異步的,會自動停止的服務,很好解決了傳統的Service中處理完耗時操作忘記停止並銷燬Service的問題

  • 會創建獨立的worker線程來處理所有的Intent請求;
  • 會創建獨立的worker線程來處理onHandleIntent()方法實現的代碼,無需處理多線程問題;
  • 所有請求處理完成後,IntentService會自動停止,無需調用stopSelf()方法停止Service;
  • 爲Service的onBind()提供默認實現,返回null;
  • 爲Service的onStartCommand提供默認實現,將請求Intent添加到隊列中;
  • IntentService不會阻塞UI線程,而普通Serveice會導致ANR異常
  • Intentservice若未執行完成上一次的任務,將不會新開一個線程,是等待之前的任務完成後,再執行新的任務,等任務完成後再次調用stopSelf()

3. Handle系列問題

3.1 介紹Handle的機制

  • Handler通過調用sendmessage方法把消息放在消息隊列MessageQueue中,Looper負責把消息從消息隊列中取出來,重新再交給Handler進行處理,三者形成一個循環
  • 通過構建一個消息隊列,把所有的Message進行統一的管理,當Message不用了,並不作爲垃圾回收,而是放入消息隊列中,供下次handler創建消息時候使用,提高了消息對象的複用,減少系統垃圾回收的次數
  • 每一個線程,都會單獨對應的一個looper,這個looper通過ThreadLocal來創建,保證每個線程只創建一個looper,looper初始化後就會調用looper.loop創建一個MessageQueue,這個方法在UI線程初始化的時候就會完成,我們不需要手動創建

3.2 談談對HandlerThread的理解

4. ListView系列問題

4.1 ListView卡頓的原因與性能優化,越多越好

  1. 重用converView: 通過複用converview來減少不必要的view的創建,另外Infalte操作會把xml文件實例化成相應的View實例,屬於IO操作,是耗時操作。

  2. 減少findViewById()操作: 將xml文件中的元素封裝成viewholder靜態類,通過converview的setTag和getTag方法將view與相應的holder對象綁定在一起,避免不必要的findviewbyid操作

  3. 避免在 getView 方法中做耗時的操作: 例如加載本地 Image 需要載入內存以及解析 Bitmap ,都是比較耗時的操作,如果用戶快速滑動listview,會因爲getview邏輯過於複雜耗時而造成滑動卡頓現象。用戶滑動時候不要加載圖片,待滑動完成再加載,可以使用這個第三方庫glide

  4. Item的佈局層次結構儘量簡單,避免佈局太深或者不必要的重繪

  5. 儘量能保證 Adapter 的 hasStableIds() 返回 true 這樣在 notifyDataSetChanged() 的時候,如果item內容並沒有變化,ListView 將不會重新繪製這個 View,達到優化的目的

  6. 在一些場景中,ScollView內會包含多個ListView,可以把listview的高度寫死固定下來。 由於ScollView在快速滑動過程中需要大量計算每一個listview的高度,阻塞了UI線程導致卡頓現象出現,如果我們每一個item的高度都是均勻的,可以通過計算把listview的高度確定下來,避免卡頓現象出現

  7. 使用 RecycleView 代替listview: 每個item內容的變動,listview都需要去調用notifyDataSetChanged來更新全部的item,太浪費性能了。RecycleView可以實現當個item的局部刷新,並且引入了增加和刪除的動態效果,在性能上和定製上都有很大的改善

  8. ListView 中元素避免半透明: 半透明繪製需要大量乘法計算,在滑動時不停重繪會造成大量的計算,在比較差的機子上會比較卡。 在設計上能不半透明就不不半透明。實在要弄就把在滑動的時候把半透明設置成不透明,滑動完再重新設置成半透明。

  9. 儘量開啓硬件加速: 硬件加速提升巨大,避免使用一些不支持的函數導致含淚關閉某個地方的硬件加速。當然這一條不只是對 ListView。

4.2 怎麼實現一個部分更新的 ListView?

4.3 怎麼實現ListView多種佈局?

4.4 ListView與數據庫綁定的實現

5. JNI系列問題

5.1 如何使用JNI

  1. JAVA中聲明native 方法如private native String printJNI(String inputStr);

  2. 使用javah工具生成.h頭文件這時候頭文件中就會自動生成對應的函數JNIEXPORT jstring JNICALL Java_com_wenming_HelloWorld_printJNI

  3. 實現JNI原生函數源文件,新建HelloWorld.c文件,對剛纔自動生成的函數進行具體的邏輯書寫,例如返回一個java叫做HelloWorld的字符串等

  4. 編譯生成動態鏈接so文件**

  5. Java中調用Sysytem.load方法把剛纔的so庫加載進來,就可以調用native方法了

5.2 如何通過JNI傳遞String對象

Java的String和C++的string是不能對等起來的,所以當我們拿到.h文件下面的jstring對象,會做一次轉換我們把jstring轉換爲C下面的char*類型, 獲取值

constchar* str;
str = env->GetStringUTFChars(prompt,false);

賦予值

char* tmpstr ="return string succeeded";
jstring rtstr = env->NewStringUTF(tmpstr);

6. OOM系列問題

6.1 什麼OOM?

OOM全稱是Out Of Merrory,Android系統的每一個應用程序都設置一個硬性的Dalvik Heap Size最大限制閾值,如果申請的內存資源超過這個限制,系統就會拋出OOM錯誤

6.2 內存泄漏有哪些場景以及解決方法

  • 類的靜態變量持有大數據對象 靜態變量長期維持到大數據對象的引用,阻止垃圾回收。

  • 非靜態內部類存在靜態實例 非靜態內部類會維持一個到外部類實例的引用,如果非靜態內部類的實例是靜態的,就會間接長期維持着外部類的引用,阻止被回收掉。

  • 資源對象未關閉 資源性對象比如(Cursor,File文件等)往往都用了一些緩衝,我們在不使用的時候,應該及時關閉它們, 以便它們的緩衝及時回收內存。它們的緩衝不僅存在於java虛擬機內,還存在於java虛擬機外。 如果我們僅僅是把它的引用設置爲null,而不關閉它們,往往會造成內存泄露。 解決辦法: 比如SQLiteCursor(在析構函數finalize(),如果我們沒有關閉它,它自己會調close()關閉), 如果我們沒有關閉它,系統在回收它時也會關閉它,但是這樣的效率太低了。 因此對於資源性對象在不使用的時候,應該調用它的close()函數,將其關閉掉,然後才置爲null. 在我們的程序退出時一定要確保我們的資源性對象已經關閉。 程序中經常會進行查詢數據庫的操作,但是經常會有使用完畢Cursor後沒有關閉的情況。如果我們的查詢結果集比較小, 對內存的消耗不容易被發現,只有在常時間大量操作的情況下才會復現內存問題,這樣就會給以後的測試和問題排查帶來困難和風險,記得try catch後,在finally方法中關閉連接

  • Handler內存泄漏 Handler作爲內部類存在於Activity中,但是Handler生命週期與Activity生命週期往往並不是相同的,比如當Handler對象有Message在排隊,則無法釋放,進而導致本該釋放的Acitivity也沒有辦法進行回收。 解決辦法

  • 聲明handler爲static類,這樣內部類就不再持有外部類的引用了,就不會阻塞Activity的釋放
  • 如果內部類實在需要用到外部類的對象,可在其內部聲明一個弱引用引用外部類。

    public class MainActivity extends Activity {
     private CustomHandler mHandler;
    
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mHandler = new CustomHandler(this);
     }
    
     static class CustomHandlerextends Handler {
         // 內部聲明一個弱引用,引用外部類
         private WeakReference<MainActivity > activityWeakReference;
         public MyHandler(MyActivity activity) {
             activityWeakReference= new WeakReference<MainActivity >(activity);
         }
                 // ... ...   
     }
    }
    
  • 在Activity onStop或者onDestroy的時候,取消掉該Handler對象的Message和Runnable

    Override
    public void onDestroy() {
     //  If null, all callbacks and messages will be removed.
     mHandler.removeCallbacksAndMessages(null);
    }
    
  • 一些不良代碼習慣 有些代碼並不造成內存泄露,但是他們的資源沒有得到重用,頻繁的申請內存和銷燬內存,消耗CPU資源的同時,也引起內存抖動 解決方案 如果需要頻繁的申請內存對象和和釋放對象,可以考慮使用對象池來增加對象的複用。 例如ListView便是採用這種思想,通過複用converview來避免頻繁的GC

6.2 如何避免 OOM 問題的出現

1. 使用更加輕量的數據結構 例如,我們可以考慮使用ArrayMap/SparseArray而不是HashMap等傳統數據結構。通常的HashMap的實現方式更加消耗內存,因爲它需要一個額外的實例對象來記錄Mapping操作。另外,SparseArray更加高效,在於他們避免了對key與value的自動裝箱(autoboxing),並且避免了裝箱後的解箱。

2. 避免在Android裏面使用Enum Android官方培訓課程提到過“Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.”,具體原理請參考《Android性能優化典範(三)》,所以請避免在Android裏面使用到枚舉。

3. 減小Bitmap對象的內存佔用 Bitmap是一個極容易消耗內存的大胖子,減小創建出來的Bitmap的內存佔用可謂是重中之重,,通常來說有以下2個措施: inSampleSize:縮放比例,在把圖片載入內存之前,我們需要先計算出一個合適的縮放比例,避免不必要的大圖載入。 decode format:解碼格式,選擇ARGB_6666/RBG_545/ARGB_4444/ALPHA_6,存在很大差異

4.Bitmap對象的複用 縮小Bitmap的同時,也需要提高BitMap對象的複用率,避免頻繁創建BitMap對象,複用的方法有以下2個措施 LRUCache : “最近最少使用算法”在Android中有極其普遍的應用。ListView與GridView等顯示大量圖片的控件裏,就是使用LRU的機制來緩存處理好的Bitmap,把近期最少使用的數據從緩存中移除,保留使用最頻繁的數據, inBitMap高級特性:利用inBitmap的高級特性提高Android系統在Bitmap分配與釋放執行效率。使用inBitmap屬性可以告知Bitmap解碼器去嘗試使用已經存在的內存區域,新解碼的Bitmap會嘗試去使用之前那張Bitmap在Heap中所佔據的pixel data內存區域,而不是去問內存重新申請一塊區域來存放Bitmap。利用這種特性,即使是上千張的圖片,也只會僅僅只需要佔用屏幕所能夠顯示的圖片數量的內存大小

4. 使用更小的圖片 在涉及給到資源圖片時,我們需要特別留意這張圖片是否存在可以壓縮的空間,是否可以使用更小的圖片。儘量使用更小的圖片不僅可以減少內存的使用,還能避免出現大量的InflationException。假設有一張很大的圖片被XML文件直接引用,很有可能在初始化視圖時會因爲內存不足而發生InflationException,這個問題的根本原因其實是發生了OOM。

5.StringBuilder 在有些時候,代碼中會需要使用到大量的字符串拼接的操作,這種時候有必要考慮使用StringBuilder來替代頻繁的“+”。

4.避免在onDraw方法裏面執行對象的創建 類似onDraw等頻繁調用的方法,一定需要注意避免在這裏做創建對象的操作,因爲他會迅速增加內存的使用,而且很容易引起頻繁的gc,甚至是內存抖動。

5. 避免對象的內存泄露 android中內存泄漏的場景以及解決辦法,參考上一問

7. ANR 系列問題

7.1 什麼ANR

ANR全稱Application Not Responding,意思就是程序未響應。如果一個應用無法響應用戶的輸入,系統就會彈出一個ANR對話框,用戶可以自行選擇繼續等待亦或者是停止當前程序。一旦出現下面兩種情況,則彈出ANR對話框

  • 應用在5秒內未響應用戶的輸入事件(如按鍵或者觸摸)
  • BroadcastReceiver未在10秒內完成相關的處理

7.2 ANR是怎麼引起的?

  • 主線程中存在耗時的計算-
  • 主線程被IO操作(從4.0之後網絡IO不允許在主線程中)阻塞。-
  • 主線程中錯誤的操作,比如Thread.wait或者Thread.sleep等

7.3 如何避免ANR問題的出現

基本思路就是把一些耗時操作放到子線程中處理

  • 使用AsyncTask處理耗時IO操作。

  • 降低子線程優先級使用Thread或者HandlerThread時,調用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)設置優先級,否則仍然會降低程序響應,因爲默認Thread的優先級和主線程相同。

  • 使用Handler處理子線程結果,而不是使用Thread.wait()或者Thread.sleep()來阻塞主線程。

  • Activity的onCreate和onResume回調中儘量避免耗時的代碼

  • BroadcastReceiver中onReceive代碼也要儘量減少耗時操作建議使用IntentService處理。IntentService是一個異步的,會自動停止的服務,很好解決了傳統的Service中處理完耗時操作忘記停止並銷燬Service的問題

8. Asynctask問題

8.1 AsynTask爲什麼要設計爲只能夠一次任務?

最核心的還是線程安全問題,多個子線程同時運行,會產生狀態不一致的問題。所以要務必保證只能夠執行一次

8.2 AsynTask造成的內存泄露的問題怎麼解決,》比如非靜態內部類AsynTask會隱式地持有外部類的引用,如果其生命週期大於外部activity的生命週期,就會出現內存泄漏

  • 注意要複寫AsynTask的onCancel方法,把裏面的socket,file等,該關掉的要及時關掉
  • 在 Activity 的onDestory()方法中調用Asyntask.cancal方法
  • Asyntask內部使用弱引用的方式來持有Activity

8.3 若Activity已經銷燬,此時AsynTask執行完並且返回結果,會報異常嗎?

當一個App旋轉時,整個Activity會被銷燬和重建。當Activity重啓時,AsyncTask中對該Activity的引用是無效的,因此onPostExecute()就不會起作用,若AsynTask正在執行,折會報 view not attached to window manager 異常

同樣也是生命週期的問題,在 Activity 的onDestory()方法中調用Asyntask.cancal方法,讓二者的生命週期同步

8.4 Activity銷燬但Task如果沒有銷燬掉,當Activity重啓時這個AsyncTask該如何解決?

還是屏幕旋轉這個例子,在重建Activity的時候,會回掉Activity.onRetainNonConfigurationInstance()重新傳遞一個新的對象給AsyncTask,完成引用的更新

9. Android觸摸分發機制

9.1 介紹觸摸事件的分發機制

enter image description here

(1) 事件從Activity.dispatchTouchEvent()開始傳遞,只要沒有被停止或攔截,從最上層的View(ViewGroup)開始一直往下(子View)傳遞。子View可以通過onTouchEvent()對事件進行處理。

(2) 事件由父View(ViewGroup)傳遞給子View,ViewGroup可以通過onInterceptTouchEvent()對事件做攔截,停止其往下傳遞。

(3) 如果事件從上往下傳遞過程中一直沒有被停止,且最底層子View沒有消費事件,事件會反向往上傳遞,這時父View(ViewGroup)可以進行消費,如果還是沒有被消費的話,最後會到Activity的onTouchEvent()函數。

(4) 如果View沒有對ACTION_DOWN進行消費,之後的其他事件不會傳遞過來。

(5) OnTouchListener優先於onTouchEvent()對事件進行消費。

上面的消費即表示相應函數返回值爲true。

9.2 View中 setOnTouchListener的onTouch,onTouchEvent,onClick的執行順序

追溯到View的dispatchTouchEvent源碼查看,有這麼一段代碼

public boolean dispatchTouchEvent(MotionEvent event) {  
        if (!onFilterTouchEventForSecurity(event)) {  
            return false;  
        }  

        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
                mOnTouchListener.onTouch(this, event)) {  
            return true;  
        }  
        return onTouchEvent(event);  
    }

當以下三個條件任意一個不成立時,

  • mOnTouchListener不爲null
  • view是enable的狀態
  • mOnTouchListener.onTouch(this, event)返回true,

函數會執行到onTouchEvent。在這裏我們可以看到,首先執行的是mOnTouchListener.onTouch的方法,然後是onTouchEvent方法

繼續追溯源碼,到onTouchEvent()觀察,發現在處理ACTION_UP事件裏有這麼一段代碼

 if (!post(mPerformClick)) {  
                                    performClick();  
                                }

此時可知,onClick方法也在最後得到了執行

所以三者的順序是:

  1. setOnTouchListener() 的onTouch
  2. onTouchEvent()
  3. onClick()

10. Dalvik虛擬機系列問題

10.1 什麼是Dalvik虛擬機?

Dalvik虛擬機是Android平臺的核心。它可以支持.dex格式的程序的運行,.dex格式是專爲Dalvik設計的一種壓縮格式,可以減少整體文件尺寸,提高I/O操作的速度,適合內存和處理器速度有限的系統。

10.2 Dalvik虛擬機的作用是什麼?

Dalvik虛擬機主要是完成對象生命週期管理,內存回收,堆棧管理,線程管理,安全和異常管理等等重要功能。

10.3 Dalvik虛擬機與JVM有什麼區別

  • Dalvik 基於寄存器,而 JVM 基於棧。基於寄存器的虛擬機對於更大的程序來說,在它們編譯的時候,花費的時間更短。
  • Dalvik執行.dex格式的字節碼,而JVM執行.class格式的字節碼。

10.4 每個應用程序對應多少個Dalvik虛擬機

  • 每一個Android應用在底層都會對應一個獨立的Dalvik虛擬機實例,其代碼在虛擬機的解釋下得以執行 ,而所有的Android應用的線程都對應一個Linux線程

11. 註冊廣播接收器有哪幾種方式,有什麼區別

  • 靜態註冊:在AndroidManifest.xml文件中進行註冊,當App退出後,Receiver仍然可以接收到廣播並且進行相應的處理
  • 動態註冊:在代碼中動態註冊,當App退出後,也就沒辦法再接受廣播了

12. 顯示Intent與隱式Intent的區別

對明確指出了目標組件名稱的Intent,我們稱之爲“顯式Intent”。 對於沒有明確指出目標組件名稱的Intent,則稱之爲“隱式 Intent”。

對於隱式意圖,在定義Activity時,指定一個intent-filter,當一個隱式意圖對象被一個意圖過濾器進行匹配時,將有三個方面會被參考到:

  • 動作(Action)
  • 類別(Category ['kætɪg(ə)rɪ] )
  • 數據(Data )

13. Android中的動畫有哪些,區別是什麼

  • 逐幀動畫(Drawable Animation): 加載一系列Drawable資源來創建動畫,簡單來說就是播放一系列的圖片來實現動畫效果,可以自定義每張圖片的持續時間

  • 補間動畫(Tween Animation): Tween可以對View對象實現一系列簡單的動畫效果,比如位移,縮放,旋轉,透明度等等。但是它並不會改變View屬性的值,只是改變了View的繪製的位置,比如,一個按鈕在動畫過後,不在原來的位置,但是觸發點擊事件的仍然是原來的座標。

  • 屬性動畫(Property Animation): 動畫的對象除了傳統的View對象,還可以是Object對象,動畫結束後,Object對象的屬性值被實實在在的改變了

14. 不使用動畫,怎麼實現一個動態的 View?

15. Postvalidata與Validata有什麼區別?

16. 如何自定義ViewGroup?

17. View的繪製流程

enter image description here measure()方法,layout(),draw()三個方法主要存放了一些標識符,來判斷每個View是否需要再重新測量,佈局或者繪製,主要的繪製過程還是在onMeasure,onLayout,onDraw這個三個方法中

1.onMesarue() 爲整個View樹計算實際的大小,即設置實際的高(對應屬性:mMeasuredHeight)和寬(對應屬性: mMeasureWidth),每個View的控件的實際寬高都是由父視圖和本身視圖決定的。

2.onLayout() 爲將整個根據子視圖的大小以及佈局參數將View樹放到合適的位置上。

3. onDraw() 開始繪製圖像,繪製的流程如下

  1. 首先繪製該View的背景
  2. 調用onDraw()方法繪製視圖本身 (每個View都需要重載該方法,ViewGroup不需要實現該方法)
  3. 如果該View是ViewGroup,調用dispatchDraw ()方法繪製子視圖
  4. 繪製滾動條

18. 數據持久化的四種方式有哪些?

  1. 文件存儲: 通過java.io.FileInputStream和java.io.FileOutputStream這兩個類來實現對文件的讀寫,java.io.File類則用來構造一個具體指向某個文件或者文件夾的對象。

  2. SharedPreferences: SharedPreferences是一種輕量級的數據存儲機制,他將一些簡單的數據類型的數據,包括boolean類型,int類型,float類型,long類型以及String類型的數據,以鍵值對的形式存儲在應用程序的私有Preferences目錄(/data/data/<包名>/shared_prefs/)中,這種Preferences機制廣泛應用於存儲應用程序中的配置信息。

  3. SQLite數據庫: 當應用程序需要處理的數據量比較大時,爲了更加合理地存儲、管理、查詢數據,我們往往使用關係數據庫來存儲數據。Android系統的很多用戶數據,如聯繫人信息,通話記錄,短信息等,都是存儲在SQLite數據庫當中的,所以利用操作SQLite數據庫的API可以同樣方便的訪問和修改這些數據。

  4. ContentProvider: 主要用於在不同的應用程序之間實現數據共享的功能,不同於sharepreference和文件存儲中的兩種全局可讀寫操作模式,內容提供其可以選擇只對哪一部分數據進行共享,從而保證我們程序中的隱私數據不會有泄漏的風險

19. fragement裏面可以再嵌套fragment?

20. Socker編程的步驟

21. Activity中如何動態的添加Fragment

22. Scrollview怎麼判斷是否滑倒底部

23. 什麼是 MVC 模式?MVC 模式的好處是什麼?

24. 應用常駐後臺,避免被第三方殺掉的方法,講講你用過的奇淫巧技?

  1. Service設置成START_STICKY kill 後會被重啓(等待5秒左右),重傳Intent,保持與重啓前一樣

  2. 通過 startForeground將進程設置爲前臺進程, 做前臺服務,優先級和前臺應用一個級別​,除非在系統內存非常缺,否則此進程不會被 kill

  3. 雙進程Service: 讓2個進程互相保護**,其中一個Service被清理後,另外沒被清理的進程可以立即重啓進程

  4. QQ黑科技: 在應用退到後臺後,另起一個只有 1 像素的頁面停留在桌面上,讓自己保持前臺狀態,保護自己不被後臺清理工具殺死

  5. 在已經root的設備下,修改相應的權限文件,將App僞裝成系統級的應用 Android4.0系列的一個漏洞,已經確認可行

  1. 用C編寫守護進程(即子進程) : Android系統中當前進程(Process)fork出來的子進程,被系統認爲是兩個不同的進程。當父進程被殺死的時候,子進程仍然可以存活,並不受影響。鑑於目前提到的在Android->- Service層做雙守護都會失敗,我們可以fork出c進程,多進程守護。死循環在那檢查是否還存在,具體的思路如下(Android5.0以上的版本不可行)
  2. 用C編寫守護進程(即子進程),守護進程做的事情就是循環檢查目標進程是否存在,不存在則啓動它。
  3. 在NDK環境中將1中編寫的C代碼編譯打包成可執行文件(BUILD_EXECUTABLE)。主進程啓動時將守護進程放入私有目錄下,賦予可執行權限,啓動它即可。

  4. 聯繫廠商,加入白名單

25.Context與ApplicationContext的區別,分別用在什麼情況下

Application的Context是一個全局靜態變量,SDK的說明是隻有當你引用這個context的生命週期超過了當前activity的生命週期,而和整個應用的生命週期掛鉤時,纔去使用這個application的context。

在android中context可以作很多操作,但是最主要的功能是加載和訪問資源。在android中有兩種context,一種是 application context,一種是activity context,通常我們在各種類和方法間傳遞的是activity context。

26. 同一個應用程序的不同Activity可以運行在不同的進程中麼?如果可以,舉例說明;

27. Java中的線程同步有哪幾種方式,舉例說明;

28. dp, dip, dpi, px, sp是什麼意思以及他們的換算公式?layout-sw400dp, layout-h400dp分別代表什麼意思;

29. 如何讓兩個TextView在一個RelativeLayout水平居中顯示;

30. 如何畫出一個印章的圖案

31. 如何實現一個字體的描邊與陰影效果

32. 設計一個從網絡請求數據,圖片,並加載到列表的系統,畫出客戶端架構並簡單的分析下;

33. 設計一個文件的斷點續傳系統;

34. 設計一個圖片緩存加載機制

數據結構與算法部分:

  1. 給最外層的rootview,把這個根視圖下的全部button背景設置成紅色,手寫代碼,不許用遞歸
  2. 給一串字符串比如abbbcccd,輸出a1b3c3d1,手寫代碼(注意有個別字符可能會出現十次以上的情況)
  3. 一個序列,它的形式是12349678,9是最高峯,經歷了一個上升又下降的過程,找出裏面的最大值的位置,要求效率儘可能高
  4. 二叉查找樹的刪除操作,手寫代碼
  5. 反轉鏈表,手寫代碼
  6. 二分查找,手寫代碼
  7. 有海量條 url,其中不重複的有300萬條,現在希望挑選出重複出現次數最高的 url,要求效率儘可能的高
  8. 一篇英語文章,去掉字符只留下k個,如何去掉才能使這k個字符字典序最小
  9. 弗洛伊德算法和 Dijkstra算法的區別?複雜度是多少?講講 Dijkstra算法的具體過程
  10. 反轉字符串,要求手寫代碼,優化速度、優化空間
  11. 給出兩個無向圖,找出這2個無向圖中相同的環路。手寫代碼
  12. 單例模式,手寫代碼
  13. 生產者與消費者,手寫代碼
  14. 二叉樹鏡像,手寫代碼
  15. 最長不重複子串(最長重複子串),手寫代碼

操作系統部分:

  1. 分別從操作系統的內存角度與進程線程角度解釋分析堆,棧二者的區別
  2. 什麼是事務?
  3. OSI七層模型有哪些,各層次的作用
  4. TCP的三次握手過程,四次揮手過程,爲什麼需要三次?
  5. 說說操作系統中進程的通信方式
  6. 瀏覽器輸入地址之後,之後的過程
  7. 談談 HTTP 中Get 和 Post 方法的區別?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章