剛剛過去2019,新的一年2020年。都說衣不如新人不如故,技術是學新不學舊的?但是舊的知識不鞏固,根基不固很容易在面試或者實戰遇到很大的問題的
如下知識點PDF版後續可見
java
更多面試內容等等
(更多完整項目下載。未完待續。源碼。圖文知識後續上傳github。)
(VX:mm14525201314)
https://github.com/xiangjiana...android
參考答案:
使用線程池的好處是減小在建立和銷燬線程上所花的時間以及系統資源的開銷,解決資源不足的問題。若是不使用線程池,有可能形成系統建立大量同類線程而致使消耗完內存或則「過分切換」的問題,概括總結就是git
Android 中的線程池都是直接或間接經過配置
ThreadPoolExecutor 來實現不一樣特性的線程池.Android 中最多見的類具備不一樣特性的線程池分別爲:github
newCachedThreadPool
: 只有非核心線程,最大線程數很是大,全部線程都活動時會爲新任務建立新線程,不然會利用空閒線程 ( 60s 空閒時間,過了就會被回收,因此線程池中有 0 個線程的可能 )來處理任務.優勢: 任何任務都會被當即執行(任務隊列SynchronousQuue
至關於一個空集合);比較適合執行大量的耗時較少的任務.面試
newFixedThreadPool
: 只有核心線程,而且數量固定的,全部線程都活動時,由於隊列沒有限制大小,新任務會等待執行,當線程池空閒時不會釋放工做線程,還會佔用必定的系統資源。優勢: 更快的響應外界請求算法
newScheduledThreadPool
: 核心線程數固定,非核心線程(閒着沒活幹會被當即回收數)沒有限制.優勢: 執行定時任務以及有固定週期的重複任務編程
newSingleThreadExecutor
: 只有一個核心線程,確保全部的任務都在同一線程中按序完成優勢: 不須要處理線程同步的問題緩存
經過源碼能夠了解到上面的四種線程池實際上仍是利用ThreadPoolExecutor
類實現的安全
//詳細介紹課參考Executors.java類 public static ExecutorService newCachedThreadpool () { return new ThreadPoolExecutor (0,Integer.MAX_VALUE 60L,TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } ThreadPoolExecutor(int corepoolSize,int maxmumpoolSize, long keepAliveTime,TimeUnit unit, Blockingqueue<Runnable>workqueue,RejectedExecutionHandler handler
參考回答:服務器
AsyncTask
: 底層封裝了線程池和 Handler,便於執行後臺任務以及在子線程中進行 UI 操做。HandlerThread
: 一種具備消息循環的線程,其內部可以使用Handler。IntentService
: 是一種異步、會自動中止的服務,內部採用HandlerThread
。參考回答:
AsyncTask
中有兩個線程池(SerialExecutor
和THREAD_POOL_EXECUTOR)和一個 Handler(InternalHandler
),其中線程池 SerialExecutor
用於任務的排隊,而線程池THREAD_POOL_EXECUTOR 用於真正地執行任務,InternalHandler
用於將執行環境從線程池切換到主線程。AsyncTask
的類必須在主線程中加載,不然同一個進程中的 AsyncTask
都將沒法正常工做。IntentService
可用於執行後臺耗時的任務,當任務執行完成後會自動中止,同時因爲 IntentService
是服務的緣由,不一樣於普通 Service,IntentService
可自動建立子線程來執行任務,這致使它的優先級比單純的線程要高,不容易被系統殺死,因此IntentService
比較適合執行一些高優先級的後臺任務。
參考回答:
onCreate()
中建立,在 onDestroy()
中銷燬。因此,在 Service 中建立的 Thread,適合長期執行一些獨立於 APP 的後臺任務,比較常見的就是:在 Service 中保持與服務器端的長鏈接。參考回答: ThreadPoolExecutor
執行任務時會遵循以下規則
ThreadPoolExecutor
會調用RejectedExecutionHandler
的rejectedExecution
方法來通知調用者。參考回答:
HandlerThread
,Android 中沒有對 Java 中的 Thread 進行任何封裝,而是提供了一個繼承自 Thread 的類 HandlerThread
類,這個類對 Java的 Thread 作了不少便利的封裝。HandlerThread
繼承於Thread,因此它本質就是個 Thread。與普通 Thread 的差異就在於,它在內部直接實現了 Looper
的實現,這是 Handler 消息機制必不可少的。有了本身的 looper
,可讓咱們在本身的線程中分發和處理消息。若是不用 HandlerThread
的話,須要手動去調用 Looper.prepare()
和 Looper.loop()
這些方法。參考回答:ThreadLocal
是一個關於建立線程局部變量的類。使用場景以下所示:
當須要使用多線程時,有個變量恰巧不須要共享,此時就沒必要使用 synchronized
這麼麻煩的關鍵字來鎖住,每一個線程都至關於在堆內存中開闢一個空間,線程中帶有對共享變量的緩衝區,經過緩衝區將堆內存中的共享變量進行讀取和操做,ThreadLocal
至關於線程內的內存,一個局部變量。每次能夠對線程自身的數據讀取和操做,並不須要經過緩衝區與 主內存中的變量進行交互。並不會像 synchronized
那樣修改主內存的數據,再將主內存的數據複製到線程內的工做內存。ThreadLocal
可讓線程獨佔資源,存儲於線程內部,避免線程堵塞形成 CPU 吞吐降低。
在每一個 Thread 中包含一個 ThreadLocalMap
,ThreadLocalMap
的 key 是 ThreadLocal
的對象,value 是獨享數據。
參考回答:
多線程的優勢:
多線程的缺點:
綜上得出,多線程不必定能提升效率,在內存空間緊張的狀況下反而是一種負擔,所以在平常開發中,應儘可能
參考回答:
//OkHttp例子 private static volatile OkHttpHelper SInstance; public static OkHttpHelper getInstance() { if (sInstance =null) { synchronized (OkHttpHelper.class) { if (sInstance =null) { sInstance = new OkHttpHelper(); } } } return sInstance; }
參考回答:
notifyAll()
方法就喚醒所有的線程。注意:調用notify()
方法後並不會當即釋放 object 鎖,會等待該線程執行完畢後釋放 Object 鎖。參考回答:
BroadcastReceiver
、20s 內未結束 ServiceRunnable
接口、使用AsyncTask
IntentService
、HandlerThread
等參考回答:
負責跨線程通訊,這是由於在主線程不能作耗時操做,而子線程不能更新 UI,因此當子線程中進行耗時操做後須要更新 UI時,經過 Handler 將有關 UI 的操做切換到主線程中執行。
具體分爲四大要素:
MessageQueue
(消息隊列): 負責消息的存儲與管理,負責管理由 Handler 發送過來的 Message。讀取會自動刪除消息,單鏈表維護,插入和刪除上有優點。在其 next()
方法中會無限循環,不斷判斷是否有消息,有就返回這條消息並移除。Handler.sendMessage()
)和處理相應消息事件(Handler.handleMessage()
),按照先進先出執行,內部使用的是單鏈表的結構。Looper
(消息池): 負責關聯線程以及消息的分發,在該線程下從 MessageQueue
獲取 Message,分發給Handler,Looper
建立的時候會建立一個MessageQueue
,調用 loop()方法的時候消息循環開始,其中會不斷調用 messageQueue
的 next()
方法,當有消息就處理,不然阻塞在 messageQueue
的next()
方法中。當 Looper
的 quit()
被調用的時候會調用messageQueue
的 quit()
,此時 next()
會返回 null,而後 loop()
方法也就跟着退出。
流程:
MessageQueue.enqueueMessage
在消息隊列中添加一條 Message。Looper.loop()
開啓消息循環不斷輪詢調用MessageQueue.next()
,取得對應的 Message 而且經過 Handler.dispatchMessage
傳遞給 Handler,最終調用 Handler.handlerMessage
處理消息。參考回答:
Looper
,一個 MessageQueen
,能夠有多個 HandlerThread(1)
:Looper(1)
: MessageQueue(1)
: Handler(N)
參考回答:
SoftReference
): 若是一個對象只具備軟引用,則內存空間充足時,垃圾回收器就不會回收它;若是內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就能夠一直被程序使用。WeakReference
): 若是一個對象只具備弱引用,那麼在垃圾回收器線程掃描的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。
參考回答:
泄露緣由:Handler 容許咱們發送延時消息,若是在延時期間用戶關閉了 Activity,那麼該 Activity 會泄露。 這個泄露是由於 Message 會持有 Handler,而又由於 Java 的特性,內部類會持有外部類,使得 Activity 會被Handler 持有,這樣最終就致使 Activity 泄露。
解決方案:將 Handler 定義成靜態的內部類,在內部持有Activity 的弱引用,並在 Acitivity
的 onDestroy()
中調用 handler.removeCallbacksAndMessages(null)
及時移除全部消息。
參考回答:
Android 的 UI 控件不是線程安全的,若是在多線程中併發訪問可能會致使 UI 控件處於不可預期的狀態
這時你可能會問爲什麼系統不對 UI 控件的訪問加上鎖機制呢?由於
參考回答:
Looer.loop()
方法可能會引發主線程的阻塞,但只要它的消息循環沒有被阻塞,能一直處理事件就不會產生 ANR 異常。參考回答:
若是隊列中只有這個消息,那麼消息不會被髮送,而是計算到時喚醒的時間,先將 Looper 阻塞,到時間就喚醒它。但若是此時要加入新消息,該消息隊列的對頭跟 delay 時間相比更長,則插入到頭部,按照觸發時間進行排序,隊頭的時間最小、隊尾的時間最大
參考回答:
不能夠,由於在主線程中,Activity 內部包含一個 Looper 對象,它會自動管理 Looper
,處理子線程中發送過來的消息。而對於子線程而言,沒有任何對象幫助咱們維護 Looper
對象,因此須要咱們本身手動維護。因此要在子線程開啓 Handler 要先建立 Looper
,並開啓 Looper
循環
//代碼示例 new Thread(new Runnable() { @Override public void run() { looper.prepare(); new Handler() { @Override public void handlerMessage(Message msg) { super.handleMessage(msg); } looper.loop(); } }).start();
參考回答: 能夠經過三種方法建立:
Message m = new Message
Message m = Message.obtain
Message m = mHandler.obtainMessage()
後二者效果更好,由於 Android 默認的消息池中消息數量是 10,然後
二者是直接在消息池中取出一個 Message 實例,這樣作就能夠避免多
生成 Message 實例。
參考回答:
參考回答:
AndroidMenifest
中給四大組件指定屬性android:process 開啓多進程模式參考回答:
全部運行在不一樣進程的四大組件(Activity、Service、Receiver丶ContentProvider)共享數據都會失敗,這是因爲 Android 爲每一個應用分配了獨立的虛擬機,不一樣的虛擬機在內存分配上有不一樣的地址空間,這會致使在不一樣的虛擬機中訪問同一個類的對象會產生多份副本。好比經常使用例子( 經過開啓多進程獲取更大內存空間、兩個或則多個應用之間共享數據、微信全家桶)
通常來講,使用多進程通訊會形成以下幾方面的問題
SharedPreferences
的可靠性降低: 這是由於 Sp 不支持兩個進程併發進行讀寫,有必定概率致使數據丟失參考回答:
與 Linux 上傳統的 IPC 機制,好比 System V,Socket 相比,Binder 好在哪呢?
傳輸效率高、可操做性強: 傳輸效率主要影響因素是內存拷貝的次數,拷貝次數越少,傳輸速率越高。從 Android進程架構角度分析:對於消息隊列、Socket 和管道來講,數據先從發送方的緩存區拷貝到內核開闢的緩存區中,再從內核緩存區拷貝到接收方的緩存區,一共兩次拷貝,如圖:
而對於 Binder 來講,數據從發送方的緩存區拷貝到內核的緩存區,而接收方的緩存區與內核的緩存區是映射到同一塊物理地址的,節省了一次數據拷貝的過程,如圖:
因爲共享內存操做複雜,綜合來看,Binder 的傳輸效率是最好的。
實現 C/S 架構方便: Linux 的衆 IPC 方式除了 Socket 之外都不是基於 C/S 架構,而 Socket 主要用於網絡間的通訊且傳輸效率較低。Binder 基於 C/S 架構 ,Server 端與Client 端相對獨立,穩定性較好。
安全性高: 傳統 Linux IPC 的接收方沒法得到對方進程可靠的 UID/PID,從而沒法鑑別對方身份;而 Binder 機制爲每一個進程分配了 UID/PID 且在 Binder 通訊時會根據UID/PID 進行有效性檢測。
參考回答:
Linux 系統將一個進程分爲 用戶空間和 內核空間。對於進程之間來講,用戶空間的數據不可共享,內核空間的數據可共享,爲了保證安全性和獨立性,一個進程不能直接操做或者訪問另外一個進程,即 Android 的進程是相互獨立、隔離的,這就須要跨進程之間的數據通訊方式
一次完整的 Binder IPC 通訊過程一般是這樣:
copyfromuser()
將數據 copy 到內核中的內核緩存區,因爲內核緩存區和接收進程的用戶空間存在內存映射,所以也就至關於把數據發送到了接收進程的用戶空間,這樣便完成了一次進程間的通訊。
參考回答:
參考回答:
參考回答:
AIDL(Android Interface Definition Language,Android接口定義語言):若是在一個進程中要調用另外一個進程中對象的方法,可以使用 AIDL 生成可序列化的參數,AIDL 會生成一個服務端對象的代理類,經過它客戶端實現間接調用服務端對象的方法
AIDL 的本質是系統提供了一套可快速實現 Binder 的工具。關鍵類和方法:
Interface
。asInterface()
: 客戶端調用,將服務端的返回的Binder 對象,轉換成客戶端所須要的 AIDL 接口類型對象。若是客戶端和服務端位於統一進程,則直接返回 Stub 對象自己,不然返回系統封裝後的Stub.proxy 對象asBinder()
:根據當前調用狀況返回代理 Proxy 的Binder 對象。onTransact()
: 運行服務端的 Binder 線程池中,當客戶端發起跨進程請求時,遠程請求會經過系統底層封裝後交由此方法來處理。transact()
: 運行在客戶端,當客戶端發起遠程請求的同時將當前線程掛起。以後調用服務端的onTransact()
直到遠程請求返回,當前線程才繼續執行。當有多個業務模塊都須要 AIDL 來進行 IPC,此時須要爲每一個模塊建立特定的 aidl 文件,那麼相應的 Service 就會不少。必然會出現系統資源耗費嚴重、應用過分重量級的問題。解決辦法是創建 Binder 鏈接池,即將每一個業務模塊的Binder 請求統一轉發到一個遠程 Service 中去執行,從而避免重複建立 Service。
工做原理: 每一個業務模塊建立本身的 AIDL 接口並實現此接口,而後向服務端提供本身的惟一標識和其對應的 Binder 對象。服務端只須要一個 Service,服務器提供一個 queryBinder
接口,它會根據業務模塊的特徵來返回相應的 Binder 對象,不一樣的業務模塊拿到所需的 Binder 對象後就可進行遠程方法的調用了
查看完整的PDF版
(更多完整項目下載。未完待續。源碼。圖文知識後續上傳github。)
能夠聯繫我獲取完整PDF
https://github.com/xiangjiana/Android-MS
(VX:mm14525201314)