Android面試題

Activity的啟動流程

  1. 點擊桌面App圖標,Launcher進程采用Binder IPC向system_server進程發(fā)起startActivity請求;
  2. system_server進程接收到請求后,向zygote進程發(fā)送創(chuàng)建進程的請求;
  3. Zygote進程fork出新的子進程,即App進程;
  4. App進程,通過Binder IPC向sytem_server進程發(fā)起attachApplication請求;
  5. system_server進程在收到請求后,進行一系列準備工作后,再通過binder IPC向App進 程發(fā)送scheduleLaunchActivity請求;
  6. App進程的binder線程(ApplicationThread)在收到請求后,通過handler向主線程發(fā)送LAUNCH_ACTIVITY消息;
  7. 主線程在收到Message后,通過發(fā)射機制創(chuàng)建目標Activity,并回調Activity.onCreate()等方法。

Activity四種啟動模式

  1. 標準模式(默認模式)android:launchMode="standard"
    每次啟動一個Activity就會創(chuàng)建一個新的實例。
  2. 棧頂復用模式:android:launchMode="singleTop"
    如果新Activity已經位于任務棧的棧頂,就不會重新創(chuàng)建,并回調onNewIntent()方法。
  3. 棧內復用模式:android:launchMode="singleTask"
    只要該Activity在一個任務棧中存在,都不會重新創(chuàng)建,并回調onNewIntent()方法
  4. 單實例模式: 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 四級緩存

  1. 一級緩存:兩個 ArrayList,一個存儲當前還在屏幕中的 ViewHolder,另一個存儲數據被更新的 ViewHolder
    final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
    ArrayList<ViewHolder> mChangedScrap = null;
  2. 二級緩存:ArrayList,默認大小為2,通常用來存儲預取的 ViewHolder,同時在回收 ViewHolder時,也可能會存儲一部分的 ViewHolder
    final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
  3. 三級緩存:ViewCacheExtension,自定義緩存【是空實現,一般都不會去實現它】
  4. 四級緩存: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域名解析

  1. 三次握手建立TCP連接
  2. 客戶端向服務器發(fā)送請求命令
  3. 客戶端發(fā)送請求頭信息
  4. 服務器應答 Http/1.1 200 OK
  5. 服務器返回相應頭信息
  6. 服務器向客戶端發(fā)送數據
  7. 服務器關閉TCP連接

談一下一次完整的https請求

  1. 客戶端發(fā)起https請求
  2. 服務器必須要有一套數字證書,可以自己制作,也可以向權威機構申請。這套證書其實就是一對公私鑰。
  3. 服務器將自己的數字證書(含有公鑰、證書的頒發(fā)機構等)發(fā)送給客戶端。
  4. 客戶端收到服務器端的數字證書之后,會對其進行驗證,主要驗證公鑰是否有效,比如頒發(fā)機構,過期時間等等。如果不通過,則彈出警告框。如果證書沒問題,則生成一個密鑰(對稱加密算法的密鑰,其實是一個隨機值),并且用證書的公鑰對這個隨機值加密。
  5. 客戶端會發(fā)起https中的第二個請求,將加密之后的客戶端密鑰(隨機值)發(fā)送給服務器。
  6. 服務器接收到客戶端發(fā)來的密鑰之后,會用自己的私鑰對其進行非對稱解密,解密之后得到客戶端密鑰,然后用客戶端密鑰對返回數據進行對稱加密,這樣數據就變成了密文。
  7. 服務器將加密后的密文返回給客戶端。
  8. 客戶端收到服務器發(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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。

相關閱讀更多精彩內容

  • 面試的時候總會遇到一些各種各樣的面試題,而且這些面試題很多都是關于平時容易疏忽的理論方面的,所以整理一份Andro...
    hydCoder閱讀 4,395評論 5 88
  • 2021年Android面試題匯總(中級)[http://m.itdecent.cn/p/c7b6c6851...
    Wocus閱讀 203,171評論 28 186
  • 1.跨進程通信的幾種方式Intent,比如撥打電話ContentProvider數據庫存儲數據Broadcast廣...
    方_f666閱讀 139評論 0 0
  • 表情是什么,我認為表情就是表現出來的情緒。表情可以傳達很多信息。高興了當然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,914評論 2 7
  • 16宿命:用概率思維提高你的勝算 以前的我是風險厭惡者,不喜歡去冒險,但是人生放棄了冒險,也就放棄了無數的可能。 ...
    yichen大刀閱讀 8,176評論 0 4

友情鏈接更多精彩內容