Android篇:2019初中級Android開發社招面試解答(中)

金三銀四,衝擊大廠,你值得擁有的一份2019初中級移動端社招面試總結+解答html

你當前所處:Android篇:2019初中級Android開發社招面試解答(中)前端

Android篇:2019初中級Android開發社招面試解答(下)java

Android篇:2019初中級Android開發社招面試解答(上)android

注:由於實際開發與參考答案會有所不一樣,再者怕誤導你們,因此這些面試題答案仍是本身去理解!面試官會針對簡歷中提到的知識點由淺入深提問,因此不要背答案,多理解。git

Handler

一、談談消息機制Handler做用 ?有哪些要素 ?流程是怎樣的 ?

  • 參考回答:
    • 負責跨線程通訊,這是由於在主線程不能作耗時操做,而子線程不能更新UI,因此當子線程中進行耗時操做後須要更新UI時,經過Handler將有關UI的操做切換到主線程中執行。
    • 具體分爲四大要素
      • Message(消息):須要被傳遞的消息,消息分爲硬件產生的消息(如按鈕、觸摸)和軟件生成的消息。
      • MessageQueue(消息隊列):負責消息的存儲與管理,負責管理由 Handler發送過來的Message。讀取會自動刪除消息,單鏈表維護,插入和刪除上有優點。在其next()方法中會無限循環,不斷判斷是否有消息,有就返回這條消息並移除。
      • Handler(消息處理器):負責Message的發送及處理。主要向消息池發送各類消息事件(Handler.sendMessage())和處理相應消息事件(Handler.handleMessage()),按照先進先出執行,內部使用的是單鏈表的結構。
      • Looper(消息池):負責關聯線程以及消息的分發,在該線程下從 MessageQueue獲取 Message,分發給Handler,Looper建立的時候會建立一個 MessageQueue,調用loop()方法的時候消息循環開始,其中會不斷調用messageQueue的next()方法,當有消息就處理,不然阻塞在messageQueue的next()方法中。當Looper的quit()被調用的時候會調用messageQueue的quit(),此時next()會返回null,而後loop()方法也就跟着退出。
    • 具體流程以下
      • 在主線程建立的時候會建立一個Looper,同時也會在在Looper內部建立一個消息隊列。而在創鍵Handler的時候取出當前線程的Looper,並經過該Looper對象得到消息隊列,而後Handler在子線程中經過MessageQueue.enqueueMessage在消息隊列中添加一條Message。
      • 經過Looper.loop() 開啓消息循環不斷輪詢調用 MessageQueue.next(),取得對應的Message而且經過Handler.dispatchMessage傳遞給Handler,最終調用Handler.handlerMessage處理消息。

二、一個線程可否建立多個Handler,Handler跟Looper之間的對應關係 ?

  • 參考回答:
    • 一個Thread只能有一個Looper,一個MessageQueen,能夠有多個Handler
    • 以一個線程爲基準,他們的數量級關係是: Thread(1) : Looper(1) : MessageQueue(1) : Handler(N)

三、軟引用跟弱引用的區別

  • 參考回答:
    • 軟引用(SoftReference):若是一個對象只具備軟引用,則內存空間充足時,垃圾回收器就不會回收它;若是內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就能夠一直被程序使用。
    • 弱引用(WeakReference):若是一個對象只具備弱引用,那麼在垃圾回收器線程掃描的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。
    • 二者之間根本區別在於:只具備弱引用的對象擁有更短暫的生命週期,可能隨時被回收。而只具備軟引用的對象只有當內存不夠的時候才被回收,在內存足夠的時候,一般不被回收。
    • 推薦文章: Java中的四種引用類型:強引用、軟引用、弱引用和虛引用

四、Handler 引發的內存泄露緣由以及最佳解決方案

  • 參考回答:
    • 泄露緣由:
      • Handler 容許咱們發送延時消息,若是在延時期間用戶關閉了 Activity,那麼該 Activity 會泄露。 這個泄露是由於 Message 會持有 Handler,而又由於 Java 的特性,內部類會持有外部類,使得 Activity 會被 Handler 持有,這樣最終就致使 Activity 泄露。
    • 解決方案:
      • 將 Handler 定義成靜態的內部類,在內部持有Activity的弱引用,並在Acitivity的onDestroy()中調用handler.removeCallbacksAndMessages(null)及時移除全部消息。

五、爲何系統不建議在子線程訪問UI?

  • 參考回答:
    • Android的UI控件不是線程安全的,若是在多線程中併發訪問可能會致使UI控件處於不可預期的狀態
    • 這時你可能會問爲什麼系統不對UI控件的訪問加上鎖機制呢?由於
      • 加鎖機制會讓UI訪問邏輯變的複雜
      • 加鎖機制會下降UI的訪問效率,由於加鎖會阻塞某些線程的執行

六、Looper死循環爲何不會致使應用卡死?

  • 參考回答:
    • 主線程的主要方法就是消息循環,一旦退出消息循環,那麼你的應用也就退出了,Looer.loop()方法可能會引發主線程的阻塞,但只要它的消息循環沒有被阻塞,能一直處理事件就不會產生ANR異常。
    • 形成ANR的不是主線程阻塞,而是主線程的Looper消息處理過程發生了任務阻塞,沒法響應手勢操做,不能及時刷新UI。
    • 阻塞與程序無響應沒有必然關係,雖然主線程在沒有消息可處理的時候是阻塞的,可是隻要保證有消息的時候可以馬上處理,程序是不會無響應的。

七、使用Handler的postDealy後消息隊列會有什麼變化?

  • 參考回答:
    • 若是隊列中只有這個消息,那麼消息不會被髮送,而是計算到時喚醒的時間,先將Looper阻塞,到時間就喚醒它。但若是此時要加入新消息,該消息隊列的對頭跟delay時間相比更長,則插入到頭部,按照觸發時間進行排序,隊頭的時間最小、隊尾的時間最大

八、能夠在子線程直接new一個Handler嗎?怎麼作?

  • 參考回答:
    • 不能夠,由於在主線程中,Activity內部包含一個Looper對象,它會自動管理Looper,處理子線程中發送過來的消息。而對於子線程而言,沒有任何對象幫助咱們維護Looper對象,因此須要咱們本身手動維護。因此要在子線程開啓Handler要先建立Looper,並開啓Looper循環
  • 推薦文章:Android異步消息處理機制徹底解析,帶你從源碼的角度完全理解

九、Message能夠如何建立?哪一種效果更好,爲何?

  • 參考回答:能夠經過三種方法建立:
    • 直接生成實例Message m = new Message
    • 經過Message m = Message.obtain
    • 經過Message m = mHandler.obtainMessage()
  • 後二者效果更好,由於Android默認的消息池中消息數量是10,然後二者是直接在消息池中取出一個Message實例,這樣作就能夠避免多生成Message實例。

線程

一、線程池的好處? 四種線程池的使用場景,線程池的幾個參數的理解?

  • 參考回答:
    • 使用線程池的好處是減小在建立和銷燬線程上所花的時間以及系統資源的開銷,解決資源不足的問題。若是不使用線程池,有可能形成系統建立大量同類線程而致使消耗完內存或則「過分切換」的問題,概括總結就是
      • 重用存在的線程,減小對象建立、消亡的開銷,性能佳。
      • 可有效控制最大併發線程數,提升系統資源的使用率,同時避免過多資源競爭,避免堵塞。
      • 提供定時執行、按期執行、單線程、併發數控制等功能。
    • Android中的線程池都是直接或間接經過配置ThreadPoolExecutor來實現不一樣特性的線程池.Android中最多見的類具備不一樣特性的線程池分別爲:
      • newCachedThreadPool:只有非核心線程,最大線程數很是大,全部線程都活動時會爲新任務建立新線程,不然會利用空閒線程 ( 60s空閒時間,過了就會被回收,因此線程池中有0個線程的可能 )來處理任務.
        • 優勢:任何任務都會被當即執行(任務隊列SynchronousQuue至關於一個空集合);比較適合執行大量的耗時較少的任務.
      • newFixedThreadPool:只有核心線程,而且數量固定的,全部線程都活動時,由於隊列沒有限制大小,新任務會等待執行,當線程池空閒時不會釋放工做線程,還會佔用必定的系統資源。
        • 優勢:更快的響應外界請求
      • newScheduledThreadPool:核心線程數固定,非核心線程(閒着沒活幹會被當即回收數)沒有限制.
        • 優勢:執行定時任務以及有固定週期的重複任務
      • newSingleThreadExecutor:只有一個核心線程,確保全部的任務都在同一線程中按序完成
        • 優勢:不須要處理線程同步的問題
    • 經過源碼能夠了解到上面的四種線程池實際上仍是利用 ThreadPoolExecutor 類實現的
  • 推薦文章:java線程池解析和四種線程池的使用

二、Android中還了解哪些方便線程切換的類?

  • 參考回答:
    • AsyncTask:底層封裝了線程池和Handler,便於執行後臺任務以及在子線程中進行UI操做。
    • HandlerThread:一種具備消息循環的線程,其內部可以使用Handler。
    • IntentService:是一種異步、會自動中止的服務,內部採用HandlerThread。

三、講講AsyncTask的原理

  • 參考回答:
    • AsyncTask中有兩個線程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一個Handler(InternalHandler),其中線程池SerialExecutor用於任務的排隊,而線程池THREAD_POOL_EXECUTOR用於真正地執行任務,InternalHandler用於將執行環境從線程池切換到主線程。
    • sHandler是一個靜態的Handler對象,爲了可以將執行環境切換到主線程,這就要求sHandler這個對象必須在主線程建立。因爲靜態成員會在加載類的時候進行初始化,所以這就變相要求AsyncTask的類必須在主線程中加載,不然同一個進程中的AsyncTask都將沒法正常工做。

四、IntentService有什麼用 ?

  • 參考回答:
    • IntentService可用於執行後臺耗時的任務,當任務執行完成後會自動中止,同時因爲IntentService是服務的緣由,不一樣於普通Service,IntentService可自動建立子線程來執行任務,這致使它的優先級比單純的線程要高,不容易被系統殺死,因此IntentService比較適合執行一些高優先級的後臺任務。

五、直接在Activity中建立一個thread跟在service中建立一個thread之間的區別?

  • 參考回答:
    • 在Activity中被建立:該Thread的就是爲這個Activity服務的,完成這個特定的Activity交代的任務,主動通知該Activity一些消息和事件,Activity銷燬後,該Thread也沒有存活的意義了。
    • 在Service中被建立:這是保證最長生命週期的Thread的惟一方式,只要整個Service不退出,Thread就能夠一直在後臺執行,通常在Service的onCreate()中建立,在onDestroy()中銷燬。因此,在Service中建立的Thread,適合長期執行一些獨立於APP的後臺任務,比較常見的就是:在Service中保持與服務器端的長鏈接。

六、ThreadPoolExecutor的工做策略 ?

  • 參考回答:ThreadPoolExecutor執行任務時會遵循以下規則
    • 若是線程池中的線程數量未達到核心線程的數量,那麼會直接啓動一個核心線程來執行任務。
    • 若是線程池中的線程數量已經達到或則超過核心線程的數量,那麼任務會被插入任務隊列中排隊等待執行。
    • 若是在第2點沒法將任務插入到任務隊列中,這每每是因爲任務隊列已滿,這個時候若是在線程數量未達到線程池規定的最大值,那麼會馬上啓動一個非核心線程來執行任務。
    • 若是第3點中線程數量已經達到線程池規定的最大值,那麼就拒絕執行此任務,ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution方法來通知調用者。

七、Handler、Thread和HandlerThread的差異?

  • 參考回答:
    • Handler:在android中負責發送和處理消息,經過它能夠實現其餘支線線程與主線程之間的消息通信。
    • Thread:Java進程中執行運算的最小單位,亦即執行處理機調度的基本單位。某一進程中一路單獨運行的程序。
    • HandlerThread:一個繼承自Thread的類HandlerThread,Android中沒有對Java中的Thread進行任何封裝,而是提供了一個繼承自Thread的類HandlerThread類,這個類對Java的Thread作了不少便利的封裝。HandlerThread繼承於Thread,因此它本質就是個Thread。與普通Thread的差異就在於,它在內部直接實現了Looper的實現,這是Handler消息機制必不可少的。有了本身的looper,可讓咱們在本身的線程中分發和處理消息。若是不用HandlerThread的話,須要手動去調用Looper.prepare()和Looper.loop()這些方法。

八、ThreadLocal的原理

  • 參考回答:
    • ThreadLocal是一個關於建立線程局部變量的類。使用場景以下所示:
      • 實現單個線程單例以及單個線程上下文信息存儲,好比交易id等。
      • 實現線程安全,非線程安全的對象使用ThreadLocal以後就會變得線程安全,由於每一個線程都會有一個對應的實例。 承載一些線程相關的數據,避免在方法中來回傳遞參數。
    • 當須要使用多線程時,有個變量恰巧不須要共享,此時就沒必要使用synchronized這麼麻煩的關鍵字來鎖住,每一個線程都至關於在堆內存中開闢一個空間,線程中帶有對共享變量的緩衝區,經過緩衝區將堆內存中的共享變量進行讀取和操做,ThreadLocal至關於線程內的內存,一個局部變量。每次能夠對線程自身的數據讀取和操做,並不須要經過緩衝區與 主內存中的變量進行交互。並不會像synchronized那樣修改主內存的數據,再將主內存的數據複製到線程內的工做內存。ThreadLocal可讓線程獨佔資源,存儲於線程內部,避免線程堵塞形成CPU吞吐降低。
    • 在每一個Thread中包含一個ThreadLocalMap,ThreadLocalMap的key是ThreadLocal的對象,value是獨享數據。

九、多線程是否必定會高效(優缺點)

  • 參考回答:
    • 多線程的優勢:
      • 方便高效的內存共享 - 多進程下內存共享比較不便,且會抵消掉多進程編程的好處
      • 較輕的上下文切換開銷 - 不用切換地址空間,不用更改CR3寄存器,不用清空TLB
      • 線程上的任務執行完後自動銷燬
    • 多線程的缺點:
      • 開啓線程須要佔用必定的內存空間(默認狀況下,每個線程都佔512KB)
      • 若是開啓大量的線程,會佔用大量的內存空間,下降程序的性能
      • 線程越多,cpu在調用線程上的開銷就越大
      • 程序設計更加複雜,好比線程間的通訊、多線程的數據共享
    • 綜上得出,多線程不必定能提升效率,在內存空間緊張的狀況下反而是一種負擔,所以在平常開發中,應儘可能
      • 不要頻繁建立,銷燬線程,使用線程池
      • 減小線程間同步和通訊(最爲關鍵)
      • 避免須要頻繁共享寫的數據
      • 合理安排共享數據結構,避免僞共享(false sharing)
      • 使用非阻塞數據結構/算法
      • 避免可能產生可伸縮性問題的系統調用(好比mmap)
      • 避免產生大量缺頁異常,儘可能使用Huge Page
      • 能夠的話使用用戶態輕量級線程代替內核線程

十、多線程中,讓你作一個單例,你會怎麼作

  • 參考回答:
    • 多線程中創建單例模式考慮的因素有不少,好比線程安全 -延遲加載-代碼安全:如防止序列化攻擊,防止反射攻擊(防止反射進行私有方法調用) -性能因素
    • 實現方法有多種,餓漢,懶漢(線程安全,線程非安全),雙重檢查(DCL),內部類,以及枚舉
    • 推薦文章:單例模式的總結

十一、除了notify還有什麼方式能夠喚醒線程

  • 參考回答:
    • 當一個擁有Object鎖的線程調用 wait()方法時,就會使當前線程加入object.wait 等待隊列中,而且釋放當前佔用的Object鎖,這樣其餘線程就有機會獲取這個Object鎖,得到Object鎖的線程調用notify()方法,就能在Object.wait 等待隊列中隨機喚醒一個線程(該喚醒是隨機的與加入的順序無關,優先級高的被喚醒機率會高)
    • 若是調用notifyAll()方法就喚醒所有的線程。注意:調用notify()方法後並不會當即釋放object鎖,會等待該線程執行完畢後釋放Object鎖。

十二、什麼是ANR ? 什麼狀況會出現ANR ?如何避免 ? 在不看代碼的狀況下如何快速定位出現ANR問題所在 ?

  • 參考回答:
    • ANR(Application Not Responding,應用無響應):當操做在一段時間內系統沒法處理時,會在系統層面會彈出ANR對話框
    • 產生ANR多是由於5s內無響應用戶輸入事件、10s內未結束BroadcastReceiver、20s內未結束Service
    • 想要避免ANR就不要在主線程作耗時操做,而是經過開子線程,方法好比繼承Thread或實現Runnable接口、使用AsyncTask、IntentService、HandlerThread等
    • 推薦文章:如何快速分析定位ANR

Bitmap

一、Bitmap使用須要注意哪些問題 ?

  • 參考回答:
    • 要選擇合適的圖片規格(bitmap類型):一般咱們優化Bitmap時,當須要作性能優化或者防止OOM,咱們一般會使用RGB_565,由於ALPHA_8只有透明度,顯示通常圖片沒有意義,Bitmap.Config.ARGB_4444顯示圖片不清楚,Bitmap.Config.ARGB_8888佔用內存最多。:
      • ALPHA_8 每一個像素佔用1byte內存
      • ARGB_4444 每一個像素佔用2byte內存
      • ARGB_8888 每一個像素佔用4byte內存(默認)
      • RGB_565 每一個像素佔用2byte內存
    • 下降採樣率:BitmapFactory.Options 參數inSampleSize的使用,先把options.inJustDecodeBounds設爲true,只是去讀取圖片的大小,在拿到圖片的大小以後和要顯示的大小作比較經過calculateInSampleSize()函數計算inSampleSize的具體值,獲得值以後。options.inJustDecodeBounds設爲false讀圖片資源。
    • 複用內存:即經過軟引用(內存不夠的時候纔會回收掉),複用內存塊,不須要再從新給這個bitmap申請一塊新的內存,避免了一次內存的分配和回收,從而改善了運行效率。
    • 使用recycle()方法及時回收內存
    • 壓縮圖片

二、Bitmap.recycle()會當即回收麼?何時會回收?若是沒有地方使用這個Bitmap,爲何垃圾回收不會直接回收?

  • 參考回答:
    • 經過源碼能夠了解到,加載Bitmap到內存裏之後,是包含兩部份內存區域的。簡單的說,一部分是Java部分的,一部分是C部分的。這個Bitmap對象是由Java部分分配的,不用的時候系統就會自動回收了
    • 可是那個對應的C可用的內存區域,虛擬機是不能直接回收的,這個只能調用底層的功能釋放。因此須要調用recycle()方法來釋放C部分的內存
    • bitmap.recycle()方法用於回收該Bitmap所佔用的內存,接着將bitmap置空,最後使用System.gc()調用一下系統的垃圾回收器進行回收,調用System.gc()並不能保證當即開始進行回收過程,而只是爲了加快回收的到來。

三、一張Bitmap所佔內存以及內存佔用的計算

  • 參考回答:
    • Bitamp 所佔內存大小 = 寬度像素 x (inTargetDensity / inDensity) x 高度像素 x (inTargetDensity / inDensity)x 一個像素所佔的內存字節大小
      • 注:這裏inDensity表示目標圖片的dpi(放在哪一個資源文件夾下),inTargetDensity表示目標屏幕的dpi,因此你能夠發現inDensity和inTargetDensity會對Bitmap的寬高進行拉伸,進而改變Bitmap佔用內存的大小。
    • 在Bitmap裏有兩個獲取內存佔用大小的方法。
      • getByteCount():API12 加入,表明存儲 Bitmap 的像素須要的最少內存。
      • getAllocationByteCount():API19 加入,表明在內存中爲 Bitmap 分配的內存大小,代替了 getByteCount() 方法。
      • 不復用 Bitmap 時,getByteCount() 和 getAllocationByteCount 返回的結果是同樣的。在經過複用 Bitmap 來解碼圖片時,那麼 getByteCount() 表示新解碼圖片佔用內存的大 小,getAllocationByteCount() 表示被複用 Bitmap 真實佔用的內存大小

四、Android中緩存更新策略 ?

  • 參考回答:
    • Android的緩存更新策略沒有統一的標準,通常來講,緩存策略主要包含緩存的添加、獲取和刪除這三類操做,但無論是內存緩存仍是存儲設備緩存,它們的緩存容量是有限制的,所以刪除一些舊緩存並添加新緩存,如何定義緩存的新舊這就是一種策略,不一樣的策略就對應着不一樣的緩存算法
    • 好比能夠簡單地根據文件的最後修改時間來定義緩存的新舊,當緩存滿時就將最後修改時間較早的緩存移除,這就是一種緩存算法,但不算很完美

五、LRU的原理 ?

  • 參考回答:
    • 爲減小流量消耗,可採用緩存策略。經常使用的緩存算法是LRU(Least Recently Used):當緩存滿時, 會優先淘汰那些近期最少使用的緩存對象。主要是兩種方式:
    • LruCache(內存緩存):LruCache類是一個線程安全的泛型類:內部採用一個LinkedHashMap以強引用的方式存儲外界的緩存對象,並提供get和put方法來完成緩存的獲取和添加操做,當緩存滿時會移除較早使用的緩存對象,再添加新的緩存對象。
    • DiskLruCache(磁盤緩存): 經過將緩存對象寫入文件系統從而實現緩存效果

性能優化

一、圖片的三級緩存中,圖片加載到內存中,若是內存快爆了,會發生什麼?怎麼處理?

  • 參考回答:
    • 首先咱們要清楚圖片的三級緩存是如何的
      若是內存足夠時不回收。內存不夠時就回收軟引用對象

二、內存中若是加載一張500*500的png高清圖片.應該是佔用多少的內存?

  • 參考回答:
    • 不考慮屏幕比的話:佔用內存=500 * 500 * 4 = 1000000B ≈ 0.95MB
    • 考慮屏幕比的的話:佔用內存= 寬度像素 x (inTargetDensity / inDensity) x 高度像素 x (inTargetDensity / inDensity)x 一個像素所佔的內存字節大小
    • inDensity表示目標圖片的dpi(放在哪一個資源文件夾下),inTargetDensity表示目標屏幕的dpi

三、WebView的性能優化 ?

  • 參考回答:
    • 一個加載網頁的過程當中,native、網絡、後端處理、CPU都會參與,各自都有必要的工做和依賴關係;讓他們相互並行處理而不是相互阻塞纔可讓網頁加載更快:
      • WebView初始化慢,能夠在初始化同時先請求數據,讓後端和網絡不要閒着。
      • 經常使用 JS 本地化及延遲加載,使用第三方瀏覽內核
      • 後端處理慢,可讓服務器分trunk輸出,在後端計算的同時前端也加載網絡靜態資源。
      • 腳本執行慢,就讓腳本在最後運行,不阻塞頁面解析。
      • 同時,合理的預加載、預緩存可讓加載速度的瓶頸更小。
      • WebView初始化慢,就隨時初始化好一個WebView待用。
      • DNS和連接慢,想辦法複用客戶端使用的域名和連接。
        WebView啓動時間
    • 推薦文章:WebView性能、體驗分析與優化

四、Bitmap如何處理大圖,如一張30M的大圖,如何預防OOM?

  • 參考回答:避免OOM的問題就須要對大圖片的加載進行管理,主要經過縮放來減少圖片的內存佔用。
    • BitmapFactory提供的加載圖片的四類方法(decodeFile、decodeResource、decodeStream、decodeByteArray)都支持BitmapFactory.Options參數,經過inSampleSize參數就能夠很方便地對一個圖片進行採樣縮放
    • 好比一張10241024的高清圖片來講。那麼它佔有的內存爲102410244,即4MB,若是inSampleSize爲2,那麼採樣後的圖片佔用內存只有512512*4,即1MB(注意:根據最新的官方文檔指出,inSampleSize的取值應該老是爲2的指數,即一、二、四、8等等,若是外界輸入不足爲2的指數,系統也會默認選擇最接近2的指數代替,好比2
    • 綜合考慮。經過採樣率便可有效加載圖片,流程以下
      • 將BitmapFactory.Options的inJustDecodeBounds參數設爲true並加載圖片
      • 從BitmapFactory.Options中取出圖片的原始寬高信息,它們對應outWidth和outHeight參數
      • 根據採樣率的規則並結合目標View的所需大小計算出採樣率inSampleSize
      • 將BitmapFactory.Options的inJustDecodeBounds參數設爲false,從新加載圖片
    • 推薦文章:Android高效加載大圖、多圖解決方案,有效避免程序OOM

五、內存回收機制與GC算法(各類算法的優缺點以及應用場景);GC原理時機以及GC對象

  • 參考回答:
    • 內存斷定對象可回收有兩種機制:
      • 引用計數算法:給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任什麼時候刻計數器爲0的對象就是不可能再被使用的。然而在主流的Java虛擬機裏未選用引用計數算法來管理內存,主要緣由是它難以解決對象之間相互循環引用的問題,因此出現了另外一種對象存活斷定算法。
      • 可達性分析法:經過一系列被稱爲『GCRoots』的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證實此對象是不可用的。其中可做爲GC Roots的對象:虛擬機棧中引用的對象,主要是指棧幀中的本地變量*、本地方法棧中Native方法引用的對象、方法區中類靜態屬性引用的對象、方法區中常量引用的對象
    • GC回收算法有如下四種:
      • 分代收集算法:是當前商業虛擬機都採用的一種算法,根據對象存活週期的不一樣,將Java堆劃分爲新生代和老年代,並根據各個年代的特色採用最適當的收集算法。
      • 新生代:大批對象死去,只有少許存活。使用『複製算法』,只需複製少許存活對象便可。
        • 複製算法:把可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用盡後,把還存活着的對象『複製』到另一塊上面,再將這一塊內存空間一次清理掉。實現簡單,運行高效。在對象存活率較高時就要進行較多的複製操做,效率將會變低
      • 老年代:對象存活率高。使用『標記—清理算法』或者『標記—整理算法』,只需標記較少的回收對象便可。
        • 標記-清除算法:首先『標記』出全部須要回收的對象,而後統一『清除』全部被標記的對象。標記和清除兩個過程的效率都不高,清除以後會產生大量不連續的內存碎片,空間碎片太多可能會致使之後在程序運行過程當中須要分配較大對象時,沒法找到足夠的連續內存而不得不提早觸發另外一次垃圾收集動做。
        • 標記-整理算法:首先『標記』出全部須要回收的對象,而後進行『整理』,使得存活的對象都向一端移動,最後直接清理掉端邊界之外的內存。標記整理算法會將全部的存活對象移動到一端,並對不存活對象進行處理,所以其不會產生內存碎片
    • 推薦文章:圖解Java 垃圾回收機制

六、內存泄露和內存溢出的區別 ?AS有什麼工具能夠檢測內存泄露

  • 參考回答:
    • 內存溢出(out of memory):是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;好比申請了一個integer,但給它存了long才能存下的數,那就是內存溢出。
    • 內存泄露(memory leak):是指程序在申請內存後,沒法釋放已申請的內存空間,一次內存泄露危害能夠忽略,但內存泄露堆積後果很嚴重,不管多少內存,早晚會被佔光。memory leak會最終會致使out of memory!
    • 查找內存泄漏能夠使用Android Studio 自帶的AndroidProfiler工具或MAT

七、性能優化,怎麼保證應用啓動不卡頓? 黑白屏怎麼處理?

  • 參考回答:
    • 應用啓動速度,取決於你在application裏面時候作了什麼事情,好比你集成了不少sdk,而且sdk的init操做都須要在主線程裏實現因此會有卡頓的感受。在非必要的狀況下能夠把加載延後或則開啓子線程處理
    • 另外,影響界面卡頓的兩大因素,分別是界面繪製和數據處理。
      • 佈局優化(使用include,merge標籤,複雜佈局推薦使用ConstraintLayout等)
      • onCreate() 中不執行耗時操做 把頁面顯示的 View 細分一下,放在 AsyncTask 裏逐步顯示,用 Handler 更好。這樣用戶的看到的就是有層次有步驟的一個個的 View 的展現,不會是先看到一個黑屏,而後一下顯示全部 View。最好作成動畫,效果更天然。
      • 利用多線程的目的就是儘量的減小 onCreate() 和 onReume() 的時間,使得用戶能儘快看到頁面,操做頁面。
      • 減小主線程阻塞時間。
      • 提升 Adapter 和 AdapterView 的效率。
    • 推薦文章:Android 性能優化以內存檢測、卡頓優化、耗電優化、APK瘦身
    • 黑白屏產生緣由:當咱們在啓動一個應用時,系統會去檢查是否已經存在這樣一個進程,若是不存在,系統的服務會先檢查startActivity中的intent的信息,而後在去建立進程,最後啓動Acitivy,即冷啓動。而啓動出現白黑屏的問題,就是在這段時間內產生的。系統在繪製頁面加載佈局以前,首先會初始化窗口(Window),而在進行這一步操做時,系統會根據咱們設置的Theme來指定它的Theme 主題顏色,咱們在Style中的設置就決定了顯示的是白屏仍是黑屏。
      • windowIsTranslucent和windowNoTitle,將這兩個屬性都設置成true (會有明顯的卡頓體驗,不推薦)
      • 若是啓動頁只是是一張圖片,那麼爲啓動頁專注設置一個新的主題,設置主題的android:windowBackground屬性爲啓動頁背景圖便可
      • 使用layer-list製做一張圖片launcher_layer.xml,將其設置爲啓動頁專注主題的背景,並將其設置爲啓動頁佈局的背景。
    • 推薦文章:Android啓動頁解決攻略

八、強引用置爲null,會不會被回收?

  • 參考回答:
    • 不會當即釋放對象佔用的內存。 若是對象的引用被置爲null,只是斷開了當前線程棧幀中對該對象的引用關係,而 垃圾收集器是運行在後臺的線程,只有當用戶線程運行到安全點(safe point)或者安全區域纔會掃描對象引用關係,掃描到對象沒有被引用則會標記對象,這時候仍然不會當即釋放該對象內存,由於有些對象是可恢復的(在 finalize方法中恢復引用 )。只有肯定了對象沒法恢復引用的時候纔會清除對象內存。

九、ListView跟RecyclerView的區別

  • 參考回答:
    • 動畫區別:
      • RecyclerView中,內置有許多動畫API,例如:notifyItemChanged(), notifyDataInserted(), notifyItemMoved()等等;若是須要自定義動畫效果,能夠經過實現(RecyclerView.ItemAnimator類)完成自定義動畫效果,而後調用RecyclerView.setItemAnimator();
      • 可是ListView並無實現動畫效果,但咱們能夠在Adapter本身實現item的動畫效果;
    • 刷新區別:
      • ListView中一般刷新數據是用全局刷新notifyDataSetChanged(),這樣一來就會很是消耗資源;自己沒法實現局部刷新,可是若是要在ListView實現局部刷新,依然是能夠實現的,當一個item數據刷新時,咱們能夠在Adapter中,實現一個onItemChanged()方法,在方法裏面獲取到這個item的position(能夠經過getFirstVisiblePosition()),而後調用getView()方法來刷新這個item的數據;
      • RecyclerView中能夠實現局部刷新,例如:notifyItemChanged();
    • 緩存區別:
      • RecyclerView比ListView多兩級緩存,支持多個離ItemView緩存,支持開發者自定義緩存處理邏輯,支持全部RecyclerView共用同一個RecyclerViewPool(緩存池)。
      • ListView和RecyclerView緩存機制基本一致,但緩存使用不一樣
    • 推薦文章:

十、ListView的adapter是什麼adapter

  • 參考回答:
    • BaseAdapter:抽象類,實際開發中咱們會繼承這個類而且重寫相關方法,用得最多的一個適配器!
    • ArrayAdapter:支持泛型操做,最簡單的一個適配器,只能展示一行文字〜
    • SimpleAdapter:一樣具備良好擴展性的一個適配器,能夠自定義多種效果!
    • SimpleCursorAdapter:用於顯示簡單文本類型的listView,通常在數據庫那裏會用到,不過有點過期,不推薦使用!

十一、LinearLayout、FrameLayout、RelativeLayout性能對比,爲何?

  • 參考回答:
    • RelativeLayout會讓子View調用2次onMeasure,LinearLayout 在有weight時,也會調用子 View 2次onMeasure
    • RelativeLayout的子View若是高度和RelativeLayout不一樣,則會引起效率問題,當子View很複雜時,這個問題會更加嚴重。若是能夠,儘可能使用padding代替margin。
    • 在不影響層級深度的狀況下,使用LinearLayout和FrameLayout而不是RelativeLayout。

JNI

一、對JNI是否瞭解

  • 參考回答:
    • Java的優勢是跨平臺,但也由於其跨平臺的的特性致使其本地交互的能力不夠強大,一些和操做系統相關的的特性Java沒法完成,因而Java提供JNI專門用於和本地代碼交互,經過JNI,用戶能夠調用C、C++編寫的本地代碼
    • NDK是Android所提供的一個工具集合,經過NDK能夠在Android中更加方便地經過JNI訪問本地代碼,其優勢在於
      • 提升代碼的安全性。因爲so庫反編譯困難,所以NDK提升了Android程序的安全性
      • 能夠很方便地使用目前已有的C/C++開源庫
      • 便於平臺的移植。經過C/C++實現的動態庫能夠很方便地在其它平臺上使用
      • 提升程序在某些特定情形下的執行效率,可是並不能明顯提高Android程序的性能

二、如何加載NDK庫 ?如何在JNI中註冊Native函數,有幾種註冊方法 ?

  • 參考回答:
public class JniTest{
        //加載NDK庫 
        static{
            System.loadLirary("jni-test");
        }
    }

複製代碼

三、你用JNI來實現過什麼功能 ? 怎麼實現的 ?(加密處理、影音方面、圖形圖像處理)

設計模式

一、你所知道的設計模式有哪些?

  • 參考回答:
    • 建立型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
    • 結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
    • 行爲型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄 模式、狀態模式、訪問者模式、中介者模式、解釋器模式。

二、談談MVC、MVP和MVVM,好在哪裏,很差在哪裏 ?

  • 參考回答:
    • MVC:
      • 視圖層(View) 對應於xml佈局文件和java代碼動態view部分
      • 控制層(Controller) MVC中Android的控制層是由Activity來承擔的,Activity原本主要是做爲初始化頁面,展現數據的操做,可是由於XML視圖功能太弱,因此Activity既要負責視圖的顯示又要加入控制邏輯,承擔的功能過多。
      • 模型層(Model) 針對業務模型,創建數據結構和相關的類,它主要負責網絡請求,數據庫處理,I/O的操做。
    • 總結
      • 具備必定的分層,model完全解耦,controller和view並無解耦層與層之間的交互儘可能使用回調或者去使用消息機制去完成,儘可能避免直接持有 controller和view在android中沒法作到完全分離,但在代碼邏輯層面必定要分清業務邏輯被放置在model層,可以更好的複用和修改增長業務。
    • MVP
      • 經過引入接口BaseView,讓相應的視圖組件如Activity,Fragment去實現BaseView,實現了視圖層的獨立,經過中間層Preseter實現了Model和View的徹底解耦。MVP完全解決了MVC中View和Controller傻傻分不清楚的問題,可是隨着業務邏輯的增長,一個頁面可能會很是複雜,UI的改變是很是多,會有很是多的case,這樣就會形成View的接口會很龐大。
    • MVVM
      • MVP中咱們說過隨着業務邏輯的增長,UI的改變多的狀況下,會有很是多的跟UI相關的case,這樣就會形成View的接口會很龐大。而MVVM就解決了這個問題,經過雙向綁定的機制,實現數據和UI內容,只要想改其中一方,另外一方都可以及時更新的一種設計理念,這樣就省去了不少在View層中寫不少case的狀況,只須要改變數據就行。
    • 三者如何選擇?
      • 若是項目簡單,沒什麼複雜性,將來改動也不大的話,那就不要用設計模式或者架構方法,只須要將每一個模塊封裝好,方便調用便可,不要爲了使用設計模式或架構方法而使用。
      • 對於偏向展現型的app,絕大多數業務邏輯都在後端,app主要功能就是展現數據,交互等,建議使用mvvm。
      • 對於工具類或者須要寫不少業務邏輯app,使用mvp或者mvvm均可。
  • 推薦文章:MVC、MVP、MVVM,我到底該怎麼選?

三、封裝p層以後.若是p層數據過大,如何解決?

四、是否能從Android中舉幾個例子說說用到了什麼設計模式 ?

  • 參考回答:
    • AlertDialog、Notification源碼中使用了Builder(建造者)模式完成參數的初始化
    • Okhttp內部使用了責任鏈模式來完成每一個Interceptor攔截器的調用
    • RxJava的觀察者模式;單例模式;GridView的適配器模式;Intent的原型模式
    • 平常開發的BaseActivity抽象工廠模式

五、裝飾模式和代理模式有哪些區別 ?

  • 參考回答:
    • 裝飾器模式與代理模式的區別就在於
      • 二者都是對類的方法進行擴展,但裝飾器模式強調的是加強自身,在被裝飾以後你可以在被加強的類上使用加強後的功能。
      • 代理模式則強調要讓別人幫你去作一些自己與你業務沒有太多關係的職責(記錄日誌、設置緩存)代理模式是爲了實現對象的控制,由於被代理的對象每每難以直接得到或者是其內部不想暴露出來。

六、實現單例模式有幾種方法 ?懶漢式中雙層鎖的目的是什麼 ?兩次判空的目的又是什麼 ?

  • 參考回答:
    • 單例模式實現方法有多種:餓漢,懶漢(線程安全,線程非安全),雙重檢查(DCL),內部類,以及枚舉
    • 所謂雙層檢驗鎖(在加鎖先後對實例對象進行兩次判空的檢驗):加鎖是爲了第一次對象實例化的線程同步,而鎖內還要有第二層判空是由於可能會有多個線程進入第一層if判斷內部,而在加鎖代碼塊外排隊等候,若是鎖內不進行第二次檢驗,仍然會出現實例化多個對象的狀況。
    • 推薦文章:單例模式的總結

七、用到的一些開源框架,介紹一個看過源碼的,內部實現過程。

八、Fragment若是在Adapter中使用應該如何解耦?

  • 參考回答:
    • 接口回調
    • 廣播

你當前所處:Android篇:2019初中級Android開發社招面試解答(中)github

Android篇:2019初中級Android開發社招面試解答(下)web

Android篇:2019初中級Android開發社招面試解答(上)面試

相關文章
相關標籤/搜索