Android 基礎面試題(2021年)


Activiey啟動:


Dialog不會調用onPause()和onStop(), 非全屏Activity會調用onPause()不會調用onStop(),全屏Activity 會調用onPause()和onStop()。

onStart 可見不可交互
onPause 可見不可交互

Fragment add & replace
Add 之前的fragment沒有銷毀
replace 之前fragment銷毀生命周期。

(SingleTop和SingleTask啟動模式的應用場景,除普通的那些還有onRestoreInstanceState與onSaveInstanceState與onNewIntent以及他們的使用場景與區別)注意調用onNewIntent不會再調用onCreate方法了,會直接調用onStart與onResume。如果是已經不可見的Activity(調用了onStop的,則會先調用onRestart之后在調用onStart方法)。

RecyclerView 相比 ListView 在基礎使用上的區別主要有如下幾點:

  • ViewHolder 的編寫規范化了
  • RecyclerView 復用 Item 的工作 Google 全幫你搞定,不再需要像 ListView 那樣自己調用 setTag
  • RecyclerView 需要多出一步 LayoutManager 的設置工作
    1、RecyclerView緩存RecyclerView.ViewHolder,抽象可理解為:View + ViewHolder(避免每次createView時調用findViewById,不必每次都重新創建很多對象,從而提升性能) + flag(標識狀態)

2、ListView緩存View

recyclerView 四級緩存 listview兩級

RecyclerView提供了局部刷新的接口。

intentService: 內部通過HandlerThread和Handler實現異步操作,onHandleIntent為異步方法,可以執行耗時操作。

多次啟動IntentService,但IntentService的實例只有一個,這跟傳統的Service是一樣的。

LocalBroadcastReceiver不能靜態注冊,只能采用動態注冊的方式。

LocalBroadcastManager的核心實現其實還是 Handler,因此它是應用內的通信,自然安全性更好,運行效率更高。

2、而 BroadcastReceiver 是全局廣播,可以跨進程通信,范圍更廣,從而導致它的運行效率沒有本地廣播高效,畢竟一個是本地的通信,一個是跨進程的通信方式,效率肯定相對較低點,但對於實時性不高的應用場景我們可以忽略不計。

事件分發:
整個事件的傳遞機制,是Android底層收到觸摸屏的事件后,使用socket跨進程通信,用InputDispatcher將事件發送給APP進程,由主線程的Looper去取出消息進行處理
Activity--> Window-->DecorView --> 布局View

  •   1、分發:dispatchTouchEvent;
    
  •   2、攔截:onInterceptTouchEvent;(只有view有)
    
  •   3、處理:onTouchEvent;
    

handler:
Message:信息的攜帶者,持有了Handler,存在MessageQueue中,一個線程可以有多個
Hanlder:消息的發起者,發送Message以及消息處理的回調實現,一個線程可以有多個Handler對象
Looper:消息的遍歷者,從MessageQueue中循環取出Message進行處理,一個線程最多只有一個
MessageQueue:消息隊列,存放了Handler發送的消息,供Looper循環取消息,一個線程最多只有一個.

  1. 處理Handler消息,是在哪個線程?一定是創建Handler的線程么?
    :
    Looper進行循環的那個線程,也就是創建Handler那個線程。因為是在Looper.loop調用Handler去執行消息的,Looper在哪個線程調用,消息就在哪個線程被Handler處理,因為對應Looper所在線程與創建對應Handler的線程必須是同一條,所以無論從哪個線程發送消息給Handler,最終會在創建Handler那個線程執行這個消息。


HTTPS 在內容傳輸的加密上使用的是對稱加密,非對稱加密只作用在證書驗證階段。

首先:非對稱加密的加解密效率是非常低的,而 http 的應用場景中通常端與端之間存在大量的交互,非對稱加密的效率是無法接受的。
另外:在 HTTPS 的場景中只有服務端保存了私鑰,一對公私鑰只能實現單向的加解密,所以HTTPS 中內容傳輸加密采取的是對稱加密,而不是非對稱加密。

LruCache(Least Recently Used)算法的核心思想就是最近最少使用算法。他在算法的內部維護了一個LinkHashMap的鏈表,通過put數據的時候判斷是否內存已經滿了,如果滿了,則將最近最少使用的數據給剔除掉,從而達到內存不會爆滿的狀態。

MeasureSpec三種模式:
1.精確模式(MeasureSpec.EXACTLY)
在這種模式下,尺寸的值是多少,那么這個組件的長或寬就是多少。
2.最大模式(MeasureSpec.AT_MOST)
這個也就是父組件,能夠給出的最大的空間,當前組件的長或寬最大只能為這么大,當然也可以比這個小。
3.未指定模式(MeasureSpec.UNSPECIFIED)

什么是三級緩存
網絡緩存, 不優先加載, 速度慢,浪費流量
本地緩存, 次優先加載, 速度快
內存緩存, 優先加載, 速度最快

這是因為人眼與大腦之間的協作無法感知超過60fps的畫面更新。12fps大概類似手動快速翻動書籍的幀率,這明顯是可以感知到不夠順滑的。24fps使得人眼感知的是連續線性的運動,這其實是歸功於運動模糊的
效果。24fps是電影膠圈通常使用的幀率,因為這個幀率已經足夠支撐大部分電影畫面需要表達的內容,同時能夠最大的減少費用支出。但是低於30fps是
無法順暢表現絢麗的畫面內容的,此時就需要用到60fps來達到想要的效果,當然超過60fps是沒有必要的(據說Dart能夠帶來120fps的體驗)。

內存泄露的原因:對象無用了,但仍然可達(未釋放),垃圾回收器無法回收。

  • CPU產生的問題:不必要的布局和失效
  • GPU產生的問題:過度繪制(overdraw)
    
onResume 后,View初始化時measure三次,layout兩次,draw一次的原因。

LeakCanary 中的 RefWatcher 就是通過弱引用及其隊列來實現監控的:
有兩個很重要的結構: retainedKeys 和 queue ,
retainedKeys 代表沒被gc 回收的對象,
而queue中的弱引用代表的是被gc了的對象,通過這兩個結構就可以監控對象是不是被回收了;

flutter選擇Dart語言很好的實現JIT,能夠讓我在開發中實現Hot Reload。
RN是依托Javascript通過“bridge”來和本地代碼溝通,因此造成了運行效率的低下。AOT可以將Dart代碼編譯成本地代碼(iOS or Android),Dart語言因而獲得更高的運行效率。

JIT,即Just-in-time,動態(即時)編譯,邊運行邊編譯;AOT,Ahead Of Time,指運行前編譯,是兩種程序的編譯方式
區別
這兩種編譯方式的主要區別在於是否在“運行時”進行編譯
優劣
JIT優點:
可以根據當前硬件情況實時編譯生成最優機器指令(ps. AOT也可以做到,在用戶使用是使用字節碼根據機器情況在做一次編譯)
可以根據當前程序的運行情況生成最優的機器指令序列
當程序需要支持動態鏈接時,只能使用JIT
可以根據進程中內存的實際情況調整代碼,使內存能夠更充分的利用
缺點:
編譯需要占用運行時資源,會導致進程卡頓
由於編譯時間需要占用運行時間,對於某些代碼的編譯優化不能完全支持,需要在程序流暢和編譯時間之間做權衡
在編譯准備和識別頻繁使用的方法需要占用時間,使得初始編譯不能達到最高性能
AOT優點:
在程序運行前編譯,可以避免在運行時的編譯性能消耗和內存消耗
可以在程序運行初期就達到最高性能
可以顯著的加快程序的啟動
缺點:
在程序運行前編譯會使程序安裝的時間增加
犧牲Java的一致性
將提前編譯的內容保存會占用更多的外

Dalvik
Dalvik使用JIT
使用.dex字節碼,是針對Android設備優化后的DVM所使用的運行時編譯字節碼
.odex是對dex的優化,deodex在系統第一次開機時會提取所有apk內的dex文件,odex優化將dex提前提取出,加快了開機的速度和程序運行的速度
ART
ART 使用AOT
在安裝apk時會進行預編譯,生成OAT文件,仍以.odex保存,但是與Dalvik下不同,這個文件是可執行文件
dex、odex 均可通過dex2oat生成oat文件,以實現兼容性
在大型應用安裝時需要更多時間和空間

在Android N中引入了一種新的編譯模式,同時使用JIT和AOT

  • 5s內無法響應用戶輸入事件(例如鍵盤輸入, 觸摸屏幕等).
  • BroadcastReceiver在10s內無法結束.
  • Service 15s。

哪些地方是執行在主線程的

  1. Activity的所有生命周期回調都是執行在主線程的.
  2. Service默認是執行在主線程的.
  3. BroadcastReceiver的onReceive回調是執行在主線程的.
  4. 沒有使用子線程的looper的Handler的handleMessage, post(Runnable)是執行在主線程的.
  5. AsyncTask的回調中除了doInBackground, 其他都是執行在主線程的.
  6. View的post(Runnable)是執行在主線程的.

Java實現多線程的方式. 有兩種實現方法, 繼承Thread 或 實現Runnable接口

AsyncTask,HandlerThread,IntentService

默認情況下Handler的handleMessage是執行在主線程的, 但是如果我給這個Handler傳入了子線程的looper, handleMessage就會執行在這個子線程中的. HandlerThread正是這樣的一個結合體

Service是運行在主線程的, 然而IntentService是運行在子線程的.
實際上IntentService就是實現了一個HandlerThread + ServiceHandler的模式.

Model層。也叫模型層,主要負責和數據交互的任務。模型層主要功能有定義數據結構。從數據庫讀、取數據,數據格式驗證,讀數據進行加工處理。

Model層類似與三層架構中的DAL 層。主要與數據庫進行交互。而且進行簡單的數據處理。

View層,即視圖層,負責全部界面層的任務。事實上就是寫入數據和顯示數據。主要功能有獲得數據,顯示數據。決定界面技術(HTML,XML,Flash等)。界面排版;向Controller返回數據,決定數據傳送方式,數據驗證。

View層類似於三層中的UI層,主要是和用戶進行數據交互的。

Controller層。集控制層。接受用戶輸入的數據。調用模型和視圖完畢用戶的需求。當用戶單擊超鏈接或者發送HTML表單時。控制器事實上不做不論什么的處理和輸出,它僅僅是依據實際情況決定調用哪個模型或者視圖去處理這個請求,然后決定使用哪個視圖來顯示返回的處理結果。

MVP模式的優點是實現了View和Model的解耦,缺點是Presenter職責太重。
MVVM模式同樣實現了View和Model的解耦。不過和MVP模式不同的是,MVVM是從數據驅動的角度出發來解決這個問題的。

當ViewPager管理的頁面有大量數據時候,也就是如果viewpager管理的頁面是一個ListView時候,推薦使用FragmentStatePagerAdapter。這是因為FragmentStatePagerAdapter在頁面不可見的時候,會銷毀這個Fragment。所以FragmentStatePagerAdapter比FragmentPagerAdapter占用內存更小。

Serializable和Parcelable對比 
 1.在使用內存的時候,Parcelable比Serializable性能高,所以推薦使用Parcelable。
 2.Serializable在序列化的時候會產生大量的臨時變量,從而引起頻繁的GC。
 3.Parcelable無法將數據進行持久化,因此在將數據保存在磁盤的時候,仍然需要使用后者,因為前者無法很好的將數據進行持久化.(原因是在不同的Android版本當中,Parcelable可能會不同,因此數據的持久化方面仍然是使用Serializable)
 4.Parcelable 不僅僅需要聲明,還需要實現內部的相應方法.writeToParcel;重寫describeContents方法,默認值為0;Public static final Parcelable.Creator CREATOR = new CREATOR (){...}
 5.Parcelable是以Ibinder作為信息載體的.在內存上的開銷比較小,因此在內存之間進行數據傳遞的時候,Android推薦使用Parcelable,既然是內存方面比價有優勢,那么自然就要優先選擇.
 6.在讀寫數據的時候,Parcelable是在內存中直接進行讀寫,而Serializable是通過使用IO流的形式將數據讀寫入在硬盤上.

Handler內存泄露的原因是什么?
"內部類持有了外部類的引用,也就是Hanlder持有了Activity的引用,從而導致無法被回收唄。"
完整的引用。
主線程 —> threadlocal —> Looper —> MessageQueue —> Message —> Handler —> Activity

永不崩潰app:
主線程崩潰,其實都是發生在消息的處理內,包括生命周期、界面繪制。
所以如果我們能控制這個過程,並且在發生崩潰后重新開啟消息循環,那么主線程就能繼續運行

如在ViewGroup的onInterceptTouchEvent方法中,如果在ACTION_DOWN事件中返回了true,那么后續的事件將直接發給onTouchEvent,而不是繼續發給onInterceptTouchEvent。攔截一系列事件。

onLongClick的發生是由單獨的線程完成的,並且在ACTION_UP之前,而onClick的發生是在ACTION_UP后,因此同一次用戶touch操作就有可能既發生onLongClick又發生onClick。這樣是不是不可思議?所以及時向系統表示“我已經完全處理(消費)了用戶的此次操作”,是很重要的事情。例如,我們如果在onLongClick()方法的最后return true,那么onClick事件就沒有機會被觸發了。

android這里的代碼,如果想要能夠讓react-native調用到,需要實現三個步驟:
1.寫出你想要調用的模塊(繼承ReactContextBaseJavaModule)
2.把這個模塊導出(實現導出接口ReactPackage)
3.把這個模塊注冊發布(在MainApplication中注冊)

了解了Handler的內部原理后,再來分析由Handler引起的內存泄露問題:

  1. 當定義了一個非靜態的Handler內部類時,內部類會隱式持有外圍類的引用。
  2. Handler執行sendMessageAtTime方法時,Message的target參數會持有Handler對象。
  3. 當Message沒有被執行時(比如now<when),若退出了Activity,此時Message依然持有Handler對象,而Handler持有Activity的對象,導致內存泄露。
    解決方案:
  4. 將Handler定義為靜態內部類。
  5. 退出Activity時清空MessageQueue中對應的Message。

Jetpack
Lifecycle 的存在,主要是為了解決 生命周期管理 的一致性問題。
LiveData 的存在,主要是為了幫助 新手老手 都能不假思索地 遵循 通過唯一可信源分發狀態 的標准化開發理念,從而在快速開發過程中 規避一系列 難以追溯、難以排查、不可預期 的問題。
ViewModel 的存在,主要是為了解決 狀態管理 和 頁面通信 的問題。
DataBinding 的存在,主要是為了解決 視圖調用 的一致性問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM