1. 程式人生 > >Android面試知識點彙總

Android面試知識點彙總

四大元件:

Activity:

  • 生命週期:
  • 啟動模式:
    • standard、singleTop、singleTask、singleInstance
    • 任務棧:前臺任務棧、後臺任務棧
    • TaskAffinity + singleTask
    • 使用adb檢視任務棧資訊
  • 啟動方式:
    • 顯式:intent.setClass()
    • 隱式:設定過濾資訊:action、category、data類別,且同時匹配上述三類
  • 四種狀態:
    • Active/Runing: 它處於可見並可和使用者互動的啟用狀態。
    • Paused: 仍然可見,但它已經失去了焦點故不可與使用者互動。比如當 Activity 被另一個透明或者 Dialog 樣式的 Activity 覆蓋時的狀態。
    • Stoped: 當 Activity 被另外一個 Activity 覆蓋、失去焦點並不可見時處於 Stoped 狀態。
    • Killed: Activity被系統殺死回收或者沒有被啟動時處於 Killed 狀態。

Service:

  • 生命週期:

  • 如何提高service的生存率

    1. 在onStartCommand()中使用startforegound,將service變成前臺程序(詳見android的集中程序等級)
    2. 在onStartCommand中return sart_sticky
    3. 註冊靜態BroadcastReceiver,監聽系統廣播,然後判斷Service狀態
    4. 守護程序

BroadcastReceiver:

  • 普通廣播、
  • 系統廣播:系統內建的一些廣播,比如監聽網路變化、開啟相機等等
  • 有序廣播:
    • 接收者按照預先宣告的優先順序依次接收Broadcast;
    • 可以把資料存入結果物件中,傳遞給下一個接收者;
    • 可以被一種某一個接收者終止。
  • 本地廣播(APP內)

ContentProvider:


Android程序(等級)

  1. foreground process 前端程序

    前端程序就是目前顯示在螢幕上和使用者互動的程序

    比如說:

    1. 頂層可互動的activity(已執行onResume);
    2. 有個Service,並繫結到跟使用者正在互動的activity;
    3. 在Service裡呼叫了startForground函式;
    4. 正在執行onReceive函式的BroadCastReceiver
  2. visible process 可見程序

    沒有任何前臺元件,但是仍然能影響使用者在螢幕上看到東西。
    比如:

    • 如果一個activity在一個對話方塊執行之後仍然是可視的;
    • 輸入法的彈出時。
  3. Service process 服務程序
    服務程序不會直接為使用者所見
    比如在後臺播放mp3或者從網上下載東西

  4. background process 後臺程序
    比如:Activity執行了onStop

  5. empty process 空程序


資料持久化

SQLight:

- SQLite是一個輕量級的資料庫,支援基本的SQL語法
- SQLiteDatabase的類,封裝了一些操作資料庫的api
    1. context.openOrCreateDatabase()方法建立SQLiteDatabase例項
    2. SQLiteDatabase例項呼叫insert()方法插入資料
    3. 呼叫query()方法查詢資料
    4. 呼叫execSQL()方法執行SQL語句

SharedPreference:

- 是一種輕量級的資料儲存方式,採用簡直對的方式來儲存資料。
- 其本質就是一個xml檔案,一般位於/data/data/包名/shared_prefs/目錄下。
- 由於記憶體中存在sharedPreference檔案的快取,所以在多程序的環境下,系統對它的讀寫不可靠。因此不建議用在IPC中

ContentProvider:

- Android系統中能實現不同應用間共享的一種資料儲存方式。例如音訊,視訊,圖片和通訊錄,一般都可以採用此種方式進行儲存
- 每個Content Provider都會對外提供一個公共的URI,應用程式通過這個URI來對資料進行操作。
- Content Provider天生支援跨程序訪問,因此可以用於IPC

Android應用程式之間是通過哪些方式共享資料的?

File,Sqlite,Content Provider,BroadCast Receiver,Intent,同個Application內部的話還可以通過靜態變數共享資料。


webView

載入

  1. 提高渲染的優先順序

    webSettings.setRenderPriority(RenderPriority.HIGH);

  2. 把圖片載入放在最後來載入渲染

    webSettings.setBlockNetworkImage(true);

  3. 使用硬體加速,該功能在Android 3.0 (API level 11)才加入。
    硬體加速可以在一下四個級別開啟或關閉:Application、Activity、Window、View
    比如,在AndroidManifest.xml中新增android:hardwareAccelerated屬性;關閉view的硬體加速myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

  4. 開啟快取
    設定websetting

js和java物件互動

  1. 獲取webview控制元件的websetting
  2. 設定websetting.setJavascriptEnabled( true )
  3. 將一個物件暴露給JavaScript:webview.addJavascriptInterface。這個物件包含了JS呼叫的方法,這些方法用@JavascriptInterface修飾

  4. JS通過這些方法與Android互動

防止OOM

  1. 在程式碼中動態地將webview設定到佈局中,而不是直接寫到xml檔案中;
  2. 在Activity的onDestory中銷燬webview

執行緒相關

Linux執行緒基礎

  • 執行緒與程序的區別
  • 執行緒同步
  • Linux執行緒通訊方式

ANR

- what
    - Activity 5s內無響應,BroadcastReceiver 10s內無響應
    - /data/anr/traces.txt 檔案記錄了ANR的資訊
- why
- how

耗時任務或者執行緒間通訊

  • AsyncTask
    • 本質上是對 ThreadPool 和 Handler 的一個封裝
    • 預設是序列的執行任務,可以呼叫executeOnExecutors()方法並行執行任務
  • Handler
  • IntentService

Handler

  • Handler + MessageQueue + Looper

MessageQueue本質上是一個單鏈表,不是Queue。採用FIFO方式管理,enqueueMessage()方法是將訊息插入一條佇列,next()方法是一個無限迴圈的方法。如果有訊息,則取出,如果沒有,就阻塞

  • HandlerThread

本質上是一個繼承了Thread的執行緒類
通過建立HandlerThread**獲取looper物件,傳遞給Handler物件,執行非同步任務。在HandlerThread中通過**Looper.prepare()來建立訊息佇列,並通過Looper.loop()來開啟訊息迴圈。建立HandlerThread後必須先呼叫start()方法,才能呼叫getLooper()獲取Looper物件。

HandlerThread封裝了Looper物件,使我們不用關心Looper的開啟和釋放的細節問題。如果不用HandlerThread的話,需要手動去呼叫Looper.prepare()和Looper.loop()這些方法。

IntentService

  • 原理:IntentService是一個抽象類,封裝了HandlerThread和Handler,負責處理耗時的任務。任務執行完畢後會自行停止。在onCreate()方法中開啟了一個HandlerThread執行緒,之後通過HandlerThread的Looper初始化了一個Handler,負責處理耗時操作。通過startService()方法啟動,在handler中呼叫抽象方法onHandleIntent(),該方法執行完成後自動呼叫stopself()方法停止

  • override onHandleIntent() 方法

  • 優點:一方面不需要自己去建立執行緒,另一方面不需要考慮在什麼時候關閉該Service

OOM

  • what
    OOM 和 記憶體洩漏 的區別

  • how

    • 靜態變數持有Activity或Context物件
    • 非靜態內部類的例項(預設持有外部類的引用)
    • 資源未關閉:file、stream、bitmap等
  • Handler造成OOM

    • 原因:使用(匿名)內部類例項化handler,預設持有context引用
    • 避免:靜態內部類、Activity在onDestroy的時候,清空handler未處理的訊息
  • WebView造成OOM


View相關

三部曲

三個核心步驟:Measure、Layout、Draw

Touch分發機制

重要

滑動衝突

簡述Activity、Window、WindowManager、View、ViewRootImpl的作用和相互之間的關係

  • Activity不負責檢視的控制,而是交給Window。這個Window本質上是一個PhoneWindow,被windowmanager管理。

  • Window中有decorview,decorview是當前檢視的底層View,是setContentView所設定View的父View
    View是所有控制元件的基類。

  • ViewRoot對應ViewRootImpl,它是連線WindowManager和DecorView的紐帶。繪製的三大流程都是在ViewRootImpl中完成的:從ViewRootImpl中的performTraversals開始,有三個方法performMeasure, performLayout, prformDraw分別對應measure,layout,draw三個流程,完成對頂級View的繪製。

  • 在父View的Measure過程中,會呼叫子View的Measure過程,如此反覆,完成對整個View樹的遍歷。同理,在Layout和Draw中也是如此。

RecyclerView

  • 優點:

    • 封裝了ViewHolder
    • 與ListView相比,耦合性更低、更加靈活:根據viewType設定不同的佈局
    • 設定LayoutManager,實現ListView的功能和GridView的功能(支援 LinearLayoutManager 和 GridLayoutManager)
    • 支援區域性重新整理:notifyItemChanged()方法 (Listview用的BaseAdapter只有notifyDataSetChanged()方法)
  • 缺點:

    • 使用更加複雜
    • 沒有onItemClickListener()、setOnItemLongClickListener()方法,只有OnItemTouchListener()方法
  • RecyclerView.Adapter

    • onCreateViewHolder()方法:產生一個ViewHolder物件,該物件中封裝了view
    • onBindViewHolder()方法:根據傳入的ViewHolder物件,顯示資料
    • getItemViewType()方法:根據情況,返回不同的viewType,方便後續顯示不同的佈局和業務處理

IPC

Linux中IPC的方式:命名管道、訊號量、共享記憶體

基礎

  • 開啟多程序的方式:給四大元件在Menifest檔案中,新增process屬性,指定程序名稱
  • Android為每個程序分配一個獨立的虛擬機器,有不同的Application和地址空間
  • 不同程序訪問同一個類的物件會有不同的副本。因此靜態成員和單例模式失效、執行緒同步失效、sharedPreference可靠性降低。

序列化

  • Serializable介面:Java的序列化介面,使用簡單,但開銷大,序列化和反序列化需要大量IO操作

  • Parcelable介面:是Android的序列化方式,使用複雜,但效率高。

  • 物件是不能直接跨程序傳輸的。物件的跨程序傳輸,其本質是序列化和反序列化的過程

機制:Bundle、檔案共享、ContentProvider、Socket、AIDL、Messager

  • 四大元件間,把資料封裝到Bundle。在一個程序中開啟另一個程序的Activity或者Service,就可以通過Intent把Bundle傳遞過去。其中,封裝在Bundle中的資料需要能夠被序列化

  • 使用檔案共享方式,多程序讀寫一個相同的檔案,獲取檔案內容進行互動。

  • 使用ContentProvider,常用於多程序共享資料,比如系統的相簿,音樂等,我們也可以通過ContentProvider訪問到

  • 使用Socket傳輸資料。服務端(比如一個程序中運行了一個Service)建立一個ServerSocket物件,監聽本地的埠;客戶端(比如另一個程序中執行的Activity)通過Socket連線本地的那個介面。經過TCP的三次握手後,建立連線。接著可以傳送資料。使用socket不僅可以實現程序間通訊,也可以實現裝置間通訊。

Binder

  • 基本原理:
    Android特有的IPC、客戶端-伺服器C/S的模式、

四個角色:Client、Server、ServiceManager、BinderDriver

呼叫過程:
1. Server向ServiceManager註冊
2. Client通過ServiceManager獲取Server的代理物件
3. Client向代理物件發起請求,該請求通過BinderDriver傳送給Server處理
4. Server通過BinderDriver返回處理結果

  • 注意:客戶端呼叫服務端的方法,被呼叫的方法執行在服務端的Binder執行緒池中,此時客戶端被掛起。因此此時需要避免ANR。(AIDL和Messager同理)

  • Binder連線池

在一個應用有多個使用AIDL的場景,無需為每一個AIDL建立自己的Service。而是使用一個Service,建立並返回一個Binder連線池的Binder物件。Activity在使用AIDL的時候,可以通過該Binder連線池物件,獲取不同的Binder物件(類似於工廠模式)

AIDL

  • 使用流程:以Activity(程序1)和Service(程序2)通訊為例
    • 建立AIDL介面,Build一下,產生相關程式碼
    • 建立IBinder例項,即例項化xxx.Stub()抽象內部類,override抽象方法
    • 建立Service,在onBind()中,把上述IBinder例項返回
    • 在Activity中呼叫bindService啟動Service,然後在ServiceConnection中的onServiceConnected方法回撥中獲得該IBinder例項。
    • Activity呼叫該例項的方法,實現通訊

Messager

  • 一種輕量級的跨程序通訊方案,底層使用AIDL實現。

  • 是一種序列的通訊,即服務端需要一個一個處理訊息。因此,在大量併發請求的情況下,用Messager就不太合適。

  • 使用流程:以Activity(程序1)和Service(程序2)通訊為例

    1. 在Service中new一個Messenger(這個Messenger需要指定Handler)
    2. 然後在onBind函式中,返回messenger的Binder物件(messenger.getBinder())
    3. 在Activity中,通過bindService啟動service,通過ServiceConnection獲取到Binder物件。
    4. 通過這個Binder物件例項化一個Messenger,然後messenger.send(message)進行通訊

啟動流程

Android開機流程

init程序-zygote程序-SystemServer程序-各種ManagerService(AMS,PMS,WMS)- launcher程式

App啟動流程

launcher-AMS-(pause)-zygote-新程序ActivityThread-(main函式)-向AMS註冊-通知ActivityThread建立Activity並執行生命週期

App內Activity啟動流程

Activity1-AMS-(pause)-在同一個ActivityThread-載入Activity2類,執行生命週期

ActivityManagerService 和 Instrument 的區別


效能及優化

apk包大小

1、減少不必要的jar包依賴
2、優先使用程式碼來設定UI效果
3、去除沒用到的資原始檔,壓縮其他資原始檔的大小,不用適配所有尺寸的裝置
4、儘量重用程式碼,避免程式碼的冗餘
5、限制app支援的cpu架構的數目:在當前的Android 生態系統中,讓你的app支援 armabi 和 x86 架構就夠了;

方法數越界 multiDex方案

  • what:dex是Android平臺上(Dalvik虛擬機器)的可執行檔案, 相當於Windows平臺中的exe檔案, 每個Apk安裝包中都有dex檔案。

  • 單個dex檔案所包含的最大方法數是65536,包含Android Framwork、依賴的jar包,以及應用本身的所有方法。

  • 解決方法數越界:

    1. 刪除無用的程式碼和第三方庫
    2. 採用外掛化機制,動態載入dex。這是一個重量級的方案
    3. multiDex方案——可以從apk中載入多個dex檔案
  • 基本使用:

    1. 配置Gradle,新增 multiDexEnabled true
    2. 新增multiDex依賴
    3. 在Application中新增 MultiDex.install(this) 程式碼

其他

目標:

- 快:流暢
- 穩:穩定
- 省:省電、省流量
- 小:安裝包小

優化方案:

  • 佈局優化:

    • 減少View樹的層數
    • 合理使用優先使用FrameLayout和LinerLayout,減少使用RelativeLayout
    • 佈局複用,使用標籤
  • OOM優化

  • ANR優化

  • ListView(GridView)優化

    • 使用viewholder,進行view複用
    • 不要在getview()中進行耗時操作
  • Bitmap優化

    • 圖片壓縮
    • 快取(核心):記憶體快取和磁碟快取、LRU演算法

架構:本質上都是一種程式碼架構思想

MVC

其中M層處理資料,業務邏輯等;V層處理介面的顯示結果;C層起到橋樑的作用,來控制V層和M層通訊
檢視層(View):一般採用XML檔案進行介面的描述,這些XML可以理解為AndroidApp的View。
控制層(Controller):Android中由Activit、Fragment承擔,負責邏輯處理
模型層(Model):提供資料,從進行資料庫或者網路的操作。

缺點:在Android開發中,Activity並不是一個標準的MVC模式中的Controller,它的首要職責是載入應用的佈局和初始化使用者介面,接受並處理來自使用者的操作請求,進而作出響應,既是view層,又是controller層。隨著介面及其邏輯的複雜度不斷提升,Activity類的職責不斷增加,以致變得龐大臃腫。

MVP

  • MVP框架由3部分組成:

    • Model:提供資料,從進行資料庫或者網路的操作
    • View:對應於Activity/Fragment等View,主要負責UI顯示
    • Presenter:是Model和View之間的橋樑,進行邏輯處理。View並不能直接對Model進行操作
  • 優點:將在Activty中的大量邏輯操作放到Presenter控制層中,避免Activity的臃腫。

  • 缺點:MVP模式需要多寫許多新的介面;過於複雜的邏輯會使得Presenter臃腫

  • 實現方法:

    • 定義IView介面,Activity實現IView介面,然後在方法中更新UI;
    • 在Presenter中維持IView的一個引用;
    • 在Activity中例項化Presenter,然後將IView的例項(即this)賦值給Presenter。
    • 在Model中做具體的操作,Presenter獲取具體的結果,通過呼叫所因為的View的方法,更新UI。

MVVM

  • Model,View和ViewModel
    • Model:提供資料,從進行資料庫或者網路的操作
    • View:應於Activity/Fragment等View,主要負責UI顯示;
    • ViewModel是負責邏輯處理;Model提供資料。ViewModel和View之間通過繫結,使得耦合度進一步降低

AAC(Android Architecture Components,架構元件)

  • LiveData:

    • 使用觀察者模式,可以與控制元件繫結,監聽資料的改變重新整理UI。
    • 可以感知控制元件的生命週期,在控制元件銷燬時自動取消註冊,因此也不會產生記憶體洩漏
  • ViewModel:將檢視的資料和邏輯從具有生命週期特性的實體(如 Activity 和 Fragment)中剝離開來。比如 AndroidViewModel(ViewModle的子類)

  • Room:官方資料庫框架,對原生的SQLite API進行了一層封裝。

    • 與SQLite相比:對於複雜的資料庫結構,SQL使用複雜,程式碼冗長、管理困難;Room,使用簡單、易於管理

MVVM和AAC

個人理解:MVVM是一種思想,AAC提供多種工具。利用AAC中的工具實現MVVM的思想

View:

ViewModel:

Model:

  • 橘黃色框的Repository及其下都是Model層。一個Repository資料倉庫負責通過不同方式獲取同類型的資料。
  • 資料來源有:
    • 本地儲存資料,如資料庫,檔案,SharedPreferences(本質也是檔案)
    • 記憶體的快取或臨時資料
    • 通過各種網路協議獲取的遠端資料
  • ViewModel在從Repository獲取資料時,不需關注資料具體是怎麼來的。

這裡寫圖片描述


響應式程式設計

RxJava/RxAndroid

基於觀察者模式,可以方便地以流的方式處理非同步事件

  1. 建立:
    Observable.create/just/from

  2. Schedulers執行緒排程

    • 在不指定執行緒的情況下, RxJava 遵循的是執行緒不變的原則

    • subscribeOn():

      • 指定 subscribe() 所發生的執行緒,即 Observable.OnSubscribe 被啟用時所處的執行緒。或者叫做事件產生的執行緒。

      • subscribeOn() 的位置放在哪裡都可以,但它是隻能呼叫一次的。

    • observeOn():

      • Subscriber 所執行在的執行緒,或者叫做事件消費的執行緒。
      • 指定的是它之後的操作所在的執行緒,以此實現執行緒的多次呼叫
    • 舉例:
      AndroidSchedulers.mainThread();
      Schedulers.single();
      Schedulers.newThread();
      Schedulers.computation();
      Schedulers.io();
  3. 變換 操作符

    1. map():將發射的每一項資料都用一個函式進行變換
    2. flapMap():
  4. 背壓

在非同步場景下,被觀察者的傳送速度遠遠大於觀察者的處理速度。

  • Observable不支援背壓

  • Flowable支援背壓:
    - 背壓策略:MISSING、ERROR、BUFFER、DROP、LATEST

Retrofit

是一個網路請求框架,底層依賴OkHttp

  • 首先,Retrofit將Http請求封裝為Java介面;
  • 然後,網路請求交給OkHttp處理;
  • 最後,OkHttp將返回的結果交給Retrofit解析

支援兩種模式:callback、RxJava/RxAndroid


其他

JIN

由於Java的跨平臺的特性,導致Java同本地互動的能力不夠強大,一些和作業系統相關的操作無法完成。通過JNI可以呼叫C和C++的程式碼,提高自己的本地互動能力

JNI開發流程:

  1. 在Java中編寫native方法
  2. 將Java編譯成class檔案,然後匯出JNI的.h標頭檔案。
  3. 用C或者C++實現java中宣告的JNI方法
  4. 編譯.so庫檔案,然後在java中呼叫

設計一個圖片載入類

  • 圖片同步、非同步載入方式
  • 圖片壓縮(使用BitmapFactory) —— 降低OOM的風險
  • 快取 —— 記憶體快取LruCache、磁碟快取DiskLruCache。兩級快取降低了網路訪問次數,減少了流量消耗
  • 網路拉取 —— 從網路載入圖片。
  • View複用
  • 呼叫過程:從記憶體快取中讀取 —— 從磁碟快取中讀取 —— 從網路中拉取

畫出一個專案中網路請求的流程圖

第三方庫原始碼解析:

OkHttp

說說XML、JSON、GSON有什麼樣的聯絡

XML全稱叫做可擴充套件標記語言,它的結構相對簡單,資料共享比較方便。但是對於一些比較複雜的資料,XML檔案格式複雜,解析的代價大。
JSON的資料格式比較簡單,易於讀寫。但是目前還沒有XML應用廣泛。
GSON的Google的一個開源庫。這個開源庫可以很方便地將JSON陣列轉換為物件,這在開發中簡化了將JSON的欄位轉換為屬性的步驟。

Android訪問許可權

apk下載到cache目錄,只有 rw 許可權,沒有 x 許可權,所以無法安裝

Runtime.getRuntime().exec()
執行 chmod 命令,修改為rwx


APM

Android效能資訊

  • CPU資訊:/proc/cpuinfo
  • Memory 資訊:/proc/meminfo

異常捕獲:

UncaughtExceptionHandler:
android全域性異常捕獲器——在Application中呼叫**Thread.setDefaultUncaughtExceptionHandler
(uncaughtExceptionHandler)**,定製自己的錯誤日誌系統。比如在發生Exception時,記錄exception的堆疊資訊。

監控Activity生命週期

在application中,呼叫registerActivityLifecycleCallbacks(),通過實現了ActivityLifecycleCallbacks介面的例項,完成對Activity各個生命週期資訊的採集。比如哪個Activity在什麼時間處於onCreate、onStop等等,進而統計出Activity的使用時間和使用次數。

對於Fragment的資訊採集

SDK提供不同介面,分別對應Fragment的各個生命週期,進而採集資訊。使用時,需要使用者在Fragment的生命週期中的各個環節中,呼叫對應的介面。
會不會使用麻煩?會的,解決方法就是:在SDK中封裝一個Fragment的子類,在這個子類中按照上述方法採集資訊。使用者在使用SDK過程中,可以直接繼承使用這個子類,而不是繼承使用Fragment。

對HTTP介面的監測

採用插裝的辦法:SDK提供兩個介面,使用者在發起HTTP請求時,呼叫第一個介面,可以記錄下url和時間。在結束HTTP時,呼叫第二個介面,記錄下url、時間和返回碼。這兩組記錄就完成了對HTTP介面的資料採集?
會不會麻煩?會的,但是這種方法很通用,其他商用的SDK中也是這種方法。或者使用Gradle外掛,在編譯的時候,將上述程式碼自動插入專案中。

錯誤資訊上報機制:

方案一:發現錯誤後立即上報。優點:實時性好;缺點:不能保證每一條資訊都能上報成功。
方案二:發生錯誤後,記錄在本地,當第二次啟動App的時候,上報上一次的資訊。

介面加密、token原理