Android常問的面試題(二)

(一)圖片

1、圖片庫對比

2、LRUCache原理

LruCache是個泛型類,主要原理是:把最近使用的對象用強引用存儲在LinkedHashMap中,當緩存滿時,把最近最少使用的對象從內(nèi)存中移除,并提供get/put方法完成緩存的獲取和添加。LruCache是線程安全的,因為使用了synchronized關鍵字。

當調(diào)用put()方法,將元素加到鏈表頭,如果鏈表中沒有該元素,大小不變,如果沒有,需調(diào)用trimToSize方法判斷是否超過最大緩存量,trimToSize()方法中有一個while(true)死循環(huán),如果緩存大小大于最大的緩存值,會不斷刪除LinkedHashMap中隊尾的元素,即最少訪問的,直到緩存大小小于最大緩存值。當調(diào)用LruCache的get方法時,LinkedHashMap會調(diào)用recordAccess方法將此元素加到鏈表頭部。

3、圖片加載原理

4、自己去實現(xiàn)圖片庫,怎么做?

5、Glide源碼解析

1)Glide.with(context)創(chuàng)建了一個RequestManager,同時實現(xiàn)加載圖片與組件生命周期綁定:在Activity上創(chuàng)建一個透明的ReuqestManagerFragment加入到FragmentManager中,通過添加的Fragment感知Activty\Fragment的生命周期。因為添加到Activity中的Fragment會跟隨Activity的生命周期。在RequestManagerFragment中的相應生命周期方法中通過liftcycle傳遞給在lifecycle中注冊的LifecycleListener

2)RequestManager.load(url) 創(chuàng)建了一個RequestBuilder<T>對象 T可以是Drawable對象或是ResourceType等

3) RequestBuilder.into(view)

-->into(glideContext.buildImageViewTarget(view, transcodeClass))返回的是一個DrawableImageViewTarget, Target用來最終展示圖片的,buildImageViewTarget-->ImageViewTargetFactory.buildTarget()根據(jù)傳入class參數(shù)不同構建不同的Target對象,這個Class是根據(jù)構建Glide時是否調(diào)用了asBitmap()方法,如果調(diào)用了會構建出BitmapImageViewTarget,否則構建的是GlideDrawableImageViewTarget對象。

-->GenericRequestBuilder.into(Target),該方法進行了構建Request,并用RequestTracker.runRequest()

??? Request request = buildRequest(target);//構建Request對象,Request是用來發(fā)出加載圖片的,它調(diào)用了buildRequestRecursive()方法以,內(nèi)部調(diào)用了GenericRequest.obtain()方法

??? target.setRequest(request);

??? lifecycle.addListener(target);

??? requestTracker.runRequest(request);//判斷Glide當前是不是處于暫停狀態(tài),若不是則調(diào)用Request.begin()方法來執(zhí)行Request,否則將Request添加到待執(zhí)行隊列里,等暫停態(tài)解除了后再執(zhí)行

-->GenericRequest.begin()

1)onSizeReady()--> Engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,

??????????? priority, isMemoryCacheable, diskCacheStrategy, this) --> a)先構建EngineKey; b) loadFromCache從緩存中獲取EngineResource,如果緩存中獲取到cache就調(diào)用cb.onResourceReady(cached); c)如果緩存中不存在調(diào)用loadFromActiveResources從active中獲取,如果獲取到就調(diào)用cb.onResourceReady(cached);d)如果active中也不存在,調(diào)用EngineJob.start(EngineRunnable), 從而調(diào)用decodeFromSource()/decodeFromCache()-->如果是調(diào)用decodeFromSource()-->ImageVideoFetcher.loadData()-->HttpUrlFetcher()調(diào)用HttpUrlConnection進行網(wǎng)絡請求資源-->得于InputStream()后,調(diào)用decodeFromSourceData()-->loadProvider.getSourceDecoder().decode()方法解碼-->GifBitmapWrapperResourceDecoder.decode()-->decodeStream()先從流中讀取2個字節(jié)判斷是GIF還是普通圖,若是GIF調(diào)用decodeGifWrapper()來解碼,若是普通靜圖則調(diào)用decodeBitmapWrapper()來解碼-->bitmapDecoder.decode()

6、Glide使用什么緩存?

1) 內(nèi)存緩存:LruResourceCache(memory)+弱引用activeResources

Map<Key, WeakReference<EngineResource<?>>> activeResources正在使用的資源,當acquired變量大于0,說明圖片正在使用,放到activeResources弱引用緩存中,經(jīng)過release()后,acquired=0,說明圖片不再使用,會把它放進LruResourceCache中

2)磁盤緩存:DiskLruCache,這里分為Source(原始圖片)和Result(轉換后的圖片)

第一次獲取圖片,肯定網(wǎng)絡取,然后存active\disk中,再把圖片顯示出來,第二次讀取相同的圖片,并加載到相同大小的imageview中,會先從memory中取,沒有再去active中獲取。如果activity執(zhí)行到onStop時,圖片被回收,active中的資源會被保存到memory中,active中的資源被回收。當再次加載圖片時,會從memory中取,再放入active中,并將memory中對應的資源回收。

之所以需要activeResources,它是一個隨時可能被回收的資源,memory的強引用頻繁讀寫可能造成內(nèi)存激增頻繁GC,而造成內(nèi)存抖動。資源在使用過程中保存在activeResources中,而activeResources是弱引用,隨時被系統(tǒng)回收,不會造成內(nèi)存過多使用和泄漏。

7、Glide內(nèi)存緩存如何控制大???

Glide內(nèi)存緩存最大空間(maxSize)=每個進程可用最大內(nèi)存*0.4(低配手機是?? 每個進程可用最大內(nèi)存*0.33)

磁盤緩存大小是250MB?? int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;

(二)網(wǎng)絡和安全機制

??? 網(wǎng)絡框架對比和源碼分析

??? 自己去設計網(wǎng)絡請求框架,怎么做?

??? okhttp源碼

??? 網(wǎng)絡請求緩存處理,okhttp如何處理網(wǎng)絡緩存的

(1)網(wǎng)絡緩存優(yōu)先考慮強制緩存,再考慮對比緩存

??? 首先判斷強制緩存中的數(shù)據(jù)的是否在有效期內(nèi)。如果在有效期,則直接使用緩存。如果過了有效期,則進入對比緩存。

??? 在對比緩存過程中,判斷ETag是否有變動,如果服務端返回沒有變動,說明資源未改變,使用緩存。如果有變動,判斷Last-Modified。

??? 判斷Last-Modified,如果服務端對比資源的上次修改時間沒有變化,則使用緩存,否則重新請求服務端的數(shù)據(jù),并作緩存工作。

(2)okhttp緩存

開啟使用Okhttp的緩存其實很簡單,只需要給OkHttpClient對象設置一個Cache對象即可,創(chuàng)建一個Cache時指定緩存保存的目錄和緩存最大的大小即可。

??? //新建一個cache,指定目錄為外部目錄下的okhttp_cache目錄,大小為100M

??? Cache cache = new Cache(new File(Environment.getExternalStorageDirectory() + "/okhttp_cache/"), 100 * 1024 * 1024);

??? //將cache設置到OkHttpClient中,這樣緩存就開始生效了。

??? OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();

相關的類有:

1)CacheControl( HTTP中的Cache-Control和Pragma緩存控制):指定緩存規(guī)則

2)Cache(緩存類)

3)DiskLruCache(文件化的LRU緩存類)

(1)讀取緩存:先獲限OkHttpClient的Cache緩存對象,就是上面創(chuàng)建OkHttpClient設置的Cahce; 傳Request請求到Cache的get方法查找緩存響應數(shù)據(jù)Response;構造一個緩存策略,再調(diào)用它的get去決策使用網(wǎng)絡請求還是緩存響應。若使用緩存,它的cacheResponse不為空,networkRequest為空,用緩存構造響應直接返回。若使用請求,則cacheResponse為空,networkRequest不為空,開始網(wǎng)絡請求流程。

Cache的get獲取緩存方法,計算request的key值(請求url進行md5加密),根據(jù)key值去DisLruCache查找是否存在緩存內(nèi)容,存則則創(chuàng)建繪存Entry實體。ENTRY_METADATA代表響應頭信息,ENTRY_BODY代表響應體信息。如果緩存存在,在指定目錄下會有兩個文件****.0??? *****.1分別存儲某個請求緩存響應頭和響應體信息。

CacheStrategy的get方法:1)若緩存響應為空或 2)請求是https但緩存響應沒有握手信息;3)請求和緩存響應都是不可緩存的;4)請求是onCache,并且又包含if-Modified-Since或If-None-Match則不使用緩存; 再計算請求有效時間是否符合響應的過期時間,若響應在有效范圍內(nèi),則緩存策略使用緩存,否則創(chuàng)建一個新的有條件的請求,返回有條件的緩存策略。

(2)存儲緩存流程:從HttpEngine的readResponse()發(fā)送請求開始,判斷hasBody(userResponse),如果緩存的話,maybeCache()緩存響應頭信息,unzip(cacheWritingResponse(storeRequest, userResponse))緩存響應體。

??? 從網(wǎng)絡加載一個10M的圖片,說下注意事項

??? TCP的3次握手和四次揮手

??? TCP與UDP的區(qū)別

??? TCP與UDP的應用

??? HTTP協(xié)議

??? HTTP1.0與2.0的區(qū)別

??? HTTP報文結構

??? HTTP與HTTPS的區(qū)別以及如何實現(xiàn)安全性

??? 如何驗證證書的合法性?

??? https中哪里用了對稱加密,哪里用了非對稱加密,對加密算法(如RSA)等是否有了解?

??? client如何確定自己發(fā)送的消息被server收到?

??? 談談你對WebSocket的理解

??? WebSocket與socket的區(qū)別

??? 談談你對安卓簽名的理解。

??? 請解釋安卓為啥要加簽名機制?

??? 視頻加密傳輸

??? App 是如何沙箱化,為什么要這么做?

??? 權限管理系統(tǒng)(底層的權限是如何進行 grant 的)?

(三)數(shù)據(jù)庫

??? sqlite升級,增加字段的語句

??? 數(shù)據(jù)庫框架對比和源碼分析

??? 數(shù)據(jù)庫的優(yōu)化

??? 數(shù)據(jù)庫數(shù)據(jù)遷移問題

(四)算法

??? 排序算法有哪些?

??? 最快的排序算法是哪個?

??? 手寫一個冒泡排序

??? 手寫快速排序代碼

??? 快速排序的過程、時間復雜度、空間復雜度

??? 手寫堆排序

??? 堆排序過程、時間復雜度及空間復雜度

??? 寫出你所知道的排序算法及時空復雜度,穩(wěn)定性

??? 二叉樹給出根節(jié)點和目標節(jié)點,找出從根節(jié)點到目標節(jié)點的路徑

??? 給阿里2萬多名員工按年齡排序應該選擇哪個算法?

??? GC算法(各種算法的優(yōu)缺點以及應用場景)

??? 蟻群算法與蒙特卡洛算法

??? 子串包含問題(KMP 算法)寫代碼實現(xiàn)

??? 一個無序,不重復數(shù)組,輸出N個元素,使得N個元素的和相加為M,給出時間復雜度、空間復雜度。手寫算法

??? 萬億級別的兩個URL文件A和B,如何求出A和B的差集C(提示:Bit映射->hash分組->多文件讀寫效率->磁盤尋址以及應用層面對尋址的優(yōu)化)

??? 百度POI中如何試下查找最近的商家功能(提示:坐標鏡像+R樹)。

??? 兩個不重復的數(shù)組集合中,求共同的元素。

??? 兩個不重復的數(shù)組集合中,這兩個集合都是海量數(shù)據(jù),內(nèi)存中放不下,怎么求共同的元素?

??? 一個文件中有100萬個整數(shù),由空格分開,在程序中判斷用戶輸入的整數(shù)是否在此文件中。說出最優(yōu)的方法

??? 一張Bitmap所占內(nèi)存以及內(nèi)存占用的計算

一張圖片(bitmap)占用的內(nèi)存影響因素:圖片原始長、寬,手機屏幕密度,圖片存放路徑下的密度,單位像素占用字節(jié)數(shù)

bitmapSize=圖片長度*(inTargetDensity手機的density / inDensity圖片存放目錄的density)*寬度*(手機的inTargetDensity / inDensity目標存放目錄的density)*單位像素占用的字節(jié)數(shù)(圖片長寬單位是像素)

1)圖片長寬單位是像素:單位像素字節(jié)數(shù)由其參數(shù)BitmapFactory.Options.inPreferredConfig變量決定,它是Bitmap.Config類型,包括以下幾種值:ALPHA_8圖片只有alpha值,占用一個字節(jié);ARGB_4444 一個像素占用2個字節(jié),A\R\G\B各占4bits;ARGB_8888一個像素占用4個字節(jié),A\R\G\B各占8bits(高質(zhì)量圖片格式,bitmap默認格式);ARGB_565一個像素占用2字節(jié),不支持透明和半透明,R占5bit, Green占6bit, Blue占用5bit. 從Android4.0開始該項無效。

2) inTargetDensity 手機的屏幕密度(跟手機分辨率有關系)

inDensity原始資源密度(mdpi:160;?? hdpi:240;?? xhdpi:320;?? xxhdpi:480; xxxhdpi:640)

當Bitmap對象在不使用時,應該先調(diào)用recycle(),再將它設置為null,雖然Bitmap在被回收時可通過BitmapFinalizer來回收內(nèi)存。但只有系統(tǒng)垃圾回收時才會回收。Android4.0之前,Bitmap內(nèi)存分配在Native堆中,Android4.0開始,Bitmap的內(nèi)存分配在dalvik堆中,即Java堆中,調(diào)用recycle()并不能立即釋放Native內(nèi)存。

??? 2000萬個整數(shù),找出第五十大的數(shù)字?

??? 燒一根不均勻的繩,從頭燒到尾總共需要1個小時。現(xiàn)在有若干條材質(zhì)相同的繩子,問如何用燒繩的方法來計時一個小時十五分鐘呢?

??? 求1000以內(nèi)的水仙花數(shù)以及40億以內(nèi)的水仙花數(shù)

??? 5枚硬幣,2正3反如何劃分為兩堆然后通過翻轉讓兩堆中正面向上的硬8幣和反面向上的硬幣個數(shù)相同

??? 時針走一圈,時針分針重合幾次

??? N*N的方格紙,里面有多少個正方形

??? x個蘋果,一天只能吃一個、兩個、或者三個,問多少天可以吃完?

(五)插件化、模塊化、組件化、熱修復、增量更新、Gradle

??? 對熱修復和插件化的理解

??? 插件化原理分析

??? 模塊化實現(xiàn)(好處,原因)

??? 熱修復,插件化

??? 項目組件化的理解

??? 描述清點擊 Android Studio 的 build 按鈕后發(fā)生了什么

(六)架構設計和設計模式

??? 談談你對Android設計模式的理解

??? MVC MVP MVVM原理和區(qū)別

??? 你所知道的設計模式有哪些?

??? 項目中常用的設計模式

??? 手寫生產(chǎn)者/消費者模式

??? 寫出觀察者模式的代碼

??? 適配器模式,裝飾者模式,外觀模式的異同?

??? 用到的一些開源框架,介紹一個看過源碼的,內(nèi)部實現(xiàn)過程。

??? 談談對RxJava的理解

RxJava是基于響應式編程,基于事件流、實現(xiàn)異步操(類似于Android中的AsyncTask、Handler作用)作的庫,基于事件流的鏈式調(diào)用,使得RxJava邏輯簡潔、使用簡單。RxJava原理是基于一種擴展的觀察者模式,有四種角色:被觀察者Observable 觀察者Observer 訂閱subscribe 事件Event。RxJava原理可總結為:被觀察者Observable通過訂閱(subscribe)按順序發(fā)送事件(Emitter)給觀察者(Observer), 觀察者按順序接收事件&作出相應的響應動作。

RxJava中的操作符:

1)defer():直到有觀察者(Observer)訂閱時,才會動態(tài)創(chuàng)建被觀察者對象(Observer)&發(fā)送事件,通過Observer工廠方法創(chuàng)建被觀察者對象,每次訂閱后,都會得到一個剛創(chuàng)建的最新的Observer對象,可以確保Observer對象里的數(shù)據(jù)是最新的。defer()方法只會定義Observable對象,只有訂閱操作才會創(chuàng)建對象。

??? Observable<T> observable = Observable.defer(new Callable<ObservableSource<? extends T>>() {

??????? @Override

??????? public ObservableSource<? extends T> call() throws Exception {

??????????? return Observable.just();

??????? }

??? }

2)timer() 快速創(chuàng)建一個被觀察者(Observable),延遲指定時間后,再發(fā)送事件

??? Observable.timer(2, TimeUnit.SECONDS)//也可以自定義線程timer(long, TimeUnit, Scheduler)

??????? .subscribe(new Observer<Long>() {

??????????? @Override

??????????? public void onSubscribe(Disposable d) {

??????????? }

??????????? ...

???????? });

3) interval() intervalRange() 快速創(chuàng)建一個被觀察者對象(Observable),每隔指定時間就發(fā)送事件

??? //interval三個參數(shù),參數(shù)1:第一次延遲時間? 參數(shù)2:間隔時間數(shù)字?? 參數(shù)3:時間單位

??? Observable.interval(3, 1, TimeUnit.SECONDS).subscribe();

??? //intervalRange五個參數(shù),參數(shù)1:事件序列起始點? 參數(shù)2:事件數(shù)量? 參數(shù)3:第一次延遲時間 參數(shù)4:間隔時間數(shù)字?? 參數(shù)5:時間單位

??? Observable.intervalRange(3, 10, 2, 1, TimeUnit.SECONDS).subscribe();

??? RxJava的功能與原理實現(xiàn)

Rxjava發(fā)送事件步驟:

1)創(chuàng)建被觀察者對象Observable&定義需要發(fā)送的事件

??? Observable.create(new ObservableOnSubscribe<T>(){

??????? @Override

??????? public void subscribe(ObservableEmitter<T> emitter) throws Exception {

??????????? //定義發(fā)送事件的行為

??????? }

??? });

Observable.create()方法實際創(chuàng)建了一個ObservableCreate對象,它是Observable的子類,傳入一個ObservableOnSubscribe對象,復寫了發(fā)送事件行為的subscribe()方法。

2)創(chuàng)建觀察者對象Observer&定義響應事件的行為

??? Observer observer = new Observer<T>() {

??????? @Override

??????? public void onSubscribe(Disposable d){//Disposable對象可用于結束事件

??????????? //默認最先調(diào)用

??????? }

??????? @Override

??????? public void onNext(T t){

??????? }

??????? @Override

??????? public void onError(Throwable d){

??????? }

??????? @Override

??????? public void onComplete(){

??????? }

??? }

3)通過subscribe()方法使觀察者訂閱被觀察者

??? Observable.subscribe(Observer observer);//實際調(diào)用的是ObservableCreate.subscribeActual()方法,具體實現(xiàn)如下

??? protected void subscribeActual(Observer<? super T> observer) {

????????????????? // 1. 創(chuàng)建1個CreateEmitter對象用于發(fā)射事件(封裝成1個Disposable對象)

??????????????? CreateEmitter<T> parent = new CreateEmitter<T>(observer);

??????????????? // 2. 調(diào)用觀察者(Observer)的onSubscribe()

??????????????? observer.onSubscribe(parent);

??????????????? try {

??????????????????? // 3. 調(diào)用source對象的(ObservableOnSubscribe對象)subscribe()

??????????????????? source.subscribe(parent);

??????????????? } catch (Throwable ex) {

??????????????????? Exceptions.throwIfFatal(ex);

??????????????????? parent.onError(ex);

??????????????? }

??????? }

??? RxJava的作用,與平時使用的異步操作來比的優(yōu)缺點

??? 說說EventBus作用,實現(xiàn)方式,代替EventBus的方式

??? 從0設計一款App整體架構,如何去做?

??? 說一款你認為當前比較火的應用并設計(比如:直播APP,P2P金融,小視頻等)

??? 談談對java狀態(tài)機理解

??? Fragment如果在Adapter中使用應該如何解耦?

??? Binder機制及底層實現(xiàn)

??? 對于應用更新這塊是如何做的?(解答:灰度,強制更新,分區(qū)域更新)?

??? 實現(xiàn)一個Json解析器(可以通過正則提高速度)

??? 統(tǒng)計啟動時長,標準

(七)性能優(yōu)化

??? 如何對Android 應用進行性能分析以及優(yōu)化?

??? ddms 和 traceView

??? 性能優(yōu)化如何分析systrace?

??? 用IDE如何分析內(nèi)存泄漏?

??? Java多線程引發(fā)的性能問題,怎么解決?

??? 啟動頁白屏及黑屏解決?

??? 啟動太慢怎么解決?

在冷啟動系統(tǒng)要執(zhí)行三個任務:加載和啟動應用程序;在app啟動后立即顯示一個空白窗體;創(chuàng)建APP進程。

應用啟動后會執(zhí)行:創(chuàng)建應用程序對象;啟動主線程;創(chuàng)建Main Activity;初始化構造View;在屏幕上布局;執(zhí)行初始化繪制操作;

應用啟動,空白窗口會一直存在直到系統(tǒng)完成了應用的首次繪制操作,此時,系統(tǒng)會替換啟動窗口,讓用戶能和APP進行交互。

對于熱啟動,如果應用的Activity駐留在內(nèi)存中,應用就可避免重復進行對象初始化。如果系統(tǒng)執(zhí)行了內(nèi)存回收并觸發(fā)GC,如onTrimMemory(),熱啟動時對象仍需重建,這樣系統(tǒng)進程也會一直顯示白屏直到應用完成Activity的渲染。

測量應用啟動時間:1) 可通過logcat中查看Displayed中顯示啟動類耗時; 2) 通過adb shell am start -S -W 包名/啟動類全限定名,-S表示重啟當前應用,命令進行檢測啟動app的耗時。3) 使用reportFullyDrawn()方法來測量應用啟動到所有資源和視圖層次結構完整顯示之間的所經(jīng)過的時間。

優(yōu)化方法:1)減少view的層級,減少復用和布局嵌套使布局扁平化,不要加載對用戶不可見的布局,如使用ViewStub;

2)將需要在主線程中初始化但可不立即完成的延遲加載,部分組件放到子線程中初始化。

3)減少Application.onCreate和啟動頁和第一個界面onCreate中方法的耗時操作。

4)設置閃屏頁,將閃屏頁設置為啟動頁的activity窗口背景windowBackground屬性,為啟動屏幕提供一個背景,

??? 怎么保證應用啟動不卡頓?

應用卡頓的主要原因有:速度曲線不夠流暢,掉幀、觸摸響應速度

Android顯示機制:app->SurfaceFlinger->Display

開發(fā)者選項->GPU呈現(xiàn)模式分析->在屏幕上顯示為條形圖,可以在屏幕上看到顯示每幀繪制花費的時間,有條基準線是16ms,超過這條基線很可能出現(xiàn)掉幀。如果藍線很長,則說明軟件draw太費時,可通過traceview來繼續(xù)分析draw的java代碼。如果中間紅色部分很長則說明是openGL ES繪制太費時,用gltrace來分析OpernGL ES調(diào)用過程。用systrace分析,用traceview來看代碼,

??? App啟動崩潰異常捕捉

??? 自定義View注意事項

??? 現(xiàn)在下載速度很慢,試從網(wǎng)絡協(xié)議的角度分析原因,并優(yōu)化(提示:網(wǎng)絡的5層都可以涉及)。

??? Https請求慢的解決辦法(提示:DNS,攜帶數(shù)據(jù),直接訪問IP)

??? 如何保持應用的穩(wěn)定性

1)需求明確清楚,編碼時明確知道要實現(xiàn)的功能和實現(xiàn)方法,技術選型等,對一些庫進行封裝再使用。防止代碼冗余、避免多線程導致的問題;

2)異常崩潰處理捕獲,在使用對象前做判空處理等

3)提高編碼質(zhì)量,用lint\findbugs進行代碼靜態(tài)分析;

4)OOM和內(nèi)存泄漏檢測

5)框架測試,兼容性測試、單元測試、monkey測試

6)發(fā)布維度,灰度,選擇部分渠道進行發(fā)布,收集問題;

7)熱更新。

??? RecyclerView和ListView的性能對比

??? ListView的優(yōu)化

??? RecycleView優(yōu)化

??? View渲染

??? Bitmap如何處理大圖,如一張30M的大圖,如何預防OOM

??? java中的四種引用的區(qū)別以及使用場景

??? 強引用置為null,會不會被回收?

如果對象沒有被GC Roots引用到,會被GC回收到,但什么時候回收需根據(jù)JVM的特性什么時候觸發(fā)GC操作。即使調(diào)用了System.gc() JVM也不一定會觸發(fā)GC。

(八)NDK、jni、Binder、AIDL、進程通信有關

??? 請介紹一下NDK

??? 什么是NDK庫?

??? jni用過嗎?

??? 如何在jni中注冊native函數(shù),有幾種注冊方式?

??? Java如何調(diào)用c、c++語言?

??? jni如何調(diào)用java層代碼?

1)Java類中要調(diào)用jni方法,需要在java類中聲明本地方法,public native void methodName();//本地方法。還要在類的靜態(tài)代碼塊中導入so庫 static { System.loadLibrary("MyJni");}

2)在C/C++獲取類的對象的方法有兩種:

a)通過c/c++創(chuàng)建java對象,通過對象獲取類,通過類獲取類的構造方法的ID,基于方法ID和類,創(chuàng)建新對象。

??? JNIEXPORT void JNICALL JAVA_nativeMethod(JNIEnv *env, jobject this, jint i) {

??????? jclass clazz = (*env).GetObjectClass(thiz);

??????? jmethodID mid = (*env).GetMethodID(clazz, "<init>","()V");

??????? jobject obj = (*env).NewObject(clazz, mid);

??? }

b) 通過c/c++創(chuàng)建不同類對象,通過FindClass方法獲取需要的類;通過類獲取類的構造方法的ID,基于方法ID和類,創(chuàng)建對象

??? JNIEXPORT void JNICALL JAVA_nativeMethod(JNIEnv *env, jobject this, jint i) {

??????? jclass clazz = (*env).FindClass("com/packagepath/className");

??????? jmethodID mid = (*env).GetMethodID(clazz, "<init>","()V");

??????? jobject obj = (*env).NewObject(clazz, mid);

??? }

調(diào)用java方法跟上面調(diào)用構造函數(shù)類似,獲取類的方法ID,基于對象的方法id調(diào)用Java方法

??? JNIEXPORT void JNICALL JAVA_nativeMethod(JNIEnv *env, jobject thiz, jint i) {

??????? jclass clazz = (*env).GetObjectClass(thiz);

??????? m_Object = (*env).NewGlobalRef(thiz);

??????? m_mid = (*env).GetMethodID(clazz, "methodName", "()V");//獲取Java方法的ID

??????? m_fid = (*env).GetFieldID(clazz, "a","I");//獲取Java變量的ID

??????? (*env).SetIntField(m_Object, m_fid, i);

??????? (*env).CallVoidMethod(m_Object, m_mid);

??? }

??? 進程間通信的方式?

??? Binder機制

??? 簡述IPC?

??? 什么是AIDL?

??? AIDL解決了什么問題?

??? AIDL如何使用?

??? Android 上的 Inter-Process-Communication 跨進程通信時如何工作的?

??? 多進程場景遇見過么?

??? Android進程分類?

前臺進行(當前正在前臺運行的進程,說明用戶當前正在與該進程交互), 滿足以下至少一個條件的叫做 foreground progcess:

? a.有一個Activity在前臺獲得焦點可與用戶互動

? b.有一個 BroadcastReceiver組件正在運行onReceive()方法

? c.有一個Sevice組件正在運行onCreate()/onStart()/onDestory()方法

可見進程(可見,但用戶不能直接與之交互)滿足以下條件之一稱為可見進程:a.有一個Activity能被用戶看見但是失去焦點(處于onPause()狀態(tài)) b.有一個 Service調(diào)用了startForeground()方法 c.綁定了一個Service,系統(tǒng)將該Service作為一個特殊的用戶知道的功能使用如自動更換壁紙,輸入法服務等。

服務進程(擁有service的進程,一般在后臺為用戶服務的),通過startService()方法開啟的沒有綁定在activity上的Service的進程,Service長時間運行 (超過30分鐘以上)會被降級到cached process

后臺進程(對用戶作用不大,缺少該進程一般不會影響用戶對系統(tǒng)的體驗)

空進程(一般作為緩存機制服務的)

??? 進程和 Application 的生命周期?

??? 進程調(diào)度

??? 談談對進程共享和線程安全的認識

Android進程共享可通過共享用戶ID來實現(xiàn),

對于SharedPreferences想實現(xiàn)多進程共享需要設置MODE_MULTI_PROCESS,設置了這個Flag后,每次調(diào)用Context.getSharedPreferences時系統(tǒng)會重新從SP

SharedPreferences myPrefs = context.getSharedPreferences(MY_FILE_NAME, Context.MODE_MULTI_PROCESS | Context.MODE_PRIVATE);

??? 談談對多進程開發(fā)的理解以及多進程應用場景

??? 什么是協(xié)程?

(九)framework層、ROM定制、Ubuntu、Linux之類的問題

??? java虛擬機的特性

??? 談談對jvm的理解

??? JVM內(nèi)存區(qū)域,開線程影響哪塊內(nèi)存

??? 對Dalvik、ART虛擬機有什么了解?

??? Art和Dalvik對比

??? 虛擬機原理,如何自己設計一個虛擬機(內(nèi)存管理,類加載,雙親委派)

??? 談談你對雙親委派模型理解

??? JVM內(nèi)存模型,內(nèi)存區(qū)域

??? 類加載機制

??? 談談對ClassLoader(類加載器)的理解

??? 談談對動態(tài)加載(OSGI)的理解

??? 內(nèi)存對象的循環(huán)引用及避免

??? 內(nèi)存回收機制、GC回收策略、GC原理時機以及GC對象

??? 垃圾回收機制與調(diào)用System.gc()區(qū)別

System.gc()只是通知垃圾回收器要進行垃圾回收操作,但并沒有立即執(zhí)行垃圾回收。它只是建議JVM安排GC運行,還有可能被拒絕。

??? Ubuntu編譯安卓系統(tǒng)

??? 系統(tǒng)啟動流程是什么?(提示:Zygote進程 –> SystemServer進程 –> 各種系統(tǒng)服務 –> 應用進程)

??? 大體說清一個應用程序安裝到手機上時發(fā)生了什么

??? 簡述Activity啟動全部過程

1)Activity.startActivity-->startActivityForResult()

2)-->Instrumentation.execStartActivity()-->execStartActivity()

3)ActivityManager.getService().startActivity()通過Binder到ActivityManagerService.startActivity()

4)-->ActivityStarter.startActivityMayWait()-->startActivityLocked()-->startActivityUnchecked()

5)--ActivityStackSupervisor.resumeFocusedStackTopActivityLocked()-->ApplicationThread$scheduleLaunchActivity

6)ApplicationThread.schedulelaunchActivity()通過ActivityThread.sendMessage,再處理消息,進入handleLaunchActivity

-->Instrumentation$newActivity創(chuàng)建Activity的實例,使用類加載器創(chuàng)建Activity對象。

-->makeApplication創(chuàng)建Application對象,調(diào)用它的Application.onCreate()方法

Instrumentation這個類就是完成對Application和Activity初始化和生命周期的工具類。

??? App啟動流程,從點擊桌面開始

1)點擊桌面App圖標,Launcher進程采用Binder IPC向system_server進程發(fā)送startActivity()

2)system_server進程向Zygote發(fā)送創(chuàng)建進程的請求(AMS中通過startActivity()方法,調(diào)用startProcessLocked()函數(shù)),Zygote通過socket通信的方式讓Zygote進程 fork一個新進程出來作為App進程;

3)App進程通過Binder IPC向system_server進程發(fā)起attachApplication請求(ActivityThread的main()函數(shù)里會創(chuàng)建Application,還調(diào)用ActivityStackSupervisor.attachApplicationLocked);

4)system_server進程收到請求后,進行一系列準備工作,再通過binder IPC向APP進程發(fā)送scheduleLaunchActivity請求;

5)App進程的binder線程(ApplicationThread)收到請求后,通過handler向主線程發(fā)送LAUNCHER_ACTIVITY消息;

6)主線程收到Message消息后,通過反射機制創(chuàng)建出Activity并回調(diào)Activity.onCreate()等方法;

7)App正式啟動,開始進入Activity的生命周期。

??? 邏輯地址與物理地址,為什么使用邏輯地址?

??? Android為每個應用程序分配的內(nèi)存大小是多少?

根據(jù)應用實際使用情況分配,初始給進程分配為8M,應用最大可分配的可能是64M\128M\256M等

??? Android中進程內(nèi)存的分配,能不能自己分配定額內(nèi)存?

進程內(nèi)存分配跟手機配置有關,不同手機可能不一樣,有64M\128M\256M等,heapgrowthlimit是一個普通應用的內(nèi)存限制,可通過ActivityManager.getLargeMemoryClass()獲得,在mainfest中設置了largeHeap=true后,可以使應用可用內(nèi)存增大到原來兩倍。并不能自己定額分配內(nèi)存,android系統(tǒng)是根據(jù)應用所需要內(nèi)存大小,先分配初始大小heapstartsize,當應用申請更多內(nèi)存,系統(tǒng)會再進行分配,但不得超過最大內(nèi)存,超過了會報OOM。

??? 進程?;畹姆绞?/p>

1)模擬前臺進程,startForeground()將當前進程偽裝成前臺進程,將進程優(yōu)先級提高到最高,現(xiàn)在前臺進程會顯示在通知欄中,取消不掉。

2)JobScheduler機制喚醒,系統(tǒng)會根據(jù)自己實現(xiàn)定時去調(diào)用改接口傳遞進程去實現(xiàn)一些操作,且這個接口被強制停止后仍能正常啟動。在調(diào)用JobSchedule.schedule()來啟動任務。

3)實現(xiàn)Service類時,將onStartCommand()返回值設置為START_STICKY,利用系統(tǒng)機制在service掛掉后自動拉活,但這種方式只適合原生系統(tǒng),像小米、華為等定制化比較高的第三方廠商,這些都限制了。

4)一像素的Activity,

5)應用之間相互喚醒。

??? 如何保證一個后臺服務不被殺死?(相同問題:如何保證service在后臺不被kill?)比較省電的方式是什么?

??? App中喚醒其他進程的實現(xiàn)方式

1)啟動其他進程的Activity\Service或是發(fā)送一條廣播給相應的應用(該應用得靜態(tài)注冊此廣播)

OOM定位與分析,如何定位哪塊原因導致應用最終發(fā)生OOM?

OOM發(fā)生后,可以用Android Studio自帶的Android Monitor dump出HPROF文件,再用SDK中hprof-conv工具轉換為標準的Java堆轉儲文件格式,再用MAT繼續(xù)分析。切換到histogram視圖,按shadow heap降序排序,對實例對象占用內(nèi)存大小排序,再查看實例到GC ROOT的路徑。

一般可能導致的如圖片:直接加載超大尺寸圖片(對圖片尺寸縮放預處理后再加載)、圖片加載后未及時釋放(利用LRU機制保證圖片個數(shù)和占用內(nèi)存);在頁面中,加載非常多的圖片(避免同時加載大量圖片)

JNI層的crash如何捕獲?

通過ndk安裝包中的addr2line objdump? ndk-stack工具進行分析crash,ndk針對不同的CPU架構實現(xiàn)了多套相同的工具,在選擇add2line objdump時要根據(jù)目標機器CPU架構來選擇。

一般JNI發(fā)生異常,會出現(xiàn)一個Fatal signal信號量,大概知道是哪個函數(shù)引起的,再看下面的backtrace日志,backtrace是JNI調(diào)用堆棧信息,以“#兩位數(shù)字 pc”開頭,找到對應的函數(shù),再用addr2line進行定位出錯的位置。addr2line -C -f -e ./obj/armeabi/xxx.so 000eea70

使用ndk-stack協(xié)助我們獲取底層崩潰的堆棧信息,adb logcat | ndk-stack -sym ./obj/armeabi/xxx.so

應用卡頓定位

1)使用UI線程的Looper打印日志

Android主線程更新UI,如果1S鐘刷新少于60次,即FPS小于60,一幀加載超過16.67ms的話,用戶就會產(chǎn)生卡頓的感覺。Android使用消息機制進行UI更新,UI線程中有個Looper,其loop方法會不斷提取message,調(diào)用其綁定的Handler在UI線程執(zhí)行。如果handler.dispatchMessage方法中有耗時操作,就會發(fā)生卡頓。如果有卡頓,就打印出UI線程的堆棧信息。

優(yōu)點:用戶使用app就可以監(jiān)控卡頓情況,但因需另開子線程獲取堆棧信息,會消耗系統(tǒng)資源。

2)使用Choreographer.FrameCallback監(jiān)控卡頓

Android系統(tǒng)每16ms會發(fā)出SYNC信息,通知界面重繪、渲染,每一次同步的周期為16.6ms,代表一幀的刷新頻率可以在兩次回調(diào)時間周期來判斷是否發(fā)生卡頓。(Android4.1以上才支持)。可以通過Choreographer.FrameCallback回調(diào)doFrame(long)函數(shù),如果兩次doFrame之間間隔大于16.67ms說明發(fā)生了卡頓。這種方法從app層面來監(jiān)控卡頓,同時可實時計算幀率和掉幀數(shù),實時監(jiān)測APP頁面的幀率數(shù)據(jù),一旦發(fā)現(xiàn)幀率過低,可自動保存現(xiàn)場堆棧信息。

卡頓監(jiān)控系統(tǒng)處理流程:開發(fā)修復-》用戶上報(后臺配置下灰度0.2%的用戶量進行卡頓監(jiān)控和上報,每個用戶一天上報一次,上報后刪除文件不影響存儲空間)-》后臺解析(過濾、去重、分類、反解堆棧、入庫)-》平臺展示-》自動提單

Http2.0有關多路復用:

多路復用原理:HTTP2流和多路復用?? HTTP2.0原理詳細解析

原理是二進制分幀層+流實現(xiàn)了多路復用,OKHttp是怎么支持的呢,那就是讀幀,讀流,Okhttp對http2的支持簡單分析

ViewStub是怎么實現(xiàn)延時加載的?

ViewStub是一個不可見、大小為0的View。具體體現(xiàn)在ViewStub的構造函數(shù)中會進行設置setVisibility(GONE)設置控件不可見,同時會設置setWillNotDraw(true),即本View不會調(diào)用onDraw()方法繪制內(nèi)容。在它的onMeasure函數(shù)中會調(diào)用setMeasureDimenssion(0, 0)即不會測量視圖,直接設置一個大小為0的View.

對于ViewStub.inflate()機制是:1)調(diào)用LayoutInflate.flate(mLayoutResource, parent, false)來加載ViewStub中android:layout設置的布局view(最后一個參數(shù)false是代表attachToRoot設置成false,說明忽略android:layout中布局根節(jié)點的layoutParams參數(shù));2)從父視圖中獲取當前ViewStub在父視圖中位置(在加載父視圖時會用一個占位符來代表ViewStub);3)將當前ViewStub從parent中移除;4)將android:layout中的布局view add到父視圖中,如果StubView中設置了layoutParams屬性就會用ViewStub中設置的。

應用場景:如網(wǎng)絡加載失敗頁面,評論區(qū)域內(nèi)的listView(當沒有評論或是請求失敗的時候不加載)

加載方式:findViewById(R.id.stubViewId).setVisibility(View.VISIBLE);或是((ViewStub)findViewById(R.id.StubViewId)).inflate();其實設置Visibility最終也是調(diào)用inflate來加載布局的。

如果ViewStub標簽下寫上width/height,在ViewStub相應的layout xml文件中也進行了寬高定義,會以誰為準?

其實上面也分析過了,會以ViewStub中設置的layoutParams為準。

ViewStub可不可以加載多次呢?

不能,上面也分析過了,ViewStub調(diào)用inflate()方法后,會把自己從父視圖中移除掉,并把自身所包含的內(nèi)容添加到父視圖中,再重新加載,找不到在父視圖了,就會拋出ViewStub must have a non-null ViewGroup viewparent。

merge標簽

merge一般可以和include/ViewStub結合使用,當include/ViewStub從外部導入xml結構時,可以將被導入的xml用merge作為根節(jié)點表示,當被加載到父布局中可以將它們?nèi)诤系礁讣壗Y構中,而不會出現(xiàn)冗余的節(jié)點。因為它是直接將其中的子元素添加到merge標簽的parent中,這樣就保證不會引入額外的層級了。

注意:1)<merge />只可以作為xml layout的根節(jié)點;2)當需要擴展的xml layout本身的根節(jié)點也是merge時,需要將被導入的xml layout置于ViewGroup中,且設置attachToRoot為true。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容