1. 程式人生 > >冷啟動白屏分析

冷啟動白屏分析

介紹下Android應用程式啟動過程
1)Launcher通過Binder程序間通訊機制通知ActivityManagerService,它要啟動一個Activity;
2)ActivityManagerService通過Binder程序間通訊機制通知Launcher進入Paused狀態;
3)Launcher通過Binder程序間通訊機制通知ActivityManagerService,它已經準備就緒進入Paused狀態,於是ActivityManagerService就建立一個新的程序,用來啟動一個ActivityThread例項,即將要啟動的Activity就是在這個ActivityThread例項中執行;
4)ActivityThread通過Binder程序間通訊機制將一個ApplicationThread型別的Binder物件傳遞給ActivityManagerService,以便以後ActivityManagerService能夠通過這個Binder物件和它進行通訊;
5)ActivityManagerService通過Binder程序間通訊機制通知ActivityThread,現在一切準備就緒,它可以真正執行Activity的啟動操作了。

Activity的啟動模式以及使用場景
1/ standrd:標準模式,每次啟動都會重新建立一個activity例項加入到任務棧中,不會考慮是不是有此例項存在,不會複用,消耗記憶體資源

2/ SingleTop:棧頂複用,如果處於棧頂,則生命週期不走onCreate()和onStart(),會呼叫onNewIntent(),適合推送訊息詳情頁,比如新聞推送詳情Activity;只檢測任務棧棧頂,只有在棧頂的Activity不會被建立,就算是在第二位也是會被建立

3/SingleTask:棧內複用,如果存在棧內,則在其上所有Activity全部出棧,使得其位於棧頂,生命週期和SingleTop一樣,app首頁基本是用這個

4/ SingleInstance:這個是SingleTask加強本,系統會為要啟動的Activity單獨開一個棧,這個棧裡只有它,適用新開Activity和app能獨立開的,如系統鬧鐘,微信的視訊聊天介面,使用微信調起自己的客戶端某個頁面,不做任何處理的情況下,按下回退或者當前Activity.finish(),頁面不會停留在自己的客戶端而是返回到微信的客戶端頁面

使用:(1) manifest設定,(2) startActivity flag

棧和佇列的區別?
(1) 佇列先進先出,棧先進後出
(2) 對插入和刪除操作的"限定"。
棧是限定只能在表的一端進行插入和刪除操作的線性表。
佇列是限定只能在表的一端進行插入和在另一端進行刪除操作的線性表。
(3)遍歷資料速度不同。棧只能從頭部取資料,也就最先放入的需要遍歷整個棧最後才能取出來,而且在遍歷資料的時候還得為資料開闢臨時空間,保持資料在遍歷前的一致性。佇列則不同,它基於地址指標進行遍歷,而且可以從頭或尾部開始遍歷,但不能同時遍歷,無需開闢臨時空間,因為在遍歷的過程中不影像資料結構,速度要快的多

Broadcast 內部實現機制?
1)自定義廣播接受者Broadcast Receiver,並複寫onRecvice方法
2)通過Binder機制像AMS進行註冊
3)廣播發送者通過Binder機制像AMS傳送廣播
4)AMS查詢符合相應條件(IntentFilter/Permission等)的Broadcast Receiver,將廣播發送到Broadcast Receiver相應的訊息佇列迴圈當中去(一般是Activity中)
5)訊息迴圈拿到此廣播,回撥Broadcast Receiver中的onReceive方法、

兩種註冊方式的不同?
動態註冊不是常駐型廣播,也就是說廣播跟隨程式的生命週期。
靜態註冊是常駐型,也就是說當應用程式關閉後,如果有資訊廣播來,程式也會被系統呼叫自動執行。

android:enabled: 定義系統是否能夠例項化這個廣播接收器
android:exported: 用於指示該廣播接收器是否能夠接收來自應用程式外部的訊息

HttpConnection 與HttpURLConnection的區別?
在Android 2.2版本之前,HttpClient擁有較少的bug,因此使用它是最好的選擇
2.3之後使用HttpURLConnection,它的API簡單,體積較小,壓縮和快取機制可以有效地減少網路訪問的流量,在提升速度和省電方面也起到了較大的作用,利於維護與優化

HTTP 和 HTTPS 的不同之處
1、開頭不同https:// 與http://
2、HTTP 是不安全的,而 HTTPS 是安全的,HTTPS 在 HTTP 應用層的基礎上使用安全套接字層作為子層
3、HTTP 標準埠是 80 ,而 HTTPS 的標準埠是 443
4、在 OSI 網路模型中,HTTP 工作於應用層,而 HTTPS 工作在傳輸層
5、HTTP 無需加密,而 HTTPS 對傳輸的資料進行加密
6、HTTP 無需證書,而 HTTPS 需要認證證書

HTTP 和 HTTPS 的相同點
而 HTTPS 和 HTTP 唯一不同的只是一個協議頭(https)的說明,其他都是一樣的,換句話說,HTTPS 跟 HTTP 一樣,只不過增加了 SSL。

什麼時候該使用 HTTPS?
銀行網站、支付閘道器、購物網站、登入頁、電子郵件以及一些企業部門的網站應該使用 HTTPS
如果某個網站要求你填寫信用卡資訊,首先你要檢查該網頁是否使用 https 加密連線

程序優先順序?
前臺程序 (Foreground process)、可見程序 (Visible process)、服務程序 (Service process)、後臺程序 (Background process)、空程序 (Empty process)

前臺程序:
1、擁有使用者正在互動的 Activity(已呼叫 onResume())
2、擁有正在“前臺”執行的 Service(服務已呼叫 startForeground())
3、擁有正在“前臺”執行的 Service(服務已呼叫 startForeground())

可見程序:
1、擁有不在前臺、但仍對使用者可見的 Activity(已呼叫 onPause())
2、擁有繫結到可見(或前臺)Activity 的 Service

服務程序:
正在執行 startService() 方法啟動的服務,且不屬於上述兩個更高類別程序的程序。

後臺程序:對使用者不可見的 Activity 的程序(已呼叫 Activity的onStop() 方法)
空程序 :不含任何活動應用元件的程序

Android 中對於記憶體的回收,主要依靠 Lowmemorykiller 來完成,是一種根據 OOM_ADJ 閾值級別觸發相應力度的記憶體回收的機制。

程序保活?
1、提供程序優先順序,降低程序被殺死的概率
2、在程序被殺死後,進行拉活

程序優先順序的方式:
1、利用 Activity 提升許可權:監控手機鎖屏解鎖事件,在螢幕鎖屏時啟動1個畫素的 Activity,在使用者解鎖時將 Activity 銷燬掉。注意該 Activity 需設計成使用者無感知,通過該方案,可以使程序的優先順序在螢幕鎖屏時間由4提升為最高優先順序1。本方案主要解決第三方應用及系統管理工具在檢測到鎖屏事件後一段時間(一般為5分鐘以內)內會殺死後臺程序,已達到省電的目的問題。
實現方式:通過windon設定windonparmeas 是指 Activity 的大小為1畫素;引用主題 Activity 為透明;通過廣播監聽鎖屏開啟來控制activity的控制與銷燬

2、利用 Notification 提升許可權
方案設計思想:Android 中 Service 的優先順序為4,通過 setForeground 介面可以將後臺 Service 設定為前臺 Service,使程序的優先順序由4提升為2,從而使程序的優先順序僅僅低於使用者當前正在互動的程序,與可見程序優先順序一致,使程序被殺死的概率大大降低。

方案實現挑戰:從 Android2.3 開始呼叫 setForeground 將後臺 Service 設定為前臺 Service 時,必須在系統的通知欄傳送一條通知,也就是前臺 Service 與一條可見的通知時繫結在一起的。對於不需要常駐通知欄的應用來說,該方案雖好,但卻是使用者感知的,無法直接使用。

方案挑戰應對措施:通過實現一個內部 Service,在 LiveService 和其內部 Service 中同時傳送具有相同 ID 的 Notification,然後將內部 Service 結束掉。隨著內部 Service 的結束,Notification 將會消失,但系統優先順序依然保持為2。

程序被殺死後,進行拉活:

1、利用系統廣播拉活
方案設計思想:在發生特定系統事件時,系統會發出響應的廣播,通過在 AndroidManifest 中“靜態”註冊對應的廣播監聽器,即可在發生響應事件時拉活。
常用的用於拉活的廣播事件包括:開機、網路變化、螢幕亮滅、解鎖屏等

方案適用範圍:適用於全部 Android 平臺。但存在如下幾個缺點:
廣播接收器被管理軟體、系統軟體通過“自啟管理”等功能禁用的場景無法接收到廣播,從而無法自啟。
系統廣播事件不可控,只能保證發生事件時拉活程序,但無法保證程序掛掉後立即拉活。

3、利用系統Service機制拉活
方案設計思想:將 Service 設定為 START_STICKY,利用系統機制在 Service 掛掉後自動拉活:

方案適用範圍:如下兩種情況無法拉活
1)Service 第一次被異常殺死後會在5秒內重啟,第二次被殺死會在10秒內重啟,第三次會在20秒內重啟,一旦在短時間內 Service 被殺死達到5次,則系統不再拉起。
2)程序被取得 Root 許可權的管理工具或系統工具通過 forestop 停止掉,無法重啟。

4、利用Native程序拉活
該方案主要適用於 Android5.0 以下版本手機。

5、利用 JobScheduler 機制拉活
建立JobService、使用JobInfo.Builder來構建一個JobInfo物件、建立JobScheduler物件,並把JobInfo交給它管理

6、利用賬號同步機制拉活
Android 系統的賬號同步機制會定期同步賬號進行,通過AccountManager開啟同步機制,並在Manifest 中定義賬號授權與同步服務。

為什麼要有事件分發機制?
安卓上面的View是樹形結構的,View可能會重疊在一起,當我們點選的地方有多個View都可以響應的時候,這個點選事件應該給誰呢?為了解決這一個問題,就有了事件分發機制。
避免view重疊帶來的響應事件混亂

螢幕適配:
使用一套1280*720的圖片啊,對應於xhdpi,使用wrap_content, match_parent, layout_weight一些佈局引數啊,使用百分比佈局啊,使用相對佈局,禁用絕對佈局,使用.9圖啊等等

當螢幕被點選的時候,由於view是樹型結構的,事件首先會傳遞給activity,然後傳遞給view的事件管理類phonewindow,而phonewindow是通過它的內部類decorview來進行訊息傳遞的,decorview又會傳遞給最大的外部父容器viewgroup,viewgroup依次傳遞給它裡面的子view,事件分發流程就是這樣的一個流程,如果最後的view沒有被處理,就是依次反轉到最高的activity,如果activity也沒有被處理,這個事件才會被浪費掉,這就是著名的責任鏈模式

RSA+AES
原理:互質關係、尤拉函式、尤拉定理、模反元素等一些數學元素

非對稱加密演算法,兩個金鑰:公開金鑰(publickey)和私有金鑰(privatekey),簡單的說是“公鑰加密,私鑰解密;私鑰加密,公鑰解密”

客戶端傳輸重要資訊給服務端,服務端返回的資訊不需加密的情況,使用RSA即可,
例如繫結銀行卡的時候,需要傳遞使用者的銀行卡號,手機號等重要資訊,客戶端這邊就需要對這些重要資訊進行加密,使用RSA公鑰加密,服務端使用RSA解密,然後返回一些普通訊息,比如狀態碼code,提示資訊msg,提示操作是成功還是失敗

客戶端傳輸重要資訊給服務端,服務端返回的資訊需加密的情況,使用RSA+AES
例如客戶端登入的時候,傳遞使用者名稱和密碼等資料,需要進行加密,服務端驗證登入資訊後,返回令牌token需要進行加密,客戶端解密後儲存

為什麼使用兩種,一種不行嗎?
兩種更安全,客戶端公鑰暴露導致資訊暴露

流程:客戶端

  1. 客戶端隨機產生AES的金鑰;
  2. 對身份證資訊(重要資訊)進行AES加密;
  3. 通過使用RSA對AES金鑰進行公鑰加密。
    服務端:
  4. 對加密後的AES金鑰進行RSA私鑰解密,拿到金鑰原文;
  5. 對加密後的重要資訊進行AES解密,拿到原始內容。

MVP與MVC的區別?
1)在MVC中,資料Modle層與檢視View層是可以直接進行資料互動的,MVP中,Modle層與View層的資料互動是通過Presenter完成的。
2)MVP的解耦性比MVC更低(因為它把Presenter層作為View層與Modle之間的橋樑,而不會輕易的讓M層與V層進行互動)
3)需要將複雜的邏輯抽離成介面的形式,且程式碼比MVC要多

MVP優點:
1,Model與View完全分離,徹底解耦
2,Presenter複用,可以將一個Presenter用於多個檢視,而不用改變Presenter的邏輯
3,可以實現View介面進行邏輯測試(Presenter的單元測試)
MVP的缺點及優化:
1,View層過大,Activity複雜,加入模板方法,分離出BaseActivity用於處理公共邏輯
2,Model層過大,做好模組劃分,進行介面隔離,在內部進行分層。
3,還有一個問題是,MVP額外的增加了很多類和介面,這個可以根據專案實際情況進行相應地優化

登陸例項:
首先宣告一個LoginContract 契約類,定義了登入頁面的Loginview介面、Loginmodel介面和對應的回撥,在view中,定義UI展示與使用者資料獲取的相關方法,如檢查賬號密碼格式成功(失敗)、登入成功(失敗)等。model負責資料請求,定義登入的方法。回撥定義了登入成功還是失敗的方法。建立Model實現類,重寫其登入方法既可,將登入結果交給回撥。定義Presenter,內部持有LoginView的例項,對獲取到的使用者資訊做一個判斷處理然後呼叫實現類modle層的登陸方法,最後在Activity中重寫LoginView的方法與宣告Presenter的例項進行操作就可以了

MQTT簡介
MQTT 是一個客戶端服務端架構的釋出/訂閱模式的訊息傳輸協議,基於TCP/IP協議上,MQTT最大優點在於,以極少的程式碼和有限的頻寬,為連線遠端裝置提供實時可靠的訊息服務。作為一種低開銷、低頻寬佔用的即時通訊協議

MQTT協議原理
1、MQTT協議實現方式
需要客戶端和伺服器端通訊完成,有三種身份:釋出者、代理(伺服器)、訂閱者。其中,訊息的釋出者和訂閱者都是客戶端,訊息代理是伺服器,訊息釋出者可以同時是訂閱者。

MQTT傳輸的訊息分為:主題和負載兩部分:
(1)Topic,可以理解為訊息的型別,訂閱者訂閱(Subscribe)後,就會收到該主題的訊息內容(payload)
(2)payload,可以理解為訊息的內容,是指訂閱者具體要使用的內容。

2、網路傳輸與應用訊息
MQTT會構建底層網路傳輸:它將建立客戶端到伺服器的連線,提供兩者之間的一個有序的、無損的、基於位元組流的雙向傳輸。當應用資料通過MQTT網路傳送時,MQTT會把與之相關的服務質量(QoS)和主題名(Topic)相關連。

3、MQTT客戶端
(1)釋出其他客戶端可能會訂閱的資訊;
(2)訂閱其它客戶端釋出的訊息;
(3)退訂或刪除應用程式的訊息;
(4)斷開與伺服器連線。

4、MQTT伺服器
MQTT伺服器以稱為“訊息代理”(Broker),可以是一個應用程式或一臺裝置。它是位於訊息釋出者和訂閱者之間,它可以:
(1)接受來自客戶的網路連線;
(2)接受客戶釋出的應用資訊;
(3)處理來自客戶端的訂閱和退訂請求;
(4)向訂閱的客戶轉發應用程式訊息。

5、MQTT協議中的訂閱、主題、會話
1)、訂閱(Subscription)
訂閱包含主題篩選器(Topic Filter)和最大服務質量(QoS)。訂閱會與一個會話(Session)關聯。一個會話可以包含多個訂閱。每一個會話中的每個訂閱都有一個不同的主題篩選器。

2)、會話(Session)
每個客戶端與伺服器建立連線後就是一個會話,客戶端和伺服器之間有狀態互動。會話存在於一個網路之間,也可能在客戶端和伺服器之間跨越多個連續的網路連線。

3)、主題名(Topic Name)
連線到一個應用程式訊息的標籤,該標籤與伺服器的訂閱相匹配。伺服器會將訊息傳送給訂閱所匹配標籤的每個客戶端。

4)、主題篩選器(Topic Filter)
一個對主題名萬用字元篩選器,在訂閱表示式中使用,表示訂閱所匹配到的多個主題。

5)、負載(Payload)
訊息訂閱者所具體接收的內容。

6、MQTT協議中的方法
通常來說,資源指伺服器上的檔案或輸出。主要方法有:
(1)Connect。等待與伺服器建立連線。
(2)Disconnect。等待MQTT客戶端完成所做的工作,並與伺服器斷開TCP/IP會話。
(3)Subscribe。等待完成訂閱。
(4)UnSubscribe。等待伺服器取消客戶端的一個或多個topics訂閱。
(5)Publish。MQTT客戶端傳送訊息請求,傳送完成後返回應用程式執行緒。

MQTT協議資料包結構
在MQTT協議中,一個MQTT資料包由:固定頭 、可變頭 、訊息體三部分構成
(1)固定頭。存在於所有MQTT資料包中,表示資料包型別及資料包的分組類標識。不能超過256M
(2)可變頭。存在於部分MQTT資料包,資料包型別決定了可變頭是否存在及其具體內容。
(3)訊息體。存在於部分MQTT資料包中,表示客戶端收到的具體內容。

固定頭:
MQTT資料包型別 位置:Byte 1中bits 7-4。相於一個4位的無符號值
標識位 位置:Byte 1中bits 3-0。在不使用標識位的訊息型別中,標識位被作 為保留位。如果收到無效的標誌時,接收端必須關閉網路連線
剩餘長度 位置:Byte 2 固定頭的第二位元組用來儲存變長頭部和訊息體的總大小

MQTT可變頭
MQTT資料包中包含一個可變頭,它駐位於固定的頭和負載之間。可變頭的內容因資料包型別而不同,較常的應用是作為包的標識:
訊息體:
Payload訊息體位MQTT資料包的第三部分,包含CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE
(1)CONNECT,訊息體內容主要是:客戶端的ClientID、訂閱的Topic、Message以及使用者名稱和密碼。
(2)SUBSCRIBE,訊息體內容是一系列的要訂閱的主題以及QoS。
(3)SUBACK,訊息體內容是伺服器對於SUBSCRIBE所申請的主題及QoS進行確認和回覆。
(4)UNSUBSCRIBE,訊息體內容是要訂閱的主題。

MQTT 定義的控制報文有16種,2種保留的,14種常用的,常用的有:建立連線、建立連線確認、訂閱、訂閱確認、取消訂閱、取消訂閱確認、心跳請求、心跳響應、傳送訊息、發訊息確認、釋出訊息收到、釋出訊息釋放、釋出完成、斷開連線

MQTT協議工作在低頻寬、不可靠的網路的遠端感測器和控制裝置通訊而設計的協議
它具有以下主要的幾項特性:
(1)使用釋出/訂閱訊息模式,提供一對多的訊息釋出,解除應用程式耦合。
這一點很類似於XMPP,但是MQTT的資訊冗餘遠小於XMPP,,因為XMPP使用XML格式文字來傳遞資料。
(2)訊息傳輸不需要知道負載內容。整個傳輸報文使用二進位制位元組流
(3)很小的傳輸消耗和協議資料交換,最大限度減少網路流量,固定長度的頭部是2位元組
(4)提供三種等級的服務質量
a)“至少一次”,確保訊息到達,但訊息重複可能會發生。(如建立連線、發訊息、收訊息、心跳。)
b)“只有一次”,確保訊息到達一次
c)“至多一次”,訊息釋出完全依賴底層TCP/IP網路。會發生訊息丟失或重複
(5)出現異常斷開連線的時候,客戶端會受到異常提醒

MQTT 定義的報文構成,分為三部分:固定報頭、可變報頭、有效載荷。
固定報頭,所有控制報文都包含
可變報頭,部分控制報文包含
有效載荷,部分控制報文包含

XMPP對比MQTT:
XMPP資料傳輸XML,MQTT傳輸二進位制資料
XMPP:協議較複雜、冗餘(基於XML)、費流量、費電
MQTT:協議簡潔、小巧、可擴充套件性強、省流量、省電

Picasso,Glide,Fresco對比?
Picasso不支援磁碟快取,不支援GIF,快取的是全尺寸,預設ARGB_8888,而 Glide的快取與ImageView的尺寸相同,Glide體積更小、支援GIF,預設RGB_565,比ARGB記憶體開銷要小

事件分發機制?
首先要知道三個方法,dispatchTouchEvent分發,dispatchTouchEvent攔截(Viewgroup才有,Activity和view沒有),onTouchEvent消費,
當螢幕被點選的時候,由於view是樹型結構的,事件首先會傳遞給activity、由activity的dispatchTouchEvent進行分發,返回ture事件結束,返回flase傳遞給view的事件管理類phonewindow,而phonewindow是通過它的內部類decorview來進行訊息傳遞的,decorview又會傳遞給最大的外部父容器viewgroup,然後呼叫dispatchTouchEvent進行分發,然後呼叫dispatchTouchEvent是否將事件進行攔截,不攔截將事件依次傳遞給它裡面的子view,在由子view的dispatchTouchEvent進行事件的分發處理,由於沒有攔截器,返回ture,反轉到最高的activity,如果activity也沒有被處理,這個事件才會被浪費掉,返回false進行view的onTouch進行處理
這就是著名的責任鏈模式

Message、Handler、MessageQueue、Looper的之間的關係?
首先,是這個MessagQueue,MessageQueue是一個訊息佇列,它可以儲存Handler傳送過來的訊息,其內部提供了進隊和出隊的方法來管理這個訊息佇列,其出隊和進隊的原理是採用單鏈表的資料結構進行插入和刪除的,即enqueueMessage()方法和next()方法。這裡提到的Message,其實就是一個Bean物件,裡面的屬性用來記錄Message的各種資訊。
然後,是這個Looper,Looper是一個迴圈器,它可以迴圈的取出MessageQueue中的Message,其內部提供了Looper的初始化和迴圈出去Message的方法,即prepare()方法和loop()方法。在prepare()方法中,Looper會關聯一個MessageQueue,而且將Looper存進一個ThreadLocal中,在loop()方法中,通過ThreadLocal取出Looper,使用MessageQueue的next()方法取出Message後,判斷Message是否為空,如果是則Looper阻塞,如果不是,則通過dispatchMessage()方法分發該Message到Handler中,而Handler執行handlerMessage()方法,由於handlerMessage()方法是個空方法,這也是為什麼需要在Handler中重寫handlerMessage()方法的原因。這裡要注意的是Looper只能在一個執行緒中只能存在一個。這裡提到的ThreadLocal,其實就是一個物件,用來在不同執行緒中存放對應執行緒的Looper。
最後,是這個Handler,Handler是Looper和MessageQueue的橋樑,Handler內部提供了傳送Message的一系列方法,最終會通過MessageQueue的enqueueMessage()方法將Message存進MessageQueue中。我們平時可以直接在主執行緒中使用Handler,那是因為在應用程式啟動時,在入口的main方法中已經預設為我們建立好了Looper。

ThreadLocal
定義:ThreadLocal是執行緒內部的儲存類,通過它可以實現在每個執行緒中儲存自己的私有資料。即資料儲存以後,只能在指定的執行緒中獲取這個儲存的物件,而其它執行緒則不能獲取到當前執行緒儲存的這個物件。
作用:負責儲存和獲取本執行緒的Looper

子執行緒為什麼不能開啟handler?
handler在呼叫sendMessage或者post(Runnable)的時候都需要一個MessageQueue 訊息佇列來儲存傳送的訊息,而預設子執行緒中是沒有開啟Looper輪循器的,而訊息佇列又是由Looper來進行管理的,所以是沒有辦法開啟的,
如果子執行緒想要開啟,需要初始化looper,並且呼叫loop.loopers開啟一個迴圈

主執行緒中的Looper.loop()一直無限迴圈檢測訊息佇列中是否有新訊息為什麼不會造成ANR?
因為Android 的是由事件驅動的,looper.loop() 不斷地接收事件、處理事件,每一個點選觸控或者說Activity的生命週期都是執行在 Looper.loop() 的控制之下,如果它停止了,應用也就停止了。只能是某一個訊息或者說對訊息的處理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。也就說我們的程式碼其實就是在這個迴圈裡面去執行的,當然不會阻塞了。
總結:
Looer.loop()方法可能會引起主執行緒的阻塞,但只要它的訊息迴圈沒有被阻塞,能一直處理事件就不會產生ANR異常。

HandlerThread:
就是在run方法中通過Looper.prepare()來建立訊息佇列,並通過Looper.loop()來開啟訊息迴圈,這樣在實際使用中就允許在HandlerThread中建立Handler,是一個無線迴圈,通過quit方法或者quitSufely退出

MQTT協議是一個以c/s架構且基於TCP/IP的釋出訂閱的訊息傳輸協議,可以在低頻寬、低消耗的情況下建立客戶端到伺服器的連線,提供兩者之間的一個有序的、無損的、基於位元組流的雙向傳輸,應用資料通過MQTT網路傳送時,MQTT會把與之相關的服務質量(QoS)和主題名(Topic)相關連起來,實現方式需要藉助釋出者,訊息代理,訂閱者來完成,其中釋出者與訂閱者都可以是客戶端,而訊息代理則是伺服器。訊息傳遞的內容主要有主題與負載,主題可以理解為訊息的型別,負載就是具體要使用的訊息。

MQTT 定義的報文有三部分構成:固定報頭、可變報頭、有效載荷。
(1)固定頭。存在於所有MQTT資料包中,表示資料包型別及資料包的分組類標識。不能超過256M
(2)可變頭。存在於部分MQTT資料包,資料包型別決定了可變頭是否存在及其具體內容。
(3)訊息體。存在於部分MQTT資料包中,表示客戶端收到的具體內容。

MQTT 定義的控制報文有16種,2種保留的,14種常用的,常用的有:建立連線、建立連線確認、訂閱、訂閱確認、取消訂閱、取消訂閱確認、心跳請求、心跳響應、傳送訊息、發訊息確認、釋出訊息收到、釋出訊息釋放、釋出完成、斷開連線

多執行緒:
執行緒池的優點:
1、重用執行緒池中的執行緒,避免重複建立和銷燬帶來的效能損耗
2、對執行緒進行管理,提供定時執行以及指定間隔執行
3、控制執行緒的最大併發數,避免太多執行緒搶佔系統資源帶來的app卡頓
Executors.newCachedThreadPool();建立執行緒

核心執行緒數量:等於CPU核心+1
最大執行緒數:cpu核心*2+1
核心執行緒無超時機制,非核心執行緒在閒置時的超時時間為1s
任務佇列最大容量為128

可快取執行緒池:CachedThreadPool

  1. 執行緒數無限制
  2. 有空閒執行緒則複用空閒執行緒,若無空閒執行緒則新建執行緒
  3. 一定程度減少頻繁建立/銷燬執行緒,減少系統開銷

定長執行緒池:FixedThreadPool()

  1. 可控制執行緒最大併發數(同時執行的執行緒數)
  2. 超出的執行緒會在佇列中等待

定長執行緒池:ScheduledThreadPool()
支援定時及週期性任務執行。

單執行緒化的執行緒池:SingleThreadExecutor()

  1. 有且僅有一個工作執行緒執行任務
  2. 所有任務按照指定順序執行,即遵循佇列的入隊出隊規則

JNI/NDK
Jni:為了方便java呼叫後c、c++程式碼封裝的一層介面,因為有時候效能不夠強大或者不能呼叫系統的一些介面退出的

NDK:是一個編譯工具,方便jni來訪問系統介面或者,提供交叉編譯器,簡單的修改mk檔案就可以生成指定平臺的so庫檔案

Java呼叫c++:宣告native方法,並使用loadLIBRARY載入要生成的so庫標識
通過jaavah-jni命令生成標頭檔案,接著建立與剛才生成的標頭檔案相同的.cpp檔案,寫上具體的邏輯程式碼,配上android.mk和applacation.mk執行 ndk.build命令生成so,如果需要特頂的平臺so,在模組build.gradle中宣告ABI_FLITE屬性即可,在呼叫so的時候程式包名一定已聲名native的方法的包名一致,或者用最新的cmake進行so的編譯工作

視訊加密:
一是M3U8描述檔案,二是TS媒體檔案,通過ffmpeng進行切片處理,並使用AES進行加密。

記憶體溢位 out of memory,是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是記憶體溢位。

原因:
1.記憶體中載入的資料量過於龐大,如一次從資料庫取出過多資料;
2.集合類中有對物件的引用,使用完後未清空,使得JVM不能回收;
3.程式碼中存在死迴圈或迴圈產生過多重複的物件實體;
4.使用的第三方軟體中的BUG;
5.啟動引數記憶體值設定的過小

記憶體洩露 memory leak,是指程式在申請記憶體後,無法釋放已申請的記憶體空間,一次記憶體洩露危害可以忽略,但記憶體洩露堆積後果很嚴重,無論多少記憶體,遲早會被佔光。

記憶體洩露會最終會導致記憶體溢位! 大家都知道虛擬機器針對每一個應用都會分配給一定量的記憶體,當你的請求量超過這個值的時候,就是記憶體溢位。

記憶體洩露解決方案:

靜態變數導致的記憶體洩露(解決辦法:在適當的時候講靜態量重置為null,使其不再持有引用,這樣也可以避免記憶體洩露。比如靜態的context,靜態的view;靜態變數儲存在方法區,它的生命週期從類載入開始,到整個程序結束。一旦靜態變數初始化後,它所持有的引用只有等到程序結束才會釋放)

靜態單例模式持有Activity或者services無法被釋放(解決辦法:引用全域性的context 單例模式對應應用程式的生命週期,所以我們在構造單例的時候儘量避免使用Activity的上下文,而是使用Application的上下文)

非靜態內部類導致記憶體洩露(情況一)(例如:Handler,非靜態內部類(包括匿名內部類)預設就會持有外部類的引用,當非靜態內部類物件的生命週期比外部類物件的生命週期長時,就會導致記憶體洩露。
mHandler會作為成員變數儲存在傳送的訊息msg中,即msg持有mHandler的引用,而mHandler是Activity的非靜態內部類例項,即mHandler持有Activity的引用,那麼我們就可以理解為msg間接持有Activity的引用。msg被髮送後先放到訊息佇列MessageQueue中,然後等待Looper的輪詢處理(MessageQueue和Looper都是與執行緒相關聯的,MessageQueue是Looper引用的成員變數,而Looper是儲存在ThreadLocal中的)。那麼當Activity退出後,msg可能仍然存在於訊息對列MessageQueue中未處理或者正在處理,那麼這樣就會導致Activity無法被回收,以致發生Activity的記憶體洩露。
解決辦法:
通常在Android開發中如果要使用內部類,但又要規避記憶體洩露,一般都會採用靜態內部類+弱引用的方式。(當GC執行垃圾回收時,遇到Activity就會回收並釋放所佔據的記憶體單元),但是msg還是有可能存在訊息佇列MessageQueue中,所以更好的是在Activity銷燬時就將mHandler的回撥和傳送的訊息給移除掉。

非靜態內部類導致記憶體洩露(情況二)
非靜態內部類造成記憶體洩露還有一種情況就是使用Thread或者AsyncTask
比如在Activity中直接new一個子執行緒Thread:(newThread new Runable方式;新建的子執行緒Thread和AsyncTask都是匿名內部類物件,預設就隱式的持有外部Activity的引用,導致Activity記憶體洩露。要避免記憶體洩露的話還是需要像上面Handler一樣使用靜態內部類+弱應用的方式(程式碼就不列了,參考上面Hanlder的正確寫法)。)

未取消註冊或回撥導致記憶體洩露
比如我們在Activity中註冊廣播,如果在Activity銷燬後不取消註冊,那麼這個剛播會一直存在系統中,同上面所說的非靜態內部類一樣持有Activity引用,導致記憶體洩露。因此註冊廣播後在Activity銷燬後一定要取消註冊。
比如我們在Activity中註冊廣播,如果在Activity銷燬後不取消註冊,那麼這個剛播會一直存在系統中,同上面所說的非靜態內部類一樣持有Activity引用,導致記憶體洩露。因此註冊廣播後在Activity銷燬後一定要取消註冊。

Timer和TimerTask導致記憶體洩露
Timer和TimerTask在Android中通常會被用來做一些計時或迴圈任務,比如實現無限輪播的ViewPager:
當我們Activity銷燬的時,有可能Timer還在繼續等待執行TimerTask,它持有Activity的引用不能被回收,因此當我們Activity銷燬的時候要立即cancel掉Timer和TimerTask,以避免發生記憶體洩漏。

集合中的物件未清理造成記憶體洩露
這個比較好理解,如果一個物件放入到ArrayList、HashMap等集合中,這個集合就會持有該物件的引用。當我們不再需要這個物件時,也並沒有將它從集合中移除,這樣只要集合還在使用(而此物件已經無用了),這個物件就造成了記憶體洩露。並且如果集合被靜態引用的話,集合裡面那些沒有用的物件更會造成記憶體洩露了。所以在使用集合時要及時將不用的物件從集合remove,或者clear集合,以避免記憶體洩漏。

資源未關閉或釋放導致記憶體洩露
在使用IO、File流或者Sqlite、Cursor等資源時要及時關閉。這些資源在進行讀寫操作時通常都使用了緩衝,如果及時不關閉,這些緩衝物件就會一直被佔用而得不到釋放,以致發生記憶體洩露。因此我們在不需要使用它們的時候就及時關閉,以便緩衝能及時得到釋放,從而避免記憶體洩露。

屬性動畫(repeatCount為無限模式)執行緒優化
動畫同樣是一個耗時任務,比如在Activity中啟動了屬性動畫(ObjectAnimator),但是在銷燬的時候,沒有呼叫cancle方法,雖然我們看不到動畫了,但是這個動畫依然會不斷地播放下去,動畫引用所在的控制元件,所在的控制元件引用Activity,這就造成Activity無法正常釋放。因此同樣要在Activity銷燬的時候cancel掉屬性動畫,避免發生記憶體洩漏。

WebView造成記憶體洩露
關於WebView的記憶體洩露,因為WebView在載入網頁後會長期佔用記憶體而不能被釋放,因此我們在Activity銷燬後要呼叫它的destory()方法來銷燬它以釋放記憶體。
另外在查閱WebView記憶體洩露相關資料時看到這種情況:
Webview下面的Callback持有Activity引用,造成Webview記憶體無法釋放,即使是呼叫了Webview.destory()等方法都無法解決問題(Android5.1之後)。

最終的解決方案是:在銷燬WebView之前需要先將WebView從父容器中移除,然後在銷燬WebView。詳細分析過程請參考這篇文章:WebView記憶體洩漏解決方法。
原理:監視一個即將被銷燬的物件
白話:對被監控物件建立一個弱引用物件,後臺執行緒檢查這個物件是否被清除,如果沒有就觸發垃圾回收,垃圾回收之後如果這個弱引用物件還存在,說明發生了記憶體洩漏
1.RefWatcher.watch()函式會為被監控的物件建立一個KeyedWeakReference弱引用物件,是WeakReference對的子類,增加了鍵值對資訊,後面會根據指定的鍵key找到弱引用物件;
2.在後臺執行緒AndroidWatchExecutor中,檢查KeyedWeakReference弱引用是否已經被清除。如果還存在,則觸發一次垃圾回收之後。垃圾回收之後,如果弱引用物件依然存在,說明發生了記憶體洩露;

記憶體洩露在Android記憶體優化是一個比較重要的一個方面,很多時候程式中發生了記憶體洩露我們不一定就能注意到,所有在編碼的過程要養成良好的習慣
構造單例的時候儘量別用Activity的引用;
靜態引用時注意應用物件的置空或者少用靜態引用;
使用靜態內部類+軟引用代替非靜態內部類;
及時取消廣播或者觀察者註冊;
耗時任務、屬性動畫在Activity銷燬時記得cancel;
檔案流、Cursor等資源及時關閉;
Activity銷燬時WebView的移除和銷燬。

螢幕適配:
首先明白DPI與Density的概念,今日頭條的核心原理就是
當前裝置螢幕總寬度(單位畫素)/設計圖總寬度(單位dp)=density
density的意思就是1dp佔當前裝置多少畫素, 只要density根據不同的裝置進行實時計算並做出改變,就能保證設計圖總寬度不變 ,我們只要保證每個裝置的螢幕總dp寬度不變,就能保證每個view在所有解析度的螢幕上的比例保持不變,從而完成等比例適配

系統中有一個方法叫applyDimension(在TypedValue類下面),無論你在佈局中填寫任何單位,最後都會被轉換為px,今日頭條適配預設只能以高或者寬來作為一個基準進行適配,進而避免佈局在寬高比例不一致的螢幕上出現變形的問題,小米8是(18:9的螢幕,在清單檔案中新增android:resizeableActivity=“true”),density=DPI/160

proguard:
壓縮:移除無用的方法,欄位,類,屬性
優化:移除無用的位元組碼命令
混淆:混淆定義的名稱,命名成無意義的名稱

工作原理:
需要理解entrypoint概念,一種標誌,可以在progurad過程中不會被處理的類或者方法,在進行壓縮的過程中從entrypoint尋找沒有被保護的類,將他們進行混淆處理

類載入機制:
都是通過BaseDexClassLoder來實現的
DexClassLoader:能夠載入未安裝的jar/apk/dex
PathClassLoader:只能載入系統中已經安裝過的apk

熱更新原理:
在BaseDexClassLoder中建立一個dexElements檔案的陣列,然後根據更新功能或者crash找到對應的類檔案,然後將修改後的類檔案打包成dex檔案放到這個dexelements的陣列的最前方,然後classloader在遍歷這個陣列的時候會優先載入最前方的一個dex檔案,這樣就完成了一個熱修復的流程

反射:在執行時或者這個類的函式、屬性、一些內部的資訊機制,通過反射可以在執行時例項化這個物件,並呼叫其中的方法
getclass invoke mothed
外掛化:
1、動態載入APK
大致流程就是通過dexclassload類載入器載入指定apk,再結合反射去獲取外掛apk中的代理方法或者onreate方法來進行動態載入apk
2、動態載入資源
通過AsetManager這個類用做的,通過反射來呼叫addAsetpath(隱藏api),然後在進行一系列的操作
3、程式碼載入
首先繫結到相關聯Activity的生命週期,通過生命週期的反射方法來進行相應的回撥

aidl :
一種ipc機制 ,android介面定義語言

機制:
1、.aidl字尾命名
2、支援八種基本資料型別、map、list、實現序列化的自定義資料型別
3、定向tag , in out inout in表示客戶端流向服務端 out表示服務端流向客戶端 inout 雙向流
4、明確導包。使用自定義資料型別的報名必須一致

記憶體優化:
1、在使用sevrcice完成任務時,儘量停止他
2、在ui不可見時候,釋放掉一些ui資源
3、在系統記憶體緊張的時候,儘可能釋放一些重要資源
4、避免濫用bitmap導致記憶體浪費,軟引用,解析度壓縮,reclcle釋放等等
5、使用針對記憶體優化過的資料容器
6、避免使用依賴注入的框架
7.使用zip對其apk
8、使用多程序,將佔用記憶體到的程序

ui卡頓:
1、人為在ui執行緒中做輕微耗時操作,導致ui卡頓
2、佈局layout過於複雜,無法再16ms中完成渲染
3、同一時間動畫執行次數過多,導致cpu和gpu負載過重
4、view過度繪製,導致某些畫素在同一幀的時間內被繪製多次,從而使cpu或gpu負載過重
5、view頻繁的觸發measure、layout,導致measue、layout累計耗時過多及整個view頻繁重新渲染
6、記憶體頻繁gc過多,導致暫時阻塞渲染操作
7、冗餘資源及邏輯導致資源和執行緩慢
8、anr 程式無響應

優化:
1、佈局優化(使用include,mage,viewstep等)
2、列表以及adapter優化
3、背景和圖片等記憶體分配優化
4、避免anr 不要在ui執行緒中進行耗時操作