Android高级面试题整理一

1.android 事件分发机制,分析整体流程

image.png

2.android View绘制机制和加载过程,详细说一
下整体流程

一个 Activity 包含一个Window,Window是一个抽象基类,是 Activity 和整个 View 系统交互的接口,只有一个实现子类PhoneWindow,提供了一系列窗口的方法,比如设置背景,标题等。一个PhoneWindow 对应一个DecorView 跟 一个 ViewRootImpl,DecorView 是ViewTree 里面的顶层布局,是继承于FrameLayout,包含两个子View,一个id=statusBarBackground 的 View 和 LineaLayout,LineaLayout 里面包含 title 跟content,title就是平时用的TitleBar或者ActionBar, content也是FrameLayout,activity通过 setContent()加载布局的时候加载到这个View上。ViewRootImpl就是建立 DecorView 和 Window 之间的联系

image

ViewRootImpl 类

performTraversals 方法中调用了performMeasure()


image.png
  1. viewRootImpl会调用performTraversals(),其内部会调用performMeasure(),performLayout,performDraw() 方法
    2.performMeasure()会调用最外层的ViewGroup的measure()--->onMeasure() ---->ViewGroup的onMeasure() 方法是抽象方法,但其提供了measeureChilden()方法,这个会遍历子View 然后循环调用measureChilden() ,这个方法中会调用getChildMeasureSpec() + 父View的MeasureSpec + 子View的LayoutParam 一起获取到本身View的MeasueSpec,然后调用子View的measure()到view的onMeasure()--->setMeasuredDimension(getDefaultSize(),getDefaultSize()) 默认返回measureSpec的测量数值 ,所以继承View 进行自定义的wrap_content 需要重写
    3.performLayout() 会调用最外层的ViewGroup的layou(l,t,r,b) ,本身View在其中使用setFrame(),设置本View的四个顶点位置,在onLayout(抽象方法)中确定子View的位置 ,比如LinearLayout 会遍历子View ,循环调用 setChildFrame--->子View的layout方法
    4.performDraw()会调用最外层的ViewGroup的draw(),其中会先后调用background.draw () 绘制背景,onDraw 方法 绘制自己本身View ---> 调用dispatchDraw() 进行子View 绘制---->onDrawScrollBars() 绘制装饰
    5.MeasureSpec 由2位SpecMode(UNSPECIFIED,EXACTILY(对应精确值和match_parent),AT_MOST 对应warp_content) 和30位SpecSize 组成一个int,DecorView的MeasureSpec 是由窗口大小与其LayoutParams决定,其他View 是由父View的MeasureSpec 和本身的View的layoutParams 来决定 ,ViewGroup 中有getChildenMeasureSpec() 来获取子View 的measureSpec

EXACTLY:父容器已经测量出子View的大小。对应是 View 的LayoutParams的match_parent 或者精确数值。
AT_MOST:父容器已经限制子view的大小,View 最终大小不可超过这个值。对应是 View 的LayoutParams的wrap_content
UNSPECIFIED:父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。

6.三种方式获取measure()后的宽高值
a.activity中的onWindowFocusChange() 方法中调用获取
b.view.post(Runnable) 将在run方法中进行获取
c.view.setViewTreeObserbable 监听中进行获取

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 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 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 过程中一共分成7步,其中两步我们直接直接跳过不分析了。

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

第二步:onDraw(canvas) :绘制 View 的内容。View 的内容是根据自己需求自己绘制的,所以方法是一个空方法,View的继承类自己复写实现绘制内容。

第三步:dispatchDraw(canvas):遍历子View进行绘制内容。在 View 里面是一个空实现,ViewGroup 里面才会有实现。在自定义 ViewGroup 一般不用复写这个方法,因为它在里面的实现帮我们实现了子 View 的绘制过程,基本满足需求。

第四步:onDrawForeground(canvas):对前景色跟滚动条进行绘制。

第五步:drawDefaultFocusHighlight(canvas):绘制默认焦点高亮
image.png

3.android 四大组件加载过程,详细介绍下

4.Activity 的启动模式
1.standard 标准模式
不指定其他模式的情况下,默认是用standard 这种模式来启动activity,谁启动的activity,就归宿到对应的activity任务栈中,standard模式默认按照任务栈的顺序进行压栈处理

image.png

singleTop 模式
singleTop 模式,如果当前打开的activity处于任务栈,栈顶activity就复用,不用创建新的activity实例,此时回调onNewIntent方法,如果当前打开的activity不处于任务栈栈顶,那么依然创建新的activity实例,并处于任务栈顶

image.png

SingleTask 模式
首先会根据taskAffinity 去寻找当前是否存在一个对应的名字的任务栈,如果不存在,则会创建新的Task任务栈,如果存在,则会查找当前任务栈中是否有当前的activity实例,如果有清除掉当前activity任务栈的所有的activity实例,并且当前的activity处于任务栈栈顶,并且回调activity中的onNewIntent方法,如果不存在当前activity的实例,在当前的任务栈中创建activity的实例,并处于任务栈栈顶


image.png

SingleInstance 模式
SingleInstance比较特殊,是全局单例模式,是一种加强的SingleTask模式。它除了具有它所有特性外,还加强了一点:具有此模式的Activity仅仅能单独位于一个任务栈中。(以singleInstance模式开启的activity 具有独占性 )

5.Activity缓存方法

6.Service的生命周期,两种启动方法,有什么区别

7.怎么保证Service 不被杀死

8.静态的Broadcast和动态BroadCast 有什么区别

  1. Intent可以传递那些数据类型

10.Json有什么优劣势,解析的原理

11.一个语言的编译过程

12.动画有几类,各有什么特点

13.Handler,Looper 消息队列模型,每个部分的作用是什么

14.怎么退出终止App

15.Android IPC Binder原理

16.描述一次跨进程通信

17.android 重要话术语解释

18 .理解Window和WindowMananger

19.Bitmap的处理

20如何实现一个网络框架 (参考Okhttp,volley)

21.ClassLoader 的基础知识

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

23.热修复 ( ps:AndFix)

24.线程同步的问题,常用的线程同步

25.Asynctask 和线程池,GC相关 (怎么判断哪些内存GC,GC算法)

26.网络相关

27.APK 打包流程和其内容

28.网络劫持的类型原理

29.Java类加载过程

30.Retrofit的了解

31.Bundle的数据结构,如何存储数据

32.ListView内点击Button并移动的事件流完整拦截过程

33.Service 的意义

34.Android的IPC通信方式 ,线程进程间通信机制有哪些

35.操作系统进程和线程的区别

36.HashMap的实现过程:Capacity就是Buckets的数目,Load factor就是Buckets填满程度的最大比例,如果对迭代性要求很高的话不要把capacity设置过大,也不要把load factor 设置过小

37.MVC,MVP,MVVM

38.Java的线程如何实现

39.ArrList 如何删除重复的元素或者指定的元素

40 .如何设计在UDP上层保证UDP的可靠行传输

参考文档
Android大厂面试题锦集附答案(BAT TMD JD 小米)
2020 Android 面试重难点
快手、小红书、最右面试题

你可能感兴趣的:(Android高级面试题整理一)