Activity的啟動流程
- 點擊桌面App圖標,Launcher進程采用Binder IPC向system_server進程發(fā)起startActivity請求;
- system_server進程接收到請求后,向zygote進程發(fā)送創(chuàng)建進程的請求;
- Zygote進程fork出新的子進程,即App進程;
- App進程,通過Binder IPC向sytem_server進程發(fā)起attachApplication請求;
- system_server進程在收到請求后,進行一系列準備工作后,再通過binder IPC向App進 程發(fā)送scheduleLaunchActivity請求;
- App進程的binder線程(ApplicationThread)在收到請求后,通過handler向主線程發(fā)送LAUNCH_ACTIVITY消息;
- 主線程在收到Message后,通過發(fā)射機制創(chuàng)建目標Activity,并回調Activity.onCreate()等方法。
Activity四種啟動模式
- 標準模式(默認模式)android:launchMode="standard"
每次啟動一個Activity就會創(chuàng)建一個新的實例。- 棧頂復用模式:android:launchMode="singleTop"
如果新Activity已經位于任務棧的棧頂,就不會重新創(chuàng)建,并回調onNewIntent()方法。- 棧內復用模式:android:launchMode="singleTask"
只要該Activity在一個任務棧中存在,都不會重新創(chuàng)建,并回調onNewIntent()方法- 單實例模式: android:launchMode="singleInstance"
具有此模式的Activity只能單獨位于一個任務棧中,且此任務棧中只有唯一一個實例。
Android中有哪幾種類型的動畫
- View動畫(View Animation)/補間動畫(Tween animation):對View進行平移、縮放、旋轉和透明度變化的動畫,不能真正的改變view的位置。應用如布局動畫、Activity切換動畫
- 逐幀動畫(Drawable Animation):是View動畫的一種,它會按照順序播放一組預先定義好的圖片
- 屬性動畫(Property Animation):對該類對象進行動畫操作,真正改變了對象的屬性
談談消息機制Hander
- 作用:跨線程通信。當子線程中進行耗時操作后需要更新UI時,通過Handler將有關UI的操作切換到主線程中執(zhí)行。
- Message(消息):需要被傳遞的消息,其中包含了消息ID,消息處理對象以及處理的數據等,由MessageQueue統一列隊,最終由Handler處理。
- MessageQueue(消息隊列):用來存放Handler發(fā)送過來的消息,內部通過單鏈表的數據結構來維護消息列表,等待Looper的抽取。
- Handler(處理者):負責Message的發(fā)送及處理。通過 Handler.sendMessage() 向消息池發(fā)送各種消息事件;通過 Handler.handleMessage() 處理相應的消息事件。
- Looper(輪詢器):通過Looper.loop()不斷地從MessageQueue中抽取Message,按分發(fā)機制將消息分發(fā)給目標處理者。
Handler導致的內存泄漏
- 泄漏原因:Message持有對Handler的引用,而非靜態(tài)內部類的Handler又隱式持有對外部類Activity的引用,使得引用關系會保持至消息得到處理,從而阻止了Activity的回收。
- 防止泄漏:
1、當外部類結束生命周期時清空消息隊列removeCallbacksAndMessages(null)
2、使用靜態(tài)內部類+WeakReference弱引用
主線程中Looper的輪詢死循環(huán)為何沒有阻塞主線程
Android是依靠事件驅動的,通過Loop.loop()不斷進行消息循環(huán),可以說Activity的生命周期都是運行在 Looper.loop()的控制之下,一旦退出消息循環(huán),應用也就退出了。而所謂的導致ANR多是因為某個事件在主線程中處理時間太耗時,因此只能說是對某個消息的處理阻塞了Looper.loop()
談談幾個常用的框架的實現原理
Glide框架
Glide.with(content).load(url).into(imageView) ,with綁定生命周期,load指定加載資源,into指明加載目標。
with:創(chuàng)建一個無UI的Fragment,通過添加的這個Fragment 感知 Activity 、Fragment 的生命周期,將RequestManager存入到之這個Fragment的LifeCycle,
通過Lifecycle在Fragment關鍵生命周期通知RequestManger進行相關的操作。
緩存機制:
1、活動緩存, 弱應用 HashMap,如果當前對應的圖片資源正在使用,則這個圖片會被Glide放入活動緩存。
Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
2、內存緩存, LruCache LinkedHasMap,如果圖片最近被加載過,并且當前沒有使用這個圖片,則會被放入內存中。
Map<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true);
3、磁盤緩存(DiskLruCache)緩存圖片到本地文件夾。
Retrofit框架
Retrofit通過動態(tài)代理,用MethodHandler完成接口方法。
Retrofit的MethodHandler通過RequestFactoryParser.parse解析,獲得接口方法的參數和注解的值,傳入到OkHttpCall,OkHttpCall生成okhttp3.Call完成Http請求并使用Converter解析數據回調。
Retrofit通過工廠設置CallAdapter和Converter,CallAdapter包裝轉換Call,Converter轉換(解析)服務器返回的數據、接口方法的注解參數。
OkHttp 是一個高效的 HTTP 客戶端,具有非常多的優(yōu)勢:
1、能夠高效的執(zhí)行 http,數據加載速度更快,更省流量
2、支持 GZIP 壓縮,提升速度,節(jié)省流量
3、緩存響應數據,避免了重復的網絡請求
4、使用簡單,支持同步阻塞調用和帶回調的異步調用
OkHttp流程:
1、采用責任鏈方式的攔截器,實現分成處理網絡請求,可更好的擴展自定義攔截器(采用GZIP壓縮,支持http緩存)
2、采用線程池(thread pool)和連接池(Socket pool)解決多并發(fā)問題,同時連接池支持多路復用(http2才支持,可以讓一個Socket同時發(fā)送多個網絡請求,內部自動維持順序.相比http只能一個一個發(fā)送,更能減少創(chuàng)建開銷))
3、底層采用socket和服務器進行連接.采用okio實現高效的io流讀寫
分化器 dispatcher:內部維護隊列與線程池,完成請求調配。
// 雙端隊列,支持首尾兩端 雙向開口可進可出
// 準備運行的異步隊列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
// 正在運行的異步
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
// 正在執(zhí)行的同步隊列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
攔截器:5個攔截器,完成整個請求過程
1、BridgeInterceptor(橋接攔截器):請求時,對必要的Header進行一些添加,接收響應時,移除必要的Header
2、RetryAndFollowUpInterceptor(重試與重定向攔截器):負責失敗重試以及重定向
3、CacheInterceptor(緩存攔截器):負責讀取緩存直接返回(根據請求的信息和緩存的響應的信息來判斷是否存在緩存可用)、更新緩存
4、ConnectInterceptor(連接攔截器):負責和服務器建立連接
5、CallServerInterceptor(請求服務器攔截器):完成HTTP協議報文的封裝和解析。
LeakCanary 原理
1、通過 registerActivityLifecycleCallbacks 監(jiān)聽Activity或者Fragment 銷毀時候的生命周期。
在Application.ActivityLifecycleCallbacks 的 onActivityDestroyed方法調用RefWatcher.watch()實現。
2、通過弱引用和引用隊列監(jiān)控對象是否被回收(弱引用和引用隊列ReferenceQueue聯合使用時,
如果弱引用持有的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。
即 KeyedWeakReference持有的Activity對象如果被垃圾回收,該對象就會加入到引用隊列queue)。
Android 創(chuàng)建多個進程,Android中的進程通信
通過在配置清單中給四大組件設置android:process屬性值,這樣我們就可以輕易開啟多進程模式。
Android中支持的多進程通信方式主要有以下幾種,它們之間各有優(yōu)缺點,可根據使用場景選擇選擇:
1、AIDL:功能強大,支持進程間一對多的實時并發(fā)通信,并可實現 RPC (遠程過程調用)。
2、Messenger:支持一對多的串行實時通信, AIDL 的簡化版本。
3、Bundle:四大組件的進程通信方式,只能傳輸 Bundle 支持的數據類型。
4、ContentProvider:強大的數據源訪問支持,主要支持 CRUD 操作,一對多的進程間數據共享,例如我們的應用訪問系統的通訊錄數據。
5、BroadcastReceiver:即廣播,但只能單向通信,接收者只能被動的接收消息。
6、文件共享:在非高并發(fā)情況下共享簡單的數據。
7、Socket:通過網絡傳輸數據。
談談 MVC、MVP、MVVM
- MVC:
模型層(Model) :針對業(yè)務模型,建立數據結構和相關的類,它主要負責網絡請求,數據庫處理,I/O的操作。
視圖層(View) :對應于xml布局文件和java代碼動態(tài)view部分。
控制層(Controller) :控制層是由Activity/Fragment來承擔的,但是因為XML視圖功能太弱,所以Activity/Fragment既要負責視圖的顯示又要加入控制邏輯,承擔的功能過多,大項目就會遇到耦合過重,Activity/Fragment類過大等問題。- MVP:為了解決MVC耦合過重的問題,MVP的核心思想就是提供一個Presenter將視圖邏輯和業(yè)務邏輯相分離,達到解耦的目的。
但是隨著業(yè)務邏輯的增加,一個頁面可能會非常復雜,UI的改變是非常多,會有非常多的case,這樣就會造成View的接口會很龐大。- MVVM:使用ViewModel代替Presenter,實現數據與View的雙向綁定,這樣就省去了很多在View層中寫很多case的情況,只需要改變數據就行。
RecycleView 四級緩存
- 一級緩存:兩個 ArrayList,一個存儲當前還在屏幕中的 ViewHolder,另一個存儲數據被更新的 ViewHolder
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;- 二級緩存:ArrayList,默認大小為2,通常用來存儲預取的 ViewHolder,同時在回收 ViewHolder時,也可能會存儲一部分的 ViewHolder
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();- 三級緩存:ViewCacheExtension,自定義緩存【是空實現,一般都不會去實現它】
- 四級緩存:RecycledViewPool,緩存從二級緩存中移除的 ViewHolder。采用 SparseArray 保存其內部靜態(tài)類 ScrapData,ScrapData 采用 ArrayList 存儲 ViewHolder,ArrayList 大小默認為5,可動態(tài)改變。ViewHolder 從二級緩存移除,加入四級緩存前,會將數據全部清除,根據 ViewType 存儲到 SparseArray 中。ViewHolder 服用時會重寫 onBindViewHolder 方法填充數據。
AIDL簡單講解一下。底層實現的是什么機制?
AIDL是進程間通信的一種方式,底層實現是通過Binder機制來實現的。Binder機制底層又是通過mmap內存映射原理來實現的,內存分為用戶空間和內核空間,Binder機制通過一次一次的數據拷貝來傳遞數據。
Binder機制了解
每個進程的內存空間都分為用戶空間和內核空間,用戶空間和內核空間之間的通信需要native層的方法來實現,方法叫copy_form_user()。進程A的內核空間和進程B的用戶空間通過Linux的一個mmap方法來創(chuàng)建出一個共享的物理內存地址,然后進程A的用戶空間和進程B的用戶空間進行通信的話,就只需要進程A的用戶空間往進程A的內核空間進行一次數據拷貝(copy_from_user),進程B的用戶空間與進程A的內核空間通過mmap內存映射的方式,就能在共享的物理內存中獲取到進程A拷貝的數據,就實現了通信。
AsyncTask原理
1 、AsyncTask中有兩個線程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一個Handler(InternalHandler),其中線程池SerialExecutor用于任務的排隊,而線程池THREAD_POOL_EXECUTOR用于真正地執(zhí)行任務,InternalHandler用于將執(zhí)行環(huán)境從線程池切換到主線程。
2、InternalHandler是一個靜態(tài)的Handler對象,為了能夠將執(zhí)行環(huán)境切換到主線程,這就要求sHandler這個對象必須在主線程創(chuàng)建。由于靜態(tài)成員會在加載類的時候進行初始化,因此這就變相要求AsyncTask的類必須在主線程中加載,否則同一個進程中的AsyncTask都將無法正常工作。
談談對RxJava的理解
RxJava是基于響應式編程,基于事件流、實現異步操(類似于Android中的AsyncTask、Handler作用)作的庫,基于事件流的鏈式調用,使得RxJava邏輯簡潔、使用簡單。RxJava原理是基于一種擴展的觀察者模式,有四種角色:被觀察者Observable 觀察者Observer 訂閱subscribe 事件Event。RxJava原理可總結為:被觀察者Observable通過訂閱(subscribe)按順序發(fā)送事件(Emitter)給觀察者(Observer), 觀察者按順序接收事件、作出相應的響應動作。
Android各版本新特性
1、Android5.0新特性:MaterialDesign設計風格、支持64位ART虛擬機、通知詳情可以用戶自己設計。
2、Android6.0新特性:動態(tài)權限管理、支持快速充電的切換、支持文件夾拖拽應用、相機新增專業(yè)模式。
3、Android7.0新特性:多窗口支持、V2簽名、增強的Java8語言模式、夜間模式。
4、Android8.0(O)新特性:優(yōu)化通知、畫中畫模式、自動填充框架、系統優(yōu)化。
5、Android9.0(P)新特性:室內WIFI定位、“劉?!逼聊恢С帧踩鰪?。
6、Android10.0(Q)新特性:用戶存儲權限的變更、用戶的定位權限的變更。
談一下一次完整的http請求
首先進行DNS域名解析
- 三次握手建立TCP連接
- 客戶端向服務器發(fā)送請求命令
- 客戶端發(fā)送請求頭信息
- 服務器應答 Http/1.1 200 OK
- 服務器返回相應頭信息
- 服務器向客戶端發(fā)送數據
- 服務器關閉TCP連接
談一下一次完整的https請求
- 客戶端發(fā)起https請求
- 服務器必須要有一套數字證書,可以自己制作,也可以向權威機構申請。這套證書其實就是一對公私鑰。
- 服務器將自己的數字證書(含有公鑰、證書的頒發(fā)機構等)發(fā)送給客戶端。
- 客戶端收到服務器端的數字證書之后,會對其進行驗證,主要驗證公鑰是否有效,比如頒發(fā)機構,過期時間等等。如果不通過,則彈出警告框。如果證書沒問題,則生成一個密鑰(對稱加密算法的密鑰,其實是一個隨機值),并且用證書的公鑰對這個隨機值加密。
- 客戶端會發(fā)起https中的第二個請求,將加密之后的客戶端密鑰(隨機值)發(fā)送給服務器。
- 服務器接收到客戶端發(fā)來的密鑰之后,會用自己的私鑰對其進行非對稱解密,解密之后得到客戶端密鑰,然后用客戶端密鑰對返回數據進行對稱加密,這樣數據就變成了密文。
- 服務器將加密后的密文返回給客戶端。
- 客戶端收到服務器發(fā)返回的密文,用自己的密鑰(客戶端密鑰)對其進行對稱解密,得到服務器返回的數據。
布局性能優(yōu)化之Include、ViewStub、Merge
- include標簽常用于將布局中的公共部分提取出來供其他layout共用,以實現布局模塊化,也是平常我們設計布局時用的最多的。
- ViewStub就是一個寬高都為0的一個View,它默認是不可見的,只有通過調用setVisibility函數或者Inflate函數才會將其要裝載的目標布局給加載出來,從而達到延遲加載的效果,這個要被加載的布局通過android:layout屬性來設置。
- 在一個布局中包含一個布局時,merge標簽有助于消除視圖層次結構中的冗余視圖組。如果是merge標簽,那么直接將其中的子元素添加到merge標簽parent中,這樣就保證了不會引入額外的層級。
非UI線程是否可以刷新UI
非UI線程是可以刷新UI的,前提是它要擁有自己的ViewRoot。如果想直接創(chuàng)建ViewRoot實例,你會發(fā)現找不到這個類。但是可以通過WindowManager。
因為通常的子線程更新UI的報錯是ViewRootImpl類的checkThread函數拋出的,而ViewRootImpl在onResume中才會創(chuàng)建,所以在這之前子線程更新UI是不會報錯的
class NonUiThread extends Thread{
@Override
public void run() {
Looper.prepare();
TextView tx = new TextView(MainActivity.this);
tx.setText("non-UiThread update textview");
WindowManager windowManager = MainActivity.this.getWindowManager();
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
200, 200, 200, 200, WindowManager.LayoutParams.FIRST_SUB_WINDOW,
WindowManager.LayoutParams.TYPE_TOAST,PixelFormat.OPAQUE);
windowManager.addView(tx, params);
Looper.loop();
}
}
Android ANR
Application Not Responding,應用無響應。
發(fā)生場景:
- Service Timeout:比如前臺服務在20s內未執(zhí)行完成。
- BroadcastQueue Timeout:比如前臺廣播在10s內未執(zhí)行完成。
- ContentProvider Timeout:內容提供者,在publish過超時10s。
- InputDispatching Timeout: 輸入事件分發(fā)超時5s,包括按鍵和觸摸事件。
導致ANR無響應的常見原因 :
- 主線程阻塞 (避免死鎖的出現,使用子線程來處理耗時操作或阻塞任務)
- IO阻塞 (文件讀寫或數據庫操作放在子線程異步操作)
- CPU大量計算,內存不足
定位分析:
- ActivityManager( 996): ANR in ... ANR發(fā)生在哪個應用
- PID:8625: 產生ANR應用的線程號
- ActivityManager( 996): Reason: Input dispatching timed out ANR產生的原因
- Load: 3.46 / 2.71 / 1.24 CPU前1分鐘、5分鐘、15分鐘的CPU平均負載
- ActivityManager( 996): CPU usage from 94ms to 643ms later