轉載自:android阿里面試題錦集
前幾天突然就經歷了阿里Android實習內推的電面,感覺有好多以前看過的東西都忘記了,然後又複習了一下,找了很多阿里的面經總結了一下,希望對大家有幫助,下面的知識點大多出自 android開發藝術探索,而且很多都是我自己的總結,方便自己記憶,如果想深入可以去看看這本書。
1.安卓事件分發機制,請詳細說下整個流程
2.安卓view繪製機制和加載過程,請詳細說下整個流程
- 1.ViewRootImpl會調用performTraversals(),其內部會調用performMeasure()、performLayout、performDraw()。
- 2.performMeasure()會調用最外層的ViewGroup的measure()-->onMeasure(),ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),這之中會遍歷子View然後循環調用measureChild()這之中會用getChildMeasureSpec()+父View的MeasureSpec+子View的LayoutParam一起獲取本View的MeasureSpec,然後調用子View的measure()到View的onMeasure()-->setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize()默認返回measureSpec的測量數值,所以繼承View進行自定義的wrap_content需要重寫。
- 3.performLayout()會調用最外層的ViewGroup的layout(l,t,r,b),本View在其中使用setFrame()設置本View的四個頂點位置。在onLayout(抽象方法)中確定子View的位置,如LinearLayout會遍歷子View,循環調用setChildFrame()-->子View.layout()。
- 4.performDraw()會調用最外層ViewGroup的draw():其中會先後調用background.draw()(繪製背景)、onDraw()(繪製自己)、dispatchDraw()(繪製子View)、onDrawScrollBars()(繪製裝飾)。
- 5.MeasureSpec由2位SpecMode(UNSPECIFIED、EXACTLY(對應精確值和match_parent)、AT_MOST(對應warp_content))和30位SpecSize組成一個int,DecorView的MeasureSpec由窗口大小和其LayoutParams決定,其他View由父View的MeasureSpec和本View的LayoutParams決定。ViewGroup中有getChildMeasureSpec()來獲取子View的MeasureSpec。
- 6.三種方式獲取measure()後的寬高:
- 1.Activity#onWindowFocusChange()中調用獲取
- 2.view.post(Runnable)將獲取的代碼投遞到消息隊列的尾部。
- 3.ViewTreeObservable.
3.activty的加載過程 請詳細介紹下:
- 1.Activity中最終到startActivityForResult()(mMainThread.getApplicationThread()傳入了一個ApplicationThread檢查APT)
->Instrumentation#execStartActivity()和checkStartActivityResult()(這是在啓動了Activity之後判斷Activity是否啓動成功,例如沒有在AM中註冊那麼就會報錯)
->ActivityManagerNative.getDefault().startActivity()(類似AIDL,實現了IAM,實際是由遠端的AMS實現startActivity())
->ActivityStackSupervisor#startActivityMayWait()
->ActivityStack#resumeTopActivityInnerLocked
->ActivityStackSupervisor#realStartActivityLocked()(在這裏調用APT的scheduleLaunchActivity,也是AIDL,不過是在遠端調起了本進程Application線程)
->ApplicationThread#scheduleLaunchActivity()(這是本進程的一個線程,用於作爲Service端來接受AMS client端的調起)
->ActivityThread#handleLaunchActivity()(接收內部類H的消息,ApplicationThread線程發送LAUNCH_ACTIVITY消息給H)
->最終在ActivityThread#performLaunchActivity()中實現Activity的啓動完成了以下幾件事: - 2.從傳入的ActivityClientRecord中獲取待啓動的Activity的組件信息
- 3.創建類加載器,使用Instrumentation#newActivity()加載Activity對象
- 4.調用LoadedApk.makeApplication方法嘗試創建Application,由於單例所以不會重複創建。
- 5.創建Context的實現類ContextImpl對象,並通過Activity#attach()完成數據初始化和Context建立聯繫,因爲Activity是Context的橋接類,
最後就是創建和關聯window,讓Window接收的事件傳給Activity,在Window的創建過程中會調用ViewRootImpl的performTraversals()初始化View。 - 6.Instrumentation#callActivityOnCreate()->Activity#performCreate()->Activity#onCreate().onCreate()中會通過Activity#setContentView()調用PhoneWindow的setContentView()
更新界面。
4.Activity的啓動模式:
- 1.standard:默認標準模式,每啓動一個都會創建一個實例,
- 2.singleTop:棧頂複用,如果在棧頂就調用onNewIntent複用,從onResume()開始
- 3.singleTask:棧內複用,本棧內只要用該類型Activity就會調到棧頂複用,從onResume()開始
- 4.singleInstance:單例模式,除了3中特性,系統會單獨給該Activity創建一個棧,
5.Activity緩存方法:
- 1.配置改變導致Activity被殺死,橫屏變豎屏:在onStop之前會調用onSaveInstanceState()保存數據在重建Activity之後,會在onStart()之後調用onRestoreInstanceState(),並把保存下來的Bundle傳給onCreate()和它會默認重建Activity當前的視圖,我們可以在onCreate()中,回覆自己的數據。
- 2.內存不足殺掉Activity,優先級分別是:前臺可見,可見非前臺,後臺。
6.Service的生命週期,兩種啓動方法,有什麼區別:
- 1.context.startService() ->onCreate()- >onStart()->Service running-->(如果調用context.stopService() )->onDestroy() ->Service shut down
- 1.如果Service還沒有運行,則調用onCreate()然後調用onStart();
- 2.如果Service已經運行,則只調用onStart(),所以一個Service的onStart方法可能會重複調用多次。
- 3.調用stopService的時候直接onDestroy,
- 4.如果是調用者自己直接退出而沒有調用stopService的話,Service會一直在後臺運行。該Service的調用者再啓動起來後可以通過stopService關閉Service。
- 2.context.bindService()->onCreate()->onBind()->Service running-->onUnbind() -> onDestroy() ->Service stop
- 1.onBind將返回給客戶端一個IBind接口實例,IBind允許客戶端回調服務的方法,比如得到Service運行的狀態或其他操作。
- 2.這個時候會把調用者和Service綁定在一起,Context退出了,Service就會調用onUnbind->onDestroy相應退出。
- 3.所以調用bindService的生命週期爲:onCreate --> onBind(只一次,不可多次綁定) --> onUnbind --> onDestory。
7.怎麼保證service不被殺死
- 1.提升service優先級
- 2.提升service進程優先級
- 3.onDestroy方法裏重啓service
8.廣播的兩種註冊方法,有什麼區別。
- 1.靜態註冊:如果有廣播信息來,你寫的廣播接收器同樣的能接受到,比如系統的一些廣播。
- 2.動態註冊:當應用程序結束了,廣播自然就沒有了,一些自己定義的廣播
9.Intent可以傳遞哪些數據類型
- 1.Serializable
- 2.charsequence: 主要用來傳遞String,char等
- 3.parcelable
- 4.Bundle
10.Json有什麼優劣勢
- 1.JSON的速度要遠遠快於XML
- 2.JSON相對於XML來講,數據的體積小
- 3.JSON對數據的描述性比XML較差
11.動畫有哪幾類,各有什麼特點:
- 1.動畫的基本原理:其實就是利用插值器和估值器,來計算出各個時刻View的屬性,然後通過改變View的屬性來,實現View的動畫效果。
- 2.View動畫:只是影像變化,view的實際位置還在原來的地方。
- 3.幀動畫是在xml中定義好一系列圖片之後,使用AnimationDrawable來播放的動畫。
- 4.View的屬性動畫:
- 1.插值器:作用是根據時間的流逝的百分比來計算屬性改變的百分比
- 2.估值器:在1的基礎上由這個東西來計算出屬性到底變化了多少數值的類
12.Handler、Loop消息隊列模型,各部分的作用。
- 1.MessageQueue:讀取會自動刪除消息,單鏈表維護,在插入和刪除上有優勢。在其next()中會無限循環,不斷判斷是否有消息,有就返回這條消息並移除。
- 2.Looper:Looper創建的時候會創建一個MessageQueue,調用loop()方法的時候消息循環開始,loop()也是一個死循環,會不斷調用messageQueue的next(),當有消息就處理,否則阻塞在messageQueue的next()中。當Looper的quit()被調用的時候會調用messageQueue的quit(),此時next()會返回null,然後loop()方法也跟着退出。
- 3.Handler:在主線程構造一個Handler,然後在其他線程調用sendMessage(),此時主線程的MessageQueue中會插入一條message,然後被Looper使用。
- 4.系統的主線程在ActivityThread的main()爲入口開啓主線程,其中定義了內部類Activity.H定義了一系列消息類型,包含四大組件的啓動停止。
13. 怎樣退出終止App:自己設置一個Activity的棧,然後一個個finish()。
14. Android IPC:Binder原理
- 1.在Activity和Service進行通訊的時候,用到了Binder。
- 1.當屬於同個進程我們可以繼承Binder然後在Activity中對Service進行操作
- 2.當不屬於同個進程,那麼要用到AIDL讓系統給我們創建一個Binder,然後在Activity中對遠端的Service進行操作。
- 2.系統給我們生成的Binder:
- 1.Stub類中有:接口方法的id,有該Binder的標識,有asInterface(IBinder)(讓我們在Activity中獲取實現了Binder的接口,接口的實現在Service裏,同進程時候返回Stub否則返回Proxy),有onTransact()這個方法是在不同進程的時候讓Proxy在Activity進行遠端調用實現Activity操作Service
- 2.Proxy類是代理,在Activity端,其中有:IBinder mRemote(這就是遠端的Binder),兩個接口的實現方法不過是代理最終還是要在遠端的onTransact()中進行實際操作。
- 3.哪一端的Binder是副本,該端就可以被另一端進行操作,因爲Binder本體在定義的時候可以操作本端的東西。所以可以在Activity端傳入本端的Binder,讓Service端對其進行操作稱爲Listener,可以用RemoteCallbackList這個容器來裝Listener,防止Listener因爲經歷過序列化而產生的問題。
- 4.當Activity端向遠端進行調用的時候,當前線程會掛起,當方法處理完畢纔會喚醒。
- 5.如果一個AIDL就用一個Service太奢侈,所以可以使用Binder池的方式,建立一個AIDL其中的方法是返回IBinder,然後根據方法中傳入的參數返回具體的AIDL。
- 6.IPC的方式有:Bundle(在Intent啓動的時候傳入,不過是一次性的),文件共享(對於SharedPreference是特例,因爲其在內存中會有緩存),使用Messenger(其底層用的也是AIDL,同理要操作哪端,就在哪端定義Messenger),AIDL,ContentProvider(在本進程中繼承實現一個ContentProvider,在增刪改查方法中調用本進程的SQLite,在其他進程中查詢),Socket
15.android的優化
16.一個singleton如何實現線程的同步問題
- 1.單例類確保自己只有一個實例(構造函數私有:不被外部實例化,也不被繼承)。
- 2.單例類必須自己創建自己的實例。
- 3.單例類必須爲其他對象提供唯一的實例。
17.android重要術語解釋
- 1.ActivityManagerServices,簡稱AMS,服務端對象,負責系統中所有Activity的生命週期
- 2.ActivityThread,App的真正入口。當開啓App之後,會調用main()開始運行,開啓消息循環隊列,這就是傳說中的UI線程或者叫主線程。與ActivityManagerServices配合,一起完成Activity的管理工作
- 3.ApplicationThread,用來實現ActivityManagerService與ActivityThread之間的交互。在ActivityManagerService需要管理相關Application中的Activity的生命週期時,通過ApplicationThread的代理對象與ActivityThread通訊。
- 4.ApplicationThreadProxy,是ApplicationThread在服務器端的代理,負責和客戶端的ApplicationThread通訊。AMS就是通過該代理與ActivityThread進行通信的。
- 5.Instrumentation,每一個應用程序只有一個Instrumentation對象,每個Activity內都有一個對該對象的引用。Instrumentation可以理解爲應用進程的管家,ActivityThread要創建或暫停某個Activity時,都需要通過Instrumentation來進行具體的操作。
- 6.ActivityStack,Activity在AMS的棧管理,用來記錄已經啓動的Activity的先後關係,狀態信息等。通過ActivityStack決定是否需要啓動新的進程。
- 7.ActivityRecord,ActivityStack的管理對象,每個Activity在AMS對應一個ActivityRecord,來記錄Activity的狀態以及其他的管理信息。其實就是服務器端的Activity對象的映像。
- 8.TaskRecord,AMS抽象出來的一個“任務”的概念,是記錄ActivityRecord的棧,一個“Task”包含若干個ActivityRecord。AMS用TaskRecord確保Activity啓動和退出的順序。如果你清楚Activity的4種launchMode,那麼對這個概念應該不陌生。
18.理解Window和WindowManager
- 1.Window用於顯示View和接收各種事件,Window有三種類型:應用Window(每個Activity對應一個Window)、子Window(不能單獨存在,附屬於特定Window)、系統window(Toast和狀態欄)
- 2.Window分層級,應用Window在1-99、子Window在1000-1999、系統Window在2000-2999.WindowManager提供了增刪改View三個功能。
- 3.Window是個抽象概念:每一個Window對應着一個View和ViewRootImpl,Window通過ViewRootImpl來和View建立聯繫,View是Window存在的實體,只能通過WindowManager來訪問Window。
- 4.WindowManager的實現是WindowManagerImpl其再委託給WindowManagerGlobal來對Window進行操作,其中有四個List分別儲存對應的View、ViewRootImpl、WindowManger.LayoutParams和正在被刪除的View
- 5.Window的實體是存在於遠端的WindowMangerService中,所以增刪改Window在本端是修改上面的幾個List然後通過ViewRootImpl重繪View,通過WindowSession(每個應用一個)在遠端修改Window。
- 6.Activity創建Window:Activity會在attach()中創建Window並設置其回調(onAttachedToWindow()、dispatchTouchEvent()),Activity的Window是由Policy類創建PhoneWindow實現的。然後通過Activity#setContentView()調用PhoneWindow的setContentView。
19.Bitmap的處理:
- 1.當使用ImageView的時候,可能圖片的像素大於ImageView,此時就可以通過BitmapFactory.Option來對圖片進行壓縮,inSampleSize表示縮小2^(inSampleSize-1)倍。
- 2.BitMap的緩存:
- 1.使用LruCache進行內存緩存。
- 2.使用DiskLruCache進行硬盤緩存。
- 3.實現一個ImageLoader的流程:同步異步加載、圖片壓縮、內存硬盤緩存、網絡拉取
- 1.同步加載只創建一個線程然後按照順序進行圖片加載
- 2.異步加載使用線程池,讓存在的加載任務都處於不同線程
- 3.爲了不開啓過多的異步任務,只在列表靜止的時候開啓圖片加載
20.綜合技術:
- 1.CrashHandler:獲取app crash的信息保存在本地然後在下一次打開app的時候發送到服務器。
- 2.multidex解決方法數過大的問題
21.如何實現一個網絡框架(參考Volley)
- 1.緩存隊列,以url爲key緩存內容可以參考Bitmap的處理方式,這裏單獨開啓一個線程。
- 2.網絡請求隊列,使用線程池進行請求。
- 3.提供各種不同類型的返回值的解析如String,Json,圖片等等。
22.ClassLoader的基礎知識:
- 1.雙親委託:一個ClassLoader類負責加載這個類所涉及的所有類,在加載的時候會判斷該類是否已經被加載過,然後會遞歸去他父ClassLoader中找。
- 2.可以動態加載Jar通過URLClassLoader
- 3.ClassLoader 隔離問題 JVM識別一個類是由:ClassLoader id+PackageName+ClassName。
- 4.加載不同Jar包中的公共類:
- 1.讓父ClassLoader加載公共的Jar,子ClassLoader加載包含公共Jar的Jar,此時子ClassLoader在加載公共Jar的時候會先去父ClassLoader中找。(只適用Java)
- 2.重寫加載包含公共Jar的Jar的ClassLoader,在loadClass中找到已經加載過公共Jar的ClassLoader,也就是把父ClassLoader替換掉。(只適用Java)
- 3.在生成包含公共Jar的Jar時候把公共Jar去掉。
23.插件化框架描述:dynamicLoadApk爲例子
- 1.可以通過DexClassLoader來對apk中的dex包進行加載訪問
- 2.如何加載資源是個很大的問題,因爲宿主程序中並沒有apk中的資源,所以調用R資源會報錯,所以這裏使用了Activity中的實現ContextImpl的getAssets()和getResources()再加上反射來實現。
- 3.由於系統啓動Activity有很多初始化動作要做,而我們手動反射很難完成,所以可以採用接口機制,將Activity的大部分生命週期提取成接口,然後通過代理Activity去調用插件Activity的生命週期。同時如果像增加一個新生命週期方法的時候,只需要在接口中和代理中聲明一下就行。
- 4.缺點:
- 1.慎用this,因爲在apk中使用this並不代表宿主中的activity,當然如果this只是表示自己的接口還是可以的。除此之外可以使用that代替this。
- 2.不支持Service和靜態註冊的Broadcast
- 3.不支持LaunchMode和Apk中Activity的隱式調用。
24.熱修復:Andfix爲例子
- 1.大致原理:apkpatch將兩個apk做一次對比,然後找出不同的部分。可以看到生成的apatch了文件,後綴改成zip再解壓開,裏面有一個dex文件。通過jadx查看一下源碼,裏面就是被修復的代碼所在的類文件,這些更改過的類都加上了一個_CF的後綴,並且變動的方法都被加上了一個叫@MethodReplace的annotation,通過clazz和method指定了需要替換的方法。然後客戶端sdk得到補丁文件後就會根據annotation來尋找需要替換的方法。最後由JNI層完成方法的替換。
- 2.無法添加新類和新的字段、補丁文件很容易被反編譯、加固平臺可能會使熱補丁功能失效