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的生存率?
- 在onStartCommand()中使用startforegound,將service變成前臺程序(詳見android的集中程序等級)
- 在onStartCommand中return sart_sticky
- 註冊靜態BroadcastReceiver,監聽系統廣播,然後判斷Service狀態
- 守護程序
BroadcastReceiver:
- 普通廣播、
- 系統廣播:系統內建的一些廣播,比如監聽網路變化、開啟相機等等
- 有序廣播:
- 接收者按照預先宣告的優先順序依次接收Broadcast;
- 可以把資料存入結果物件中,傳遞給下一個接收者;
- 可以被一種某一個接收者終止。
- 本地廣播(APP內)
ContentProvider:
Android程序(等級)
foreground process 前端程序
前端程序就是目前顯示在螢幕上和使用者互動的程序
比如說:
- 頂層可互動的activity(已執行onResume);
- 有個Service,並繫結到跟使用者正在互動的activity;
- 在Service裡呼叫了startForground函式;
- 正在執行onReceive函式的BroadCastReceiver
visible process 可見程序
沒有任何前臺元件,但是仍然能影響使用者在螢幕上看到東西。
比如:- 如果一個activity在一個對話方塊執行之後仍然是可視的;
- 輸入法的彈出時。
Service process 服務程序
服務程序不會直接為使用者所見
比如在後臺播放mp3或者從網上下載東西background process 後臺程序
比如:Activity執行了onStopempty 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
載入
提高渲染的優先順序
webSettings.setRenderPriority(RenderPriority.HIGH);
把圖片載入放在最後來載入渲染
webSettings.setBlockNetworkImage(true);
使用硬體加速,該功能在Android 3.0 (API level 11)才加入。
硬體加速可以在一下四個級別開啟或關閉:Application、Activity、Window、View
比如,在AndroidManifest.xml中新增android:hardwareAccelerated屬性;關閉view的硬體加速myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);開啟快取
設定websetting
js和java物件互動
- 獲取webview控制元件的websetting
- 設定websetting.setJavascriptEnabled( true )
將一個物件暴露給JavaScript:webview.addJavascriptInterface。這個物件包含了JS呼叫的方法,這些方法用@JavascriptInterface修飾
JS通過這些方法與Android互動
防止OOM
- 在程式碼中動態地將webview設定到佈局中,而不是直接寫到xml檔案中;
- 在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)通訊為例
- 在Service中new一個Messenger(這個Messenger需要指定Handler)
- 然後在onBind函式中,返回messenger的Binder物件(messenger.getBinder())
- 在Activity中,通過bindService啟動service,通過ServiceConnection獲取到Binder物件。
- 通過這個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包,以及應用本身的所有方法。
解決方法數越界:
- 刪除無用的程式碼和第三方庫
- 採用外掛化機制,動態載入dex。這是一個重量級的方案
- multiDex方案——可以從apk中載入多個dex檔案
基本使用:
- 配置Gradle,新增 multiDexEnabled true
- 新增multiDex依賴
- 在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
基於觀察者模式,可以方便地以流的方式處理非同步事件
建立:
Observable.create/just/fromSchedulers執行緒排程
在不指定執行緒的情況下, RxJava 遵循的是執行緒不變的原則
subscribeOn():
指定 subscribe() 所發生的執行緒,即 Observable.OnSubscribe 被啟用時所處的執行緒。或者叫做事件產生的執行緒。
subscribeOn() 的位置放在哪裡都可以,但它是隻能呼叫一次的。
observeOn():
- Subscriber 所執行在的執行緒,或者叫做事件消費的執行緒。
- 指定的是它之後的操作所在的執行緒,以此實現執行緒的多次呼叫
- 舉例:
AndroidSchedulers.mainThread();
Schedulers.single();
Schedulers.newThread();
Schedulers.computation();
Schedulers.io();
變換 操作符
- map():將發射的每一項資料都用一個函式進行變換
- flapMap():
背壓
在非同步場景下,被觀察者的傳送速度遠遠大於觀察者的處理速度。
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開發流程:
- 在Java中編寫native方法
- 將Java編譯成class檔案,然後匯出JNI的.h標頭檔案。
- 用C或者C++實現java中宣告的JNI方法
- 編譯.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的時候,上報上一次的資訊。