Android開發四年面試相關知識整理

現創建了一個Android開發水友圈,圈內會不定時更新一些Android中高級的進階資料,歡迎大家帶著技術問題來討論,共同成長進步!(包含資深UI工程師,Android底層開發工程師,Android架構師,原生性能優化及混合優化,flutter專精);希望有技術的大佬加入,水圈內解決的問題越多獲得的權利越大!

一. Android相關

1. mvc mvp mvvm三種架構模式

  • mvc:業務邏輯、數據、界面分離的一種模式,簡單的來說,就是通過controller來操作model層的數據,並且返回給view顯示。 activity不是標準的controller,隨著界面邏輯交互的複雜度提升,activity類的職責不斷增加,變得臃腫。 view和model相互耦合,不利於開發。
  • mvp:主要是提出了presenter層,作為view和model之間溝通的橋樑。 程序邏輯放在presenter中處理,完全將view和model進行了分離,不允許他們之間溝通。
  • mvvm:主要是將presenter改為了viewmodel,和mvp類似,不同的是viewmodel跟view和model進行雙向綁定。 使用了data binding

二. Android系統結構層次

Android系統架構分為5層,從下到上依次為 Linux內核層,硬件抽象層,系統運行庫層(Native),應用框架層,應用層。

  • Linux內核層:Android的核心基於Linux內核,在此基礎上添加了Android的專用驅動(比如Binder)、系統的安全性、內存管理、進程管理等等。
  • 硬件抽象層(HAL):有了核心還不行,你得需要運行到相應的硬件上才能實現自己的價值吧。而硬件抽象層就是硬件和Linux內核之間的接口,目的就是將硬件抽象化,保護硬件廠商的知識產權(Linux是有開源協議的)
  • 系統運行庫層:怎麼操縱硬件,顯示圖像到屏幕?這一層就是幹這個的,它分為兩部分,分別是C++程序庫和Android運行時。 C++程序庫:被Android系統中的不同組件使用,可以通過應用框架層被開發者使用,下面是主要的程序庫: openGL ES 3D繪圖函數庫 Media Framework 多媒體庫 SQLite 關係型數據庫引擎 SSL 安全套接層 Android Runtime:ART虛擬機(5.0之後,Dalvik虛擬機被ART取代),ART在應用第一次安裝的時候,就會將字節碼預編譯成機器碼存儲到本地,這樣應用每次運行就無須執行編譯了(Dalvik是每次打開都要即時編譯),典型的以空間換時間 應用框架層:Framework層,這層代碼是用java編寫的,為開發人員提供了API。 應用層。

三. Activity活動的啟動模式及應用場景

  1. standard: 默認的模式,新建一個Activity就在棧中新建一個activity實例。
  2. singleTop:棧頂複用模式,與standard相比棧頂複用可以有效減少activity重複創建對資源的消耗 。 登錄頁面 ,wxpay等支付頁面
  3. singleTask:棧內單例模式,棧內只有一個activity實例,棧內已存activity實例,在其他activity中start這個activity,Android直接把這個實例上面其他activity實例踢出棧GC掉。主頁面 ,WebView頁面、掃一掃頁面 ,付款界面
  4. singleInstance:開闢一個新的棧存放activity。系統Launcher、鎖屏鍵、來電顯示等系統應用。

四. Android進程間通信的方式

1.)傳統的IPC通信方式

Android系統是基於Linux內核的,Linux提供了管道、消息隊列、共享內存和socket等IPC機制。那為什麼Android還要提供Binder來實現IPC呢?主要是基於性能、穩定性和安全性方面的考慮。

性能:socket作為通用接口,傳輸效率低,開銷大,主要用到跨網絡進程通信。消息隊列、共享內存和管道採用存儲-轉發模式,數據拷貝至少需要兩次,共享內存雖然無需拷貝,但是控制複雜,難以使用。而binder只需要拷貝一次,性能上只次於共享內存。

穩定性:Binder 基於 C/S 架構,客戶端(Client)有什麼需求就丟給服務端(Server)去完成,架構清晰、職責明確又相互獨立,自然穩定性更好。

安全性:Android 為每個安裝好的 APP 分配了自己的 UID,故而進程的 UID 是鑑別進程身份的重要標誌。傳統的 IPC 只能由用戶在數據包中填入 UID/PID,但這樣不可靠,容易被惡意程序利用。可靠的身份標識只有由 IPC 機制在內核中添加。其次傳統的 IPC 訪問接入點是開放的,只要知道這些接入點的程序都可以和對端建立連接,不管怎樣都無法阻止惡意程序通過猜測接收方地址獲得連接。

2.)傳統IPC通信原理

通常的做法是消息發送方將要發送的數據存放在內存緩存區中,通過系統調用進入內核態。然後內核程序在內核空間分配內存,開闢一塊內核緩存區,調用 copy_from_user() 函數將數據從用戶空間的內存緩存區拷貝到內核空間的內核緩存區中。同樣的,接收方進程在接收數據時在自己的用戶空間開闢一塊內存緩存區,然後內核程序調用 copy_to_user() 函數將數據從內核緩存區拷貝到接收進程的內存緩存區。這樣數據發送方進程和數據接收方進程就完成了一次數據傳輸,我們稱完成了一次進程間通信。

一次數據傳遞需要經歷:內存緩存區 --> 內核緩存區 --> 內存緩存區,需要 2 次數據拷貝

接收數據的緩存區由數據接收進程提供,但是接收進程並不知道需要多大的空間來存放將要傳遞過來的數據,因此只能開闢儘可能大的內存空間或者先調用 API 接收消息頭來獲取消息體的大小,這兩種做法不是浪費空間就是浪費時間。

3.)Binder IPC實現

Binder IPC 機制中涉及到的內存映射通過 mmap() 來實現,mmap() 是操作系統中一種內存映射的方法。內存映射簡單的講就是將用戶空間的一塊內存區域映射到內核空間。映射關係建立後,用戶對這塊內存區域的修改可以直接反應到內核空間;反之內核空間對這段區域的修改也能直接反應到用戶空間。

五. 多線程的實現方法(synchronized和lock的異同)

  1. 繼承Thread類創建線程
  2. 實現Runnable接口創建線程,推薦使用這種方式,可以複用runnable
  3. 實現Callbale接口,通過FutureTask包裝器來創建一個帶返回值的線程

synchronized

在用法上,它是java的關鍵字,一般我們不太需要關注他的鎖的釋放,代碼執行完畢或者報錯會自動釋放鎖,並且無法判斷鎖的狀態。

lock

是一個接口,我們使用ReentrantLock 比較多,有多個獲取鎖的方式,可以trylock直接返回獲取成功或者失敗,線程不用一直等待。在finally中必須要釋放該鎖。

六. 說一下View的事件分發機制

為什麼要有事件分發

注:引用水圈G神的博客

Android中的view是樹形結構的,view可能會重疊在一起,當我們點擊的地方有多個view的時候,這個時間該給誰,這就是為什麼要有事件分發。

先來看看view的樹形結構:

Android開發四年面試相關知識整理

上面多出來兩個東西是phonewindow和decorview,其中,主題顏色和標題欄內容等主要就是decorview來負責顯示的,那PhoneWindow是做什麼的呢?

PhoneWindow 繼承window,並且是window唯一的實現類,window是一個抽象類,是所有視圖的最頂層容器,視圖的外觀和行為都歸他管,不論是背景顯示,標題欄還是事件處理都是他管理的範疇,它其實就像是View界的太上皇。

`DecorView` 是 `PhoneWindow` 的一個內部類,其職位相當於小太監,就是跟在 `PhoneWindow` 身邊專業為 `PhoneWindow` 服務的,除了自己要幹活之外,也負責消息的傳遞,`PhoneWindow` 的指示通過 `DecorView` 傳遞給下面的 View,而下面 View 的信息也通過 `DecorView` 回傳給 `PhoneWindow`

事件分發、攔截、消費

類型 相關方法 Activity ViewGroup View 事件分發 dispatchTouchEvent √ √ √ 事件攔截 onInterceptTouchEvent X √ X 事件消費 onTouchEvent √ √ √

Activity作為原始的事件分發者,不需要攔截事件,如果需要這個事件不分發下去就行了。

同樣的,view在事件傳遞的最末端,也不需要攔截事件,不處理回傳回去就行了。

事件在收集之後最先傳遞給Activity,然後依次向下傳遞:

<code>Activity -> PhoneWindow -> DectorView -> ViewGroup -> ... -> view/<code>

如果沒有任何View消費掉事件,那麼這個事件會按照反方向回傳,最終傳回給Activity,如果最後 Activity 也沒有處理,本次事件才會被拋棄 :

<code>Activity /<code>

上面的模式是一個非常經典的責任鏈模式

七、說一下View從app啟動到顯示在界面上的繪製流程

注:參考鏈接

在activity的attach方法裡面,會創建一個PhoneWindow。

在onCreate中調用setContentView,setContentView是window的一個抽象方法,真正實現類是PhoneWindow:

<code> @Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {

//1.初始化
//創建DecorView對象和mContentParent對象 ,並將mContentParent關聯到DecorView上
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();//Activity轉場動畫相關
}

//2.填充Layout
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());

transitionTo(newScene);//Activity轉場動畫相關
} else {
//將Activity設置的佈局文件,加載到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
}

//讓DecorView的內容區域延伸到systemUi下方,防止在擴展時被覆蓋,達到全屏、沉浸等不同體驗效果。
mContentParent.requestApplyInsets();

//3. 通知Activity佈局改變
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {

//觸發Activity的onContentChanged方法
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}/<code>

核心方法就兩個:installDecor() 和 mLayoutInflater.inflate(layoutResID, mContentParent) ;

installDecor會創建一個DecorView 對象,該對象將作為整個應用窗口的根視圖。然後配置不同窗口修飾屬性(style theme等)。

mLayoutInflater.inflate就是解析xml,深度優先地遞歸解析xml,一層層添加到root view上,最終返回root view.解析的部分大致包含兩點:1.解析出View對象,2.解析View對應的Params,並設置給View。

八、知道什麼會引起ANR嗎 怎麼避免

有四種情況會造成ANR發生:

  1. 5秒內無法響應屏幕觸摸事件或鍵盤輸出
  2. 在執行前臺廣播的onReceive()函數時10秒沒有處理完成,後臺為20秒
  3. 前臺服務20秒內,後臺服務在200秒內沒有執行完成
  4. ContentProvider的publish在10s內沒進行完

如何避免:

儘量避免在主線程中做耗時操作。 多線程==>引出如何實現多線程,線程池的使用

如何分析ANR:

  1. 產生anr之後,會在data/anr/目錄下生成一個文件traces.txt
  2. Logcat中查看
  3. Java線程調用分析
  4. DDMS分析

九、有做過app的性能優化嗎

  1. app啟動加速:一個app的啟動分為三種不同的狀態,其中,我們只需要對第一種狀態做優化。對於App來說, 我們可以控制的啟動時間線無外乎Application的onCreate,首屏Activity的渲染。 冷啟動:App沒有啟動過或者App進程被kill,系統中不存在該App進程。此時App的啟動需要創建App進程,加載相關資源,啟動main thread,初始化首屏Activity等。 熱啟動:App進程只是處於後臺, 系統只是將其從後臺帶到前臺, 展示給用戶。屏幕會顯示一個空白的窗口(顏色基於主題), 直至activity渲染完畢。 溫啟動:介於冷啟動和熱啟動之間, 一般來說在以下兩種情況下發生, 用戶back退出了App, 然後又啟動. App進程可能還在運行, 但是activity需要重建 用戶退出App後, 系統可能由於內存原因將App殺死, 進程和activity都需要重啟, 但是可以在onCreate中將被動殺死鎖保存的狀態(saved instance state)恢復
  2. 佈局優化 減少不必要的嵌套 儘量不要嵌套使用RelativeLayout 儘量不要在嵌套的LinearLayout中都使用weight屬性 ConstraintLayout 善用TextView的Drawable減少佈局層級
  3. 響應優化
  4. 內存優化 bitmap的使用,options的jusdecodeBounds屬性,設置只解析bitmap的寬高等,然後使用insimplesize對bitmap進行壓縮。在android2.3的時代,bitmap的回收需要調用recycler方法,並且置空,但是之後只需要進行置空操作。 加載一張大圖:使用BitmapRegionDecode進行局部解碼, decodeRegion(Rect rect, BitmapFactory.Options options) 指定rect區域獲取圖像,options參數不支持inPurgeable,其他都支持 lru算法的實現=> LinkedHashMap
  5. 電池使用優化
  6. 網絡優化 參考:Android App優化, 要怎麼做?

十、瞭解過Android最新技術嗎 使用過嗎

flutter

一步手機的刷新率為60hz,當一幀圖像繪製完畢後準備繪製下一幀時,顯示器會發出一個垂直同步信號(如VSync), 60Hz的屏幕就會一秒內發出 60次這樣的信號。而這個信號主要是用於同步CPU、GPU和顯示器的。

一般地來說,計算機系統中,CPU、GPU和顯示器以一種特定的方式協作:CPU將計算好的顯示內容提交給 GPU,GPU渲染後放入幀緩衝區,然後視頻控制器按照同步信號從幀緩衝區取幀數據傳遞給顯示器顯示。

Android開發四年面試相關知識整理

進圈方式:點贊+關注,私信回覆我‘資料’即可進圈。


分享到:


相關文章: