1. Android中為什麼主執行緒不會因為Looper.loop()裡的死迴圈卡死?
Android 應用程式在主執行緒進入訊息迴圈之前,也就是ActivityThread中的main函式中呼叫Looper.loop()之前,內部底層的linux會先建立一個管道,這個管道的作用使得Android應用程式的主執行緒在訊息佇列為空的情況下,可以進入等待空閒的狀態,當訊息佇列中有新的訊息時,再喚醒應用程式的主線執行緒。 在Lopper.loop()中通過一個死迴圈來處理分發訊息,本身是不會卡死的,正在會引起卡死的是在處理訊息的回撥中,如 onCreate(),onStart()...中。
2. 執行緒與程式
程式: app啟動時,是通過系統的zygote程式fork了一個程式,每個app執行在單獨的一個程式中,除非在AndroidManifest.xml中配置Android:process屬性,或通過native程式碼fork程式。程式直接的資源是不共享的。
前臺程式>科技程式>服務程式>後臺程式>空程式
**執行緒** 比如每次通過new Thread().start會建立一個新的執行緒,該執行緒與所在程式之間的資源是共享的。從linux角度來說執行緒與程式之間沒有本質的區別。
3. binder
binder通常用於不同程式間的通訊,hander通常用於同一程式的不同執行緒間的通訊
Android 中app程式間是隔離的,資料不共享。
一個程式空間包含使用者空間,和核心空間,使用者空間在程式間無法共享資料,核心空間可以共享資料。
android 中的binder機制是一種跨程式的ipc通訊,採用client,server模式。
Binder將client程式,service程式,serviceManager連線起來,實現程式間的通訊。在核心空間中建立一個資料快取區域,將client程式與service程式,以及核心空間建立對映關係。 開始由server端註冊服務到Binder驅動,再由clienth獲取服務,呼叫方法,傳遞資料到binder中,通過servicemanger建立client到server的連線關係,最後有server執行,將結果再返回到client.
程式間通訊 AIDL
server:
- 新建aidl檔案,定義要通訊的方法,編譯工程
- 自定義service繼承service,
- 自定義binder繼承stub 實現AIDL介面方法,並實現生命週期方法
- 註冊service,暴露服務
client
- 將server端aidl檔案複製過來
- 使用Stub.asInterface介面獲取伺服器的Binder;
- 建立service連線,獲取到AIDL中的介面
- 呼叫方法,返回結果
4. Handler
Handler 通常是處理不同執行緒間的通訊問題
- Looper 能夠保持執行緒持續存活並不斷的從任務佇列中獲取任務並執行
- MessageQuenue 使用intent ,message ,runnable做為任務的載體在不同的執行緒中進行傳遞
- Handler 實現任務佇列的管理
我們可以通過Handler傳送一個Message物件,或者Runnable物件,到MessageQuenue,然後通過Lopper.loop(),用Handler.dispatchMessage()迴圈傳送訊息到指定的handleMessage()回撥,或者Callback回撥。
傳送message通常可以new Message(),或者Message.obtain()來獲取Message,通常使用後者,複用Message,避免建立更多的Message物件。
Handler在初始化時,變建立了自己的MessageQuenue物件,並和當前的Thread繫結,一個handler只能繫結一個Thread,且一個Thread只能有一個Lopper物件。
可以通過Looper.prepare()來建立一個Lopper.UI執行緒中不需要,在UI執行緒初始化時,會自動建立,可以通過getMainLopper()來獲取主執行緒中的Lopper.
通常用Handler來處理訊息,並更新UI操作。傳送一些延時訊息等。
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }
*/
複製程式碼
5.如何避免多執行緒產生的問題
使用場景
- Android中在初始化Application,或初始化介面時,可以採用多執行緒來提高應用程式的併發執行效能,如需要初始化一些第三方的sdk,或資料庫操作,等。
- 採用多執行緒將一些複雜任務耗時操作放到子執行緒中去執行,執行完成後更新介面UI,提高介面的流暢性
- 大檔案下載時可以採用多執行緒
- 批量處理資料,如壓縮100張圖片,可以建立多執行緒來處理,提高並行效率
使用多執行緒產生的問題
- 會觸發記憶體的消耗,使得記憶體消耗過多
- 可能會產生死鎖的想象
- 可能會出現記憶體洩漏
怎麼來使用多執行緒
- AsyncTask 為UI執行緒和工作執行緒提供快速的切換操作,處理一些生命週期短的操作。
- HandlerThread 為某些回撥方法或者耗時任務的執行設定單獨的執行緒任務,並提供執行緒任務的排程機制
- ThreadPool 多個執行單元同時執行,處理一些併發操作
- IntentService 通過UI執行緒出發一些後臺執行的任務操作,並通過一定的機制將結果返回到UI執行緒。
- Rxjava 處理執行緒的排程
AsyncTask為什麼不用他?
在Activity內部建立一個AsyncTask,這是一個內部類,會持有外部物件activity的引用,若activity銷燬時,asyncTask仍然執行,就會導致Activity無法完全釋放,導致記憶體洩漏。所以不建議使用AsyncTask.
HandlerThread
- 適合處理那些在子執行緒中執行,需要消耗時間的任務,在任務結束後,或者隔一段時間通知UI執行緒更新。
- 內部自動建立了一個looper,並維持任務佇列。不會阻礙UI執行緒
- 每個任務會以佇列的方式挨個執行,若其中的一個耗費較長時間,將會阻塞其它任務的執行。
- 使用時要對不同的thread設定優先順序呼叫Process.XXX數值越小優先順序越高。cpu根據不同優先順序對執行緒排程時會調優,優先處理一些前臺執行緒。
- 在onLooperPrepared中做一些初始化操作
ThreadPool
執行緒池適合將任務進行分解,在多個操作單元中執行,併發執行的場景。注意記憶體的使用開銷。
IntentService
預設的service 是在主執行緒中執行的,會因為搶佔主執行緒的資源,影響介面的繪製。IntentService 繼承自普通的service,並在內部建立了一個HandlerThread 在onHandlerIntent中處理扔到service中的任務,所以IntentService不僅僅具備了非同步執行緒的特性,還保留的service不受主頁面生命週期的影響。不容易被後臺殺死,介於前臺執行緒與後臺執行緒之間。
可以在IntentService間隔性的處理一些定時任務,如重新整理資料,更新快取等。內部維持了任務佇列,所以某一個任務耗時將會影響到其它任務的執行。
6 UI卡頓分析
Android 中的UI繪製是在主執行緒中執行的,且大部分的任務操作都是主線中完成的,一旦我們在主執行緒出處理了一些耗時操作,可能就會阻礙UI執行緒的觸控事件,滑動事件,或者介面繪製。為了使介面的重新整理幀率達到60FPS,我們要確保16ms內完成單次重新整理操作,一旦主執行緒中的任務過於繁重就會阻礙主執行緒介面的繪製操作,當接收到重新整理介面的訊號時,因為資源被佔用,就無法完成介面重新整理操作,這樣就會出現掉幀的現象,隨之會影響幀率的下降就造成了介面卡頓。
原因?
- 介面佈局過於複雜,巢狀層數太多
- UI執行緒中操作耗時任務,導致介面卡頓
- 記憶體抖動,大量的物件被瞬間建立與釋放會引起介面卡頓
- 記憶體不足,頻繁GC都會引起介面卡頓
- 記憶體洩漏
如何解決?
- 採用多執行緒將一些複雜任務耗時操作放到子執行緒中去執行,執行完成後更新介面UI,提高介面的流暢性
- 檢視佈局減少巢狀層數,比如使用ConstraintLayout, 替換relativeLayout LinearLayout的巢狀,還有合理使用include ,viewStub,merge標籤等。
- 合理配置檢視的背景色,重複設定父檢視,子檢視的背景色,會增加介面繪製的次數
- 圖片展示 合理的壓縮質量 大小來展示
- 避免記憶體洩漏引起的記憶體不足
檢測工具
- 手機上可以通過開啟開發者選項中的顯示介面佈局,檢視重繪程度
- 手機上開啟Gpu的過渡渲染來檢視檢視更新過程中花費的時間
- android 開啟除錯工具欄的 android profile 檢視memory 記憶體使用量,排查佔用記憶體的操作
7.Executor執行緒池有哪幾種,說一下區別,說說執行緒池特性是由什麼決定的?
執行緒池的優點:
- 重用執行緒池中的執行緒物件,避免重複建立執行緒物件帶來的開銷。
- 有效的控制執行緒的併發問題,提高系統資源利用率,避免資源競爭,避免擁堵。
- 對多執行緒的管理比較簡單,易用
- Executors.newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
複製程式碼
建立一個可快取的執行緒池,但有任務執行時,若有執行緒可用,則複用執行緒池,若沒有則新建一個執行緒,超過60s,沒用的執行緒將會被回收。
- Executors.newFixedThreadPool(int);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
複製程式碼
建立一個定長的執行緒池,可控制最大併發數,超出的執行緒會在等待佇列等待。
- Executors.newSingleThreadPool();
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
複製程式碼
建立一個單執行緒化的執行緒池,它只會用單一的執行緒來處理工作佇列,保證所有的工作佇列,按照指定的順序執行。
- Executors.newScheduledThreadPool();
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
複製程式碼
建立一個定長執行緒池,支援定時和週期性的任務。。
8.app啟動優化
- Application onCreate方法中儘量不要處理耗時操作
- Application onCreate方法中經常會有一些第三方平臺的初始化操作,會耗時,可以建立一個IntentService 在後臺的子執行緒中來做初始化操作。
- 首屏展示優化,可以設定定義的主題顏色,展示不同的顏色,或者不同的圖片 windowBackgroud 設定color 或者 drawable,避免白屏黑屏
- 可以建立一個不載入檢視的activity做為啟動屏
9. 開源框架如何選型?
- 分析業務應用的各個場景,看看滿足度如何,是否能滿足需求
- 分析效能滿足度如何?穩定性如何
- 是否易於測試
- 整合的難以程度如何,程式碼侵入性要儘量的小,儘量解耦
- 開源協議
- api 開發文件是否齊全
- 擴充新功能的能力如何,是否可以定製
- 版本升級,維護更新如何
10. BlockingQueue
BlockingQueue 是一個先進先出的阻塞佇列,為啥說是阻塞?是因為BlockingQueue支援獲取當前佇列中的元素,但佇列為空的時候,會阻塞,等待佇列中有元素時再返回;新增元素時,如果佇列已滿,那麼等到佇列可以放入新的元素時再放入。
- 不接受null值的插入
- 使用來設計實現生產者-消費者佇列的。
- 實現是執行緒安全的
- 不支援close shutdown 等關閉操作
- 是一個簡單的執行緒安全容器
ArrayBlockingQueue 是BlockingQueue介面的有界佇列實現類,底層採用陣列來實現。其併發控制採用可重入鎖來控制,不管是插入還是讀取操作,都需要獲取到鎖才能進行操作。
- 設定佇列容量,在構造時傳入元素個數
- 指定獨佔鎖是公平鎖還是非公平鎖,非公平鎖的吞吐量高,公平鎖保證每次都是等待最久的執行緒獲取到鎖。
- 可以指定一個集合來初始化,在構造方法期間加入到佇列。
LinkedBlockingQueue 底層基於單向連結串列實現的阻塞佇列,可以當做無界佇列或有界佇列來使用,看構造方法。 無界佇列 傳 Interger.MAX_VALUE。
SynchronousQueue 同步佇列,當一個執行緒往佇列中寫入一個元素時,寫入操作不會立即返回,需要等待另一個執行緒來將這個元素拿走。同理當一個執行緒做讀操作的時候,同樣需要一個相匹配的寫執行緒的寫操作。讀執行緒和寫執行緒要同步,一個讀執行緒匹配一個寫執行緒。
- 不提供任何空間來儲存元素,資料必須從某個寫執行緒交給某個讀執行緒
- 不能peek,不能迭代
PriorityBlockingQueue 帶排序的BlockingQueue實現,無界佇列,在開始時可以指定初始大小,插入元素時,空間不夠可以自動擴容。初入佇列的元素必須是可比較大小的(comparable ),否則報異常。
11. 專案中如何排查記憶體洩露?
容易發生記憶體洩露的地方:
- 集合類
集合類 新增元素後,仍引用著 集合元素物件,導致該集合元素物件不可被回收,從而 導致記憶體洩漏
- Static關鍵字修飾的成員變數 被 Static 關鍵字修飾的成員變數的生命週期 = 應用程式的生命週期
若使被 Static 關鍵字修飾的成員變數 引用耗費資源過多的例項(如Context),則容易出現該成員變數的生命週期 > 引用例項生命週期的情況,當引用例項需結束生命週期銷燬時,會因靜態變數的持有而無法被回收,從而出現記憶體洩露
解決方案
儘量避免 Static 成員變數引用資源耗費過多的例項(如 Context) 若需引用 Context,則儘量使用Applicaiton的Context 使用 弱引用(WeakReference) 代替 強引用 持有例項
常見的單例模式持有context的應用,單例模式 由於其靜態特性,其生命週期的長度 = 應用程式的生命週期,儘量使用Applicaiton的Context
- 非靜態內部類 / 匿名類 非靜態內部類 / 匿名類 預設持有 外部類的引用;而靜態內部類則不會 非靜態內部類預設持有外部類的引用 而導致外部類無法釋放,最終 造成記憶體洩露
多執行緒的使用方法 = 非靜態內部類 / 匿名類;即 執行緒類 屬於 非靜態內部類 / 匿名類
當 工作執行緒正在處理任務 & 外部類需銷燬時, 由於 工作執行緒例項 持有外部類引用,將使得外部類無法被垃圾回收器(GC)回收,從而造成 記憶體洩露
- 資源物件使用後未關閉
對於資源的使用(如 廣播BraodcastReceiver、檔案流File、資料庫遊標Cursor、圖片資源Bitmap等),若在Activity銷燬時無及時關閉 / 登出這些資源,則這些資源將不會被回收,從而造成記憶體洩漏
- LeakCanary 可以直接在手機上檢視記憶體洩漏的地方,方便,常用
- Android profile中的memory 進行檢視 手動gc抓取procf檔案,用相應的工具檢視分析。
- Memory analyer tool MAT eclipse中使用
12. FragmentPagerAdapter和FragmentPagerStateAdapter的區別?
- PagerAdapter:快取三個,通過重寫instantiateItem和destroyItem達到建立和銷燬view的目的。
- FragmentPagerAdapter:內部通過FragmentManager來持久化每一個Fragment,在destroyItem方法呼叫時只是detach對應的Fragment,並沒有真正移除!
- FragmentPagerStateAdapter:內部通過FragmentManager來管理每一個Fragment,在destroyItem方法 呼叫時移除對應的Fragment。
所以,我們分情況使用這三個Adapter
PagerAdapter:當所要展示的檢視比較簡單時適用
FragmentPagerAdapter:當所要展示的檢視是Fragment,並且數量比較少時適用
FragmentStatePagerAdapter:當所要展示的檢視是Fragment,並且數量比較多時適用
13. 介面和抽象類的區別?
介面和抽象類的相似性
- 介面和抽象類都不能被例項化,它們都位於繼承樹的頂端,用於被其他類實現和繼承。
- 介面和抽象類都可以包含抽象方法,實現介面或繼承抽象類的普通子類都必須實現這些抽象方法。
介面和抽象類的區別
- 介面裡只能包含抽象方法,靜態方法和預設方法,不能為普通方法提供方法實現,抽象類則完全可以包含普通方法。
- 介面裡只能定義靜態常量,不能定義普通成員變數,抽象類裡則既可以定義普通成員變數,也可以定義靜態常量。
- 介面不能包含構造器,抽象類可以包含構造器,抽象類裡的構造器並不是用於建立物件,而是讓其子類呼叫這些構造器來完成屬於抽象類的初始化操作。
- 介面裡不能包含初始化塊,但抽象類裡完全可以包含初始化塊。
- 一個類最多隻能有一個直接父類,包括抽象類,但一個類可以直接實現多個介面,通過實現多個介面可以彌補Java單繼承不足。
14. 談談你對物件導向三大特性的理解?
封裝,繼承,多型
15. KMP演算法
16. 時間複雜度為n的情況下,將兩個有序陣列合併成一個
17. 點選了桌面圖示以後做了哪些事?
18. jvm記憶體
- java堆記憶體
儲存Java物件的例項, GC活動區域, 執行緒間共享記憶體,記憶體不夠時發生oom
- java虛擬機器棧(棧記憶體)
儲存java執行方法中的引用變數,引數型別等。 Java棧幀的入棧出棧,執行緒間記憶體不共享,能發生oom,與stackoverflow異常
- 方法區
儲存了jvm已經載入了類的資訊,常量,靜態變數,等資料。記憶體共享,能發生oom,記憶體回收,對常量池的處理
- 本地虛擬機器棧
同Java虛擬機器棧,這裡主要指向native方法
- Java程式計數器
當前執行緒位元組碼的行號指示器,不會發生oom ,記憶體執行緒私有,一個執行緒一個計數器。
19. Android如何解決螢幕適配
- 佈局 現在推薦使用ConstraintLayout,LinearLayout ,FrameLayout來佈局,寬高不要寫死,使用weight屬性,推薦使用的ContraintLayout一般可以解決佈局視適配問題。
- 圖片 一般會用不同的解析度的圖片,儘量用drawable.xml向量圖,.9圖片,現在主流螢幕在1080P左右,需採用xxhdpi下的圖片
- 版本控制 相容不同版本,可能會建立不同的theme,style,string
- 螢幕大小資源配置 對於超大屏可以單獨配置不同layout,values檔案屬性
20. Android View事件傳遞機制
-
activity-->ViewGroup-->View
-
dispatchTouchEvent()->onInterceptEvent()->onTouchEvent();
-
viewGroup中有onIntercepterEvent()操作 view中沒有,沒有子view,不需要攔截操作。
-
true--消費 攔截
false--不消費 繼續傳遞 -
控制元件enable時才能出來攔截,點選事件
-
onTouch()->onTouchEvent()->performClick()->onClick()
-
ActionDown出發後不一定能接受到ActionMove,ActionUp事件,如:父控制元件在move事件中做了攔截操作,會出發子View的ActionCancle操作,然後再執行父View的actionMove,actionUp事件
21.Android訊息推送
-
MQTT
-
輪詢請求pull
-
XMPP
-
手機廠商推送(小米,華為) 應用系統覆蓋廣,廠商不會殺死自己的推送服務
-
第三方平臺的推送 建議,推送抵達率高,延時低(友盟,極關,雲巴(基於MQTT)) 阿里雲移動推送、騰訊信鴿推送、百度雲推送
若多個app都使用了友盟推送,自己app在殺死情況下,若有其它使用友盟app存活,可以收到訊息。 安全性不夠高,別人的伺服器
-
自己搭建
看使用者群體,渠道,規模,效能,可以整合多方推送,保證訊息的到達率
通知欄訊息,透傳訊息
透傳訊息在整個訊息傳遞過程中比通知欄訊息多了一步-傳遞到App,因此透傳訊息就增加一些被系統限制的概率,給系統殺死的概率就高一些,所以說,通知欄訊息比透傳訊息應該能提供更好的送達率。
通知欄訊息的優點:送達率高
透傳訊息的優點:對訊息操作程度高 & 自定義程度高
22.Android:XML 解析方式對比(DOM、SAX、PULL)
- DOM: 載入整個文件,佔用記憶體,耗時較長,操作文件效率高,可以修改,使用於,文件較小,需要頻繁操作的場景。
- SAX:
事件驅動型,解析速度快,靈活性高,佔用記憶體小,無法修改文件,可擴充性差,解析方法複雜,適用於大型文件,要求解析效率高,不需要修改文件,不需要控制結束時機。
- PULL:
同SAX,優點是可以控制訪問結束的時機,使用簡單些,推薦使用
23. Activity 生命週期
onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory()
-
啟動Activity 系統會先呼叫onCreate方法,然後呼叫onStart方法,最後呼叫onResume,Activity進入執行狀態。
-
當前Activity被其他Activity覆蓋一部分其上或被鎖屏 系統會呼叫onPause方法,暫停當前Activity的執行
-
當前Activity由被覆蓋(一部分)狀態回到前臺或解鎖屏 系統會呼叫onResume方法,再次進入執行狀態。
-
當前Activity轉到新的Activity介面或按Home鍵回到主屏,自身退居後臺 系統會先呼叫onPause方法,然後呼叫onStop方法,進入停滯狀態
-
使用者後退回到此Activity 系統會先呼叫onRestart方法,然後呼叫onStart方法,最後呼叫onResume方法,再次進入執行狀態。
-
當前Activity處於被覆蓋狀態或者後臺不可見狀態,當更高優先順序的apps需要記憶體,系統記憶體不足 系統就會殺死當前Activity
-
而後使用者退回當前Activity 再次呼叫onCreate方法、onStart方法、onResume方法,進入執行狀態。
-
使用者退出當前Activity 系統先呼叫onPause方法,然後呼叫onStop方法,最後呼叫onDestory方法,結束當前Activity。
fragment
onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()->onPause()->onStop()->onDestoryView()->onDestory()->onDetach()
-
onAttach方法 Fragment和Activity建立關聯的時候呼叫(獲得activity的傳遞的值)
-
onCreateView方法 為Fragment建立檢視(載入佈局)時呼叫(給當前的fragment繪製UI佈局,可以使用執行緒更新UI)
-
onActivityCreated方法 當Activity中的onCreate方法執行完後呼叫(表示activity執行oncreate方法完成了的時候會呼叫此方法)
-
onDestroyView方法 Fragment中的佈局被移除時呼叫(表示fragment銷燬相關聯的UI佈局)
-
onDetach方法 Fragment和Activity解除關聯的時候呼叫(脫離activity)
-
螢幕滅掉 onPause() onSaveInstanceState() onStop()
-
螢幕解鎖 onStart() onResume()
-
切換到其他Fragment onPause() onStop() onDestroyView()
-
切換回本身的Fragment onCreateView() onActivityCreated() onStart() onResume()
-
回到桌面 onPause() onSaveInstanceState() onStop()
-
回到應用 onStart() onResume()
-
退出應用 onPause() onStop() onDestroyView() onDestroy() onDetach()
啟動模式
-
standard 標準模式,後進先出
-
singletop 棧頂複用模式,棧頂存在activity,複用,呼叫onNewIntent(),否則新建,加入棧頂。
-
singleTask 棧內複用模式,不存在棧,新建棧,加入新的activity,存在棧,存在activity,複用,否則新建activity,加入
-
singleInstance 單例,建立單獨的棧,存放單獨的activity,多執行緒公用。
24. Service 生命週期
- 手動呼叫startService() : onCreate()[第一次]->onStartCommond()[可能多次]->onStop()->onDestory();
- 手動呼叫stopServie(): 若沒有呼叫onStartCommond()直接結束。若已經呼叫onStartCommond(),看是否有bindservice(),有的話,看是否解綁,已經解綁unBindService(),ondestroy().沒有繫結服務的話,直接onDestory
- 手動呼叫bindservice():oncreat(是否已經調過),沒有的話,oncreate()-onBind();已經調過,則看是否已經繫結服務,沒有的話onBind(),否則結束。
- 手動呼叫unBindService(): 若沒有onbind(),直接結束。否則,看是否呼叫onStartCommond(),沒呼叫過,unBind(),ondestroy(),呼叫過,unBind(),結束。
startService,stopService 只能開啟,關閉服務,無法操作服務,startService之後,呼叫者退出後,service繼續後臺執行。
bindService,unBindService 除了能夠繫結Service還能操作Service,呼叫者退出後,service銷燬。
25. BrodcastReciver的使用
自定義廣播接收者繼承自BrodCastReciver,實現onRecive()在UI執行緒中執行,不能執行耗時操作,並註冊廣播到訊息中心,當廣播發起方發起廣播時,訊息中心更加註冊的廣播,安照一定的規則,通知接收者。
靜態註冊 常駐廣播,耗電,耗記憶體,可以及時喚醒程式,為了是app體驗流暢,Android O 對隱式廣播做了部分限制,推薦使用動態註冊。可能部分隱式廣播在8.0上將無法起作用。
Activity生命週期的方法是成對出現的:
onCreate() & onDestory()
onStart() & onStop()
onResume() & onPause()
動態註冊最好在Activity 的onResume()註冊、onPause()登出。否則容易造成記憶體洩露,APP殺死回收前一定會走onPause所以保證能夠登出。不能重複註冊登出廣播。
自定義廣播 普通廣播
系統廣播 網路變動,電話來電,鎖屏,電量低,飛航模式等
有序廣播 設定的priority有序廣播。
app內廣播 LocalBroadcastManager 設定內部廣播傳送接收。
粘性廣播 5.0 廢棄
26. 一鍵退出app
- 設定入口activity 為SingleTask ,退出時呼叫跳轉到該activity,重寫onNewIntent()殺死自己,若有多工棧的activity 不可以退出,如singleIntance.
- 設定入口activity 的標誌位為 activity_clear_top,activity_single_top,重寫onNewIntent()殺死自己,若有多工棧的activity 不可以退出,如singleIntance.
- 在Android5.0以上可以採用自身的activityManager,獲取當前activity任務棧中的activity,逐個關閉。若有多工棧的activity 不可以退出,如singleIntance.
- 建立自己的activity棧,在application中註冊activityLifecycleCallback監聽,儲存所有的activity,在退出時,一個個逐步退出。
- 註冊廣播,rxbus
- 輔助可以呼叫本地方法,android.os.Process.killProcess(),System.exit(0),來結束當前程式,activity。
27. RecycleView 相比ListView優點:
- item複用性高,強制使用ViewHolder,規範ViewHolder的使用,把要展示的view設定給Viewholder,通過對Viewholder的複用,來複用View
- 靈活,可定製,擴充性高,充分的解耦,通過佈局管理器,來管理佈局展示效果,方便的實現列表,網格,流式佈局。
- 控制item的間隔,可以繪製
- 設定item增刪的動畫
缺點
item點選事件比較麻煩,需要自己實現
28.ContentProvider
為應用間的資料互動提供一個安全的環境,允許將自己應用的資料根據需求給其它應用進行增刪改查,而不用擔心開放資料庫許可權引發安全問題。
可以用於程式間通訊,資料共享,內部通訊等。
訪問聯絡人通訊錄,日曆日程,音訊,視訊,圖片檔案等。
通過ContentRescover 與ContentProvider交換,操作簡單,可靠。
29. 如何減少安裝包的體積
lib,res,dex,
- proguard 做程式碼混淆,在編譯時會剔除無用的程式碼
- 使用Android lint做檢查,刪除沒有用到的String,圖片,values,styles等。
- 檢查asset資料夾,看看有哪些不用的檔案,刪除掉
- 儘量用drawable.xml,shape,layerlist替換一些圖片,背景
- 剔除一些ldpi,xxxhdpi下面不常用的圖片
- 在build.gradle中配置defaultConfig,提供了resConfig這個flavor來指定打包出只打包某些資源,比如字串、圖片,so庫等等
defaultConfig {
// ...
resConfigs "en", "de", "fr", "it"
resConfigs "hdpi", "xhdpi", "xxhdpi"
ndk {
abiFilters 'armeabi-v7a'
}
}
複製程式碼
- 對美工給的圖片做一下壓縮處理
- 第三方jar包,對原始碼進行刪減,提取我們有用的程式碼,打成jar來用
- 大專案可以使用外掛化
30. 熟練掌握常見資料庫操作
sqlite,GreenDao,realm,AFinal,OrmLite GreenDao 體積小,速度快 推薦使用:realm,c++編寫,速度快,跨平臺
create TABLE people (id interger primary key autoincrement,name varchar(20)) select * from people select name from people select name from people by id desc select name from people group by select name frome people where insert into people(name,age) values(a,3) update people set name = aa where id = 0 delete from people where id = 9
31. 熟練應用常見的設計模式
原則:
- 單一職責原則 單一功能
- 里氏替換原則 父類出現的地方,可以用其子類替換
- 開放關閉原則 對外修改開發,對內修改關閉
- 依賴倒置原則 細節依賴於抽象,抽象不依賴於細節
- 最小知識原則 單獨模組儘量少的依賴其它物件功能
- 介面隔離原則 多介面分類
* 工廠模式 * 單例模式 * builder模式 * 模板模式 * 裝飾器 * 責任鏈模式 * 觀察者模式 * 代理模式 起到中介作用,連線客戶端和目標物件,播放器中使用到 * 外觀模式 定義了一個高層、統一的介面,外部與通過這個統一的介面對子系統中的一群介面進行訪問。 * 策略模式
### 32. 熟練掌握自定義view的操作,滿足工作需求
- 分析功能場景,簡單的view,直接繼承自View來實現
- 再看是否能在現有的view上擴充套件功能實現
- 能複用組合View實現複雜佈局
- 繼承自ViewGroup
自定義View
- 實現構造方法,1(程式碼中動態new的一般),2(xml中配置的呼叫),3,4個構造引數的,最少實現一個
- 自定義屬性 values 建attrs.xml
- onMeasure()
MeasureSpec 將測量模式,和size封裝打包,減少記憶體
子View的MeasureSpec值根據子View的佈局引數(LayoutParams)和父容器的MeasureSpec值計算得來的,具體計算邏輯封裝在getChildMeasureSpec()裡。
測量模式(Mode)的型別有3種:
UNSPECIFIED (不常用到)
EXACTLY (match_parent,100dp) 父控制元件來決定其測量大小
AT_MOST (wrap_content) 子控制元件來實現自己的測量策略,來定大小
- onLayout()
繼承自ViewGroup需要遍歷,測量每個子View的位置,呼叫layout(),傳入位置,通過getMeasureWidth(),height(),獲取寬高
- onDraw() 繪製介面
33. 熟練掌握常見動畫的實現
補間動畫
- 平移
- 旋轉
- 透明度
- 縮放 使用簡單,不能改變view屬性,視覺效果上的變化
逐幀動畫
幀動畫,大量引用圖片,容易引起oom
屬性動畫
可以改變view的屬性,如顏色,背景,位置,實現複雜的動畫效果, 可以自定義屬性差值器,時間差值器,已經提供了幾種差值器,
34. 常見的排序
冒泡,快速,插入,選擇,希爾排序。
//冒泡
private static void sortm(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
複製程式碼
//快速排序
public static void sortk(int[] arr, int left, int right) {
if (left > right) return;
int i, j, temp;
temp = arr[left];
i = left;
j = right;
while (i < j) {
while (arr[j] >= temp && i < j)
j--;
while (arr[i] <= temp && i < j)
i++;
if (i < j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
arr[left] = arr[i];
arr[i] = temp;
sortk(arr, left, i - 1);
sortk(arr, i + 1, right);
}
複製程式碼
//選擇排序
public static void sort(int[] arr) {
int k = 0;
for (int i = 0; i < arr.length; i++) {
k = i;
for (int j = i; j < arr.length; j++) {
if (arr[j] < arr[k]) {
k = j;
}
}
int temp = arr[k];
arr[k] = arr[i];
arr[i] = temp;
}
}
複製程式碼
//插入排序
private static void sortc(int[] arr){
for (int i = 0; i < arr.length; i++) {
for (int j=i;j>0&&arr[j]<arr[j-1];j--){
int temp = arr[j];
arr[j]= arr[j-1];
arr[j-1]=temp;
}
}
}
複製程式碼
//希爾排序 最壞情況時間複雜度為:O(n^1.5),平均時間複雜度為O(nlogn)。
private static void sortX(int[] arr){
int len = arr.length;
int temp = 0;
while (len>=1){
for (int i = len; i < arr.length; i++) {
for (int j=i;j>=len&&arr[j]<arr[j-len];j-=len){
temp = arr[j];
arr[j]=arr[j-len];
arr[j-len] = temp;
}
}
len/=arr.length-1;
}
}
複製程式碼
35. 資料結構
36. 元件化開發,外掛化開發,熱修復
元件化
1:建module
2: config.gradle 配置 是否獨立執行
3:統一配置 第三方庫的版本
4:資原始檔命名規範
5: library -application 切換
6: mainfest, lanchactivity切換
7: common-library,base-application,main-application
8: 元件間的通訊 ARouter,Dagger
9: DataBinding
10: okhttp,retrofit,rxjava2,rxbus,fastjson,glide,lifeCycle,LeakCanary 記憶體洩露
11: mvp
外掛化
1: 定義標準化介面 2: 外掛app實現標準化介面 3: 宿主appactivity 代理activity,service,實現其生命週期相關方法,傳遞上下文Context,重寫getClassLoader,getResource(); 4: 通過dexClassLoader 載入外掛apk 5: 通過classloader,與傳遞過來的完整類名,獲取構造方法,建立類的例項。呼叫相關方法。
要考慮相容性 宿主activity與外掛activity的通訊 外掛activiy,fragment相互之間的通訊
熱修復
Sophix 阿里雲,tingker 微信,AndFix支付寶。
37. 常見第三方庫的使用,EventBus,RxJava2,Retrofit,okhttp,glide,Dagger2,fastjson,DataBinding,
38. mvc,mvp,mvvp,mvvm
39. 強,軟,弱,虛應用
強引用: 為null,或不在引用時,回收 軟引用,記憶體空間足夠,垃圾回收器就不會回收它; 弱引用,當JVM進行垃圾回收時,無論記憶體是否充足,回收。 虛引用,並不影響物件的生命週期,任何時候都會被回收
40. DVM 與 JVM的區別
- JVM基於棧,jvm位元組碼中,變數壓入棧進行計算
- DVM基於暫存器,dalvik位元組碼複製給暫存器,直接訪問暫存器,基於暫存器的編譯要比棧更快
- DVM將編譯的class打包為一個dex,外加一些資原始檔,最終打包成apk.
- jvm 將編譯的多個class打包為一個jar包,使用
- DVM執行多個例項,每個例項做為單獨的linux程式執行
41.class檔案和dex檔案有什麼區別
class檔案,通過將jvm語言(Java,kotlin,python等)編譯得到的二進位制檔案,儲存類的所有資訊。 dex檔案是有Android dvm將編譯生成的多個class的檔案打包生成的檔案。 dex檔案去除了class檔案中的一些多餘的資訊,更加精簡,執行效率高
42.http和https的區別,https是怎麼認證的
http是基於tcp的一種網路傳輸協議,明文傳輸,不安全。Https是在http的基礎上加了一層ssl協議,對資料進行加密處理,對需要加密傳輸,保證安全的資料,可以採用https.
https需要申請證照,http不需要 http預設埠80 https 443 http連結簡單,不安全 https安全
1: 客戶端通過https的url訪問伺服器,要求建立ssl連線。 2: 伺服器將網站的證照和公鑰返回給客戶端 3: 客戶端隨機生成一個祕鑰,用公鑰對稱加密傳送給伺服器 4: 伺服器用自己的私鑰解密,獲取祕鑰 5:伺服器利用該祕鑰對資料加密和客戶端通訊
43.理解StackOverflowError與OutOfMemoryError
jvm執行緒操作方法是基於棧的實現,每個方法的執行時一個棧幀的入棧與出棧的過程。當方法過多,遞迴太深的話,棧內空間不足,無法繼續入棧,就會發生棧溢位。
private static void method() {
method();
}
複製程式碼
jvm不能分配給建立物件所需要的空間,並且gc回收的空間也不足以滿足時,會發生OOM.
List<Object> list = new ArrayList<Object>();
while(true){
int[] index = new int[20_0000_0000];
list.add(index);
}
複製程式碼
44.httpurlconnection,httpclient, 和okhttp的區別
httpclient : Apache開發的第三方網路框架,api多,穩定,用起來方便。封裝的眾多api改進起來比較困難,Android 6.0 已改為使用okhttp.
httpUrlConection: sun公司提供,api簡單,小巧但它主要的 API使我們能輕易的使用和擴充它,2.2之前會有bug。
okttp square 家族的,square出品必屬精品。 okhttp是專注於提升網路連線效率的http客戶端。 1、它能實現同一ip和埠的請求重用一個socket,這種方式能大大降低網路連線的時間,和每次請求都建立socket,再斷開socket的方式相比,降低了伺服器的壓力。 2、okhttp 對http和https都有良好的支援。 3、okhttp 不用擔心android版本變換的困擾。 4、成熟的網路請求解決方案,比HttpURLConnection更好用。 5、缺點,okhttp請求網路切換回來是線上程裡面的,不是在主執行緒,不能直接重新整理UI,需要我們手動處理。
45.Java中有哪些執行緒安全的集合?
Vector,HashTable,concurrentMap
46. volatile synchronized
47. picasso glide
- 載入圖片的編碼格式不一樣 Picasso argb_8888,glide rgb_565,載入同樣的一張圖片,glide需要的記憶體空間要小很多。
- 快取策略不一樣,Picasso 會快取整個原圖,glide會快取最後載入的圖(可以調整快取策略)
- glide可以載入gif,和解析視訊第一幀圖片載入,Picasso不可以
- glide載入圖片速度快於Picasso,佔用記憶體較小,有效防止oom
- glide 定義動畫,對圖片處理,展示更加靈活
- glide 可以繫結Activity,fragment的生命週期,及時停止與恢復載入
- Picasso 的方法數要遠小於 glide ,可以減少655635的限制