Android高級(jí)面試題整理一

1.android 事件分發(fā)機(jī)制,分析整體流程

image.png

2.android View繪制機(jī)制和加載過(guò)程,詳細(xì)說(shuō)一
下整體流程

一個(gè) Activity 包含一個(gè)Window,Window是一個(gè)抽象基類,是 Activity 和整個(gè) View 系統(tǒng)交互的接口,只有一個(gè)實(shí)現(xiàn)子類PhoneWindow,提供了一系列窗口的方法,比如設(shè)置背景,標(biāo)題等。一個(gè)PhoneWindow 對(duì)應(yīng)一個(gè)DecorView 跟 一個(gè) ViewRootImpl,DecorView 是ViewTree 里面的頂層布局,是繼承于FrameLayout,包含兩個(gè)子View,一個(gè)id=statusBarBackground 的 View 和 LineaLayout,LineaLayout 里面包含 title 跟content,title就是平時(shí)用的TitleBar或者ActionBar, content也是FrameLayout,activity通過(guò) setContent()加載布局的時(shí)候加載到這個(gè)View上。ViewRootImpl就是建立 DecorView 和 Window 之間的聯(lián)系

image

ViewRootImpl 類

performTraversals 方法中調(diào)用了performMeasure()


image.png
  1. viewRootImpl會(huì)調(diào)用performTraversals(),其內(nèi)部會(huì)調(diào)用performMeasure(),performLayout,performDraw() 方法
    2.performMeasure()會(huì)調(diào)用最外層的ViewGroup的measure()--->onMeasure() ---->ViewGroup的onMeasure() 方法是抽象方法,但其提供了measeureChilden()方法,這個(gè)會(huì)遍歷子View 然后循環(huán)調(diào)用measureChilden() ,這個(gè)方法中會(huì)調(diào)用getChildMeasureSpec() + 父View的MeasureSpec + 子View的LayoutParam 一起獲取到本身View的MeasueSpec,然后調(diào)用子View的measure()到view的onMeasure()--->setMeasuredDimension(getDefaultSize(),getDefaultSize()) 默認(rèn)返回measureSpec的測(cè)量數(shù)值 ,所以繼承View 進(jìn)行自定義的wrap_content 需要重寫
    3.performLayout() 會(huì)調(diào)用最外層的ViewGroup的layou(l,t,r,b) ,本身View在其中使用setFrame(),設(shè)置本View的四個(gè)頂點(diǎn)位置,在onLayout(抽象方法)中確定子View的位置 ,比如LinearLayout 會(huì)遍歷子View ,循環(huán)調(diào)用 setChildFrame--->子View的layout方法
    4.performDraw()會(huì)調(diào)用最外層的ViewGroup的draw(),其中會(huì)先后調(diào)用background.draw () 繪制背景,onDraw 方法 繪制自己本身View ---> 調(diào)用dispatchDraw() 進(jìn)行子View 繪制---->onDrawScrollBars() 繪制裝飾
    5.MeasureSpec 由2位SpecMode(UNSPECIFIED,EXACTILY(對(duì)應(yīng)精確值和match_parent),AT_MOST 對(duì)應(yīng)warp_content) 和30位SpecSize 組成一個(gè)int,DecorView的MeasureSpec 是由窗口大小與其LayoutParams決定,其他View 是由父View的MeasureSpec 和本身的View的layoutParams 來(lái)決定 ,ViewGroup 中有g(shù)etChildenMeasureSpec() 來(lái)獲取子View 的measureSpec

EXACTLY:父容器已經(jīng)測(cè)量出子View的大小。對(duì)應(yīng)是 View 的LayoutParams的match_parent 或者精確數(shù)值。
AT_MOST:父容器已經(jīng)限制子view的大小,View 最終大小不可超過(guò)這個(gè)值。對(duì)應(yīng)是 View 的LayoutParams的wrap_content
UNSPECIFIED:父容器不對(duì)View有任何限制,要多大給多大,這種情況一般用于系統(tǒng)內(nèi)部,表示一種測(cè)量的狀態(tài)。

6.三種方式獲取measure()后的寬高值
a.activity中的onWindowFocusChange() 方法中調(diào)用獲取
b.view.post(Runnable) 將在run方法中進(jìn)行獲取
c.view.setViewTreeObserbable 監(jiān)聽(tīng)中進(jìn)行獲取

image.png
   private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        mLayoutRequested = false;
        mScrollMayChange = true;
        mInLayout = true;

        final View host = mView;
        if (host == null) {
            return;
        }
        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
            Log.v(mTag, "Laying out " + host + " to (" +
                    host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
        }

        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
        try {
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

            mInLayout = false;
            int numViewsRequestingLayout = mLayoutRequesters.size();
            if (numViewsRequestingLayout > 0) {
                // requestLayout() was called during layout.
                // If no layout-request flags are set on the requesting views, there is no problem.
                // If some requests are still pending, then we need to clear those flags and do
                // a full request/measure/layout pass to handle this situation.
                ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
                        false);
                if (validLayoutRequesters != null) {
                    // Set this flag to indicate that any further requests are happening during
                    // the second pass, which may result in posting those requests to the next
                    // frame instead
                    mHandlingLayoutInLayoutRequest = true;

                    // Process fresh layout requests, then measure and layout
                    int numValidRequests = validLayoutRequesters.size();
                    for (int i = 0; i < numValidRequests; ++i) {
                        final View view = validLayoutRequesters.get(i);
                        Log.w("View", "requestLayout() improperly called by " + view +
                                " during layout: running second layout pass");
                        view.requestLayout();
                    }
                    measureHierarchy(host, lp, mView.getContext().getResources(),
                            desiredWindowWidth, desiredWindowHeight);
                    mInLayout = true;
                    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

                    mHandlingLayoutInLayoutRequest = false;

                    // Check the valid requests again, this time without checking/clearing the
                    // layout flags, since requests happening during the second pass get noop'd
                    validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
                    if (validLayoutRequesters != null) {
                        final ArrayList<View> finalRequesters = validLayoutRequesters;
                        // Post second-pass requests to the next frame
                        getRunQueue().post(new Runnable() {
                            @Override
                            public void run() {
                                int numValidRequests = finalRequesters.size();
                                for (int i = 0; i < numValidRequests; ++i) {
                                    final View view = finalRequesters.get(i);
                                    Log.w("View", "requestLayout() improperly called by " + view +
                                            " during second layout pass: posting in next frame");
                                    view.requestLayout();
                                }
                            }
                        });
                    }
                }

            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        mInLayout = false;
    }

Draw繪制流程


image.png

draw 過(guò)程中一共分成7步,其中兩步我們直接直接跳過(guò)不分析了。

第一步:drawBackground(canvas): 作用就是繪制 View 的背景。

第二步:onDraw(canvas) :繪制 View 的內(nèi)容。View 的內(nèi)容是根據(jù)自己需求自己繪制的,所以方法是一個(gè)空方法,View的繼承類自己復(fù)寫實(shí)現(xiàn)繪制內(nèi)容。

第三步:dispatchDraw(canvas):遍歷子View進(jìn)行繪制內(nèi)容。在 View 里面是一個(gè)空實(shí)現(xiàn),ViewGroup 里面才會(huì)有實(shí)現(xiàn)。在自定義 ViewGroup 一般不用復(fù)寫這個(gè)方法,因?yàn)樗诶锩娴膶?shí)現(xiàn)幫我們實(shí)現(xiàn)了子 View 的繪制過(guò)程,基本滿足需求。

第四步:onDrawForeground(canvas):對(duì)前景色跟滾動(dòng)條進(jìn)行繪制。

第五步:drawDefaultFocusHighlight(canvas):繪制默認(rèn)焦點(diǎn)高亮
image.png

3.android 四大組件加載過(guò)程,詳細(xì)介紹下

4.Activity 的啟動(dòng)模式
1.standard 標(biāo)準(zhǔn)模式
不指定其他模式的情況下,默認(rèn)是用standard 這種模式來(lái)啟動(dòng)activity,誰(shuí)啟動(dòng)的activity,就歸宿到對(duì)應(yīng)的activity任務(wù)棧中,standard模式默認(rèn)按照任務(wù)棧的順序進(jìn)行壓棧處理

image.png

singleTop 模式
singleTop 模式,如果當(dāng)前打開(kāi)的activity處于任務(wù)棧,棧頂activity就復(fù)用,不用創(chuàng)建新的activity實(shí)例,此時(shí)回調(diào)onNewIntent方法,如果當(dāng)前打開(kāi)的activity不處于任務(wù)棧棧頂,那么依然創(chuàng)建新的activity實(shí)例,并處于任務(wù)棧頂

image.png

SingleTask 模式
首先會(huì)根據(jù)taskAffinity 去尋找當(dāng)前是否存在一個(gè)對(duì)應(yīng)的名字的任務(wù)棧,如果不存在,則會(huì)創(chuàng)建新的Task任務(wù)棧,如果存在,則會(huì)查找當(dāng)前任務(wù)棧中是否有當(dāng)前的activity實(shí)例,如果有清除掉當(dāng)前activity任務(wù)棧的所有的activity實(shí)例,并且當(dāng)前的activity處于任務(wù)棧棧頂,并且回調(diào)activity中的onNewIntent方法,如果不存在當(dāng)前activity的實(shí)例,在當(dāng)前的任務(wù)棧中創(chuàng)建activity的實(shí)例,并處于任務(wù)棧棧頂


image.png

SingleInstance 模式
SingleInstance比較特殊,是全局單例模式,是一種加強(qiáng)的SingleTask模式。它除了具有它所有特性外,還加強(qiáng)了一點(diǎn):具有此模式的Activity僅僅能單獨(dú)位于一個(gè)任務(wù)棧中。(以singleInstance模式開(kāi)啟的activity 具有獨(dú)占性 )

5.Activity緩存方法

6.Service的生命周期,兩種啟動(dòng)方法,有什么區(qū)別

7.怎么保證Service 不被殺死

8.靜態(tài)的Broadcast和動(dòng)態(tài)BroadCast 有什么區(qū)別

  1. Intent可以傳遞那些數(shù)據(jù)類型

10.Json有什么優(yōu)劣勢(shì),解析的原理

11.一個(gè)語(yǔ)言的編譯過(guò)程

12.動(dòng)畫有幾類,各有什么特點(diǎn)

13.Handler,Looper 消息隊(duì)列模型,每個(gè)部分的作用是什么

14.怎么退出終止App

15.Android IPC Binder原理

16.描述一次跨進(jìn)程通信

17.android 重要話術(shù)語(yǔ)解釋

18 .理解Window和WindowMananger

19.Bitmap的處理

20如何實(shí)現(xiàn)一個(gè)網(wǎng)絡(luò)框架 (參考Okhttp,volley)

21.ClassLoader 的基礎(chǔ)知識(shí)

22.插件化框架描述 (ps:dynamicLoadApk)

23.熱修復(fù) ( ps:AndFix)

24.線程同步的問(wèn)題,常用的線程同步

25.Asynctask 和線程池,GC相關(guān) (怎么判斷哪些內(nèi)存GC,GC算法)

26.網(wǎng)絡(luò)相關(guān)

27.APK 打包流程和其內(nèi)容

28.網(wǎng)絡(luò)劫持的類型原理

29.Java類加載過(guò)程

30.Retrofit的了解

31.Bundle的數(shù)據(jù)結(jié)構(gòu),如何存儲(chǔ)數(shù)據(jù)

32.ListView內(nèi)點(diǎn)擊Button并移動(dòng)的事件流完整攔截過(guò)程

33.Service 的意義

34.Android的IPC通信方式 ,線程進(jìn)程間通信機(jī)制有哪些

35.操作系統(tǒng)進(jìn)程和線程的區(qū)別

36.HashMap的實(shí)現(xiàn)過(guò)程:Capacity就是Buckets的數(shù)目,Load factor就是Buckets填滿程度的最大比例,如果對(duì)迭代性要求很高的話不要把capacity設(shè)置過(guò)大,也不要把load factor 設(shè)置過(guò)小

37.MVC,MVP,MVVM

38.Java的線程如何實(shí)現(xiàn)

39.ArrList 如何刪除重復(fù)的元素或者指定的元素

40 .如何設(shè)計(jì)在UDP上層保證UDP的可靠行傳輸

參考文檔
Android大廠面試題錦集附答案(BAT TMD JD 小米)
2020 Android 面試重難點(diǎn)
快手、小紅書、最右面試題

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

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