那可能是Android最经典的面试题(2017.11.01)

转发请注脚出处


synchronized函数和synchronized代码块的差别


    1. 首先synchronized函数和synchronized代码快的功能范围有分别,synchronized函数一般锁定的是方今类对象,synchronized代码块锁定成效域能够选拔是本对象,也得以是字符串等等.
    1. 眼下类对象锁没有自由的时候,本类的保有synchronized(this)同步代码块都卡住。若是有并发请求synchronized函数,同目前间只好有一个请求执行
      .
    1. 唯独当前类对象锁没有自由的时候,别的请求能够访问本类中不带synchronized(this)的代码块,也足以访问非同一把锁的代码块例如synchronized(Str)等.
    1. 由于效果范围有分别,一般意义范围越小执行功能越高,平常花费中一般选拔成效范围较小的synchronized.

怎么着判定二个指标是足以被回收的


    1. 前边java虚拟机使用引用计数器的算法,当引用计数器为0时表示该对象没有引用明白后被清理。可是这些办法很难化解循环引用难题,所以近期甘休使用了。
    1. 最近使用的是可达性分析算法来明确1个目的是或不是能够被回收。
    1. 原理是:通过一个叫GC
      Roots的对象当作根对象,然后初始向下搜寻,搜索的路子叫做引用链,当对象到GC
      Roots没有任何引用链相连的时候,则表明此目的是不可用的.
    1. 不可用对象并不是随即就实行回收措施,执行清理措施在此以前至少要经历三回标记进程.
  • ①假使指标在开始展览可达性分析后发现没有与GC
    Roots相连接的引用链,那它将会被第②回标记并且实行2回筛选,筛选的原则是此目的是或不是有必不可少履行finalize()方法。当对象没有覆盖finalize()方法,大概finalize()方法已经被虚拟机调用过,虚拟机将那三种状态都算得“没有要求实施”。(即意味着一向回收).

  • ②比方这一个目的被判定为有必不可少实施finalize()方法,那么这几个目的将会停放在二个称呼F-Queue的类别之中,并在稍后由三个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。那里所谓的“执行”是指虚拟机会接触这几个办法,但并不承诺会等待它运转结束,那样做的因由是,如若贰个对象在finalize()方法中实行缓慢,只怕发生了死循环(更极端的情景),将很只怕会造成F-Queue队列中其它对象永久处于等候,甚至造成整个内部存款和储蓄器回收类别崩溃.
    1. finalize()方法是指标回收前的结尾2回机会,稍后GC将对F-Queue中的对象开始展览第四回小圈圈的号子,假使目的要在finalize()中不被回收,只要重新与引用链上的其它二个对象建立关联即可,譬如把温馨(this关键字)赋值给某些类变量也许目的的成员变量,那在其次次标记时它将被移除出“即将回收”的集结;借使目的那时候还从未回避,那大多它就真的被回收了。
    1. 任何多少个目的的finalize()方法都只会被系统活动调用1回,假使目的面临下三回回收,它的finalize()方法不会被再次实施,由此第贰段代码的自救行动失利了。因为finalize()方法已经被虚拟机调用过,虚拟机都实属“没有要求履行”。(即意味着向来回收).

写二个函数,输入八个数如38,拆分 3 + 8 = 11,1 + 1 = 2,最终2无法拆分就回到


    public  int  getNum(int num) {
        while (num >= 10) {
            num = num / 10 + num % 10;
        }
        return num;
    }

四个经过同时调用三个ContentProvider的query获取数据,ContentPrvoider是怎么样反应的呢?


  • 分析:
    大家知道Activity那样的零部件,它生命周期的回调函数是在UI线程中实施的,ContentProvider的onCreate()方法也是在UI线程中运营的,回答那一个题材前,大家首先要搞清楚ContentProvider的Query(),insert(),delete(),updata()那多少个点子是还是不是也是在UI线程中运作。
  • 发现标题:
    假诺以上多少个艺术是在UI线程中运作的,那么几个线程并发去调用就很有恐怕出现AN奔驰G级;要是否在UI线程运转的,那它是在3个工作线程中运作的照旧在三个线程中运营的吗?即ContentProvider是不是援救并发操作呢?
  • 分析难题:
    ContentResolver与ContentProvider类隐藏了贯彻细节,可是ContentProvider所提供的Query(),insert(),delete(),updata()那多少个点子都以在ContentProvider实行的线程池中运作的,而不是在经过的主线程中运转,以为那些格局有恐怕被多个地点调用,所以它们是线程安全的。
    ContentProvider完成过程通讯是借助于Binder机制的,所以上述难题会回归到Binder线程处理难点,并不是每贰个ContentProvider都会有二个线程池,而是3个进度共用三个线程池,共用的线程池正是Binder线程池。
  • 标准答案:
    二个content
    provider能够承受来自此外多个进程的数码请求。即使ContentResolver与ContentProvider类隐藏了落到实处细节,不过ContentProvider所提供的query(),insert(),delete(),update()都是在ContentProvider进程的线程池中被调用执行的,而不是经过的主线程中。那个线程池是有Binder创造和保养的,其实使用的正是每种应用进程中的Binder线程池。

Android设计ContentProvider的目标是什么?


    1. 隐藏数据的贯彻格局,对外提供统一的数目访问接口;
    1. 更好的多寡访问权限管理。ContentProvider能够对开发的数据开始展览权力设置,不一致的U揽胜极光I能够对应区别的权位,唯有切合权限须要的组件才能访问到ContentProvider的具体操作。
    1. ContentProvider封装了跨进程共享的逻辑,大家只要求Uri即可访问数据。由系统来保管ContentProvider的创制、生命周期及走访的线程分配,简化大家在利用间共享数据(进度间通讯)的不二法门。大家就算通过ContentResolver访问ContentProvider所提醒的数目接口,而不需求操心它所在进程是开发银行照旧未运转。

运维在主线程的ContentProvider为啥不会影响主线程的UI操作?


    1. ContentProvider的onCreate()是运营在UI线程的,而query(),insert(),delete(),update()是运作在线程池中的工作线程的,所以调用这向个方法并不会堵塞ContentProvider所在进度的主线程,但可能会堵塞调用者所在的进度的UI线程!
    1. 因而,调用ContentProvider的操作仍旧要放在子线程中去做。固然一向的CRUD的操作是在办事线程的,但系统会让您的调用线程等待那么些异步的操作达成,你才足以继续线程以前的干活。

请详细讲述Android事件分发机制:


那道题是不胜枚举家面试公司会问到的一道经典面试题,但又平时被面试者忽略。

看了广大博客也看了广大代码,半数以上都以当机不断,不方便人民群众阅读固计算如下:

主线传递唯有三步:Activity->ViewGroup->View

Activity和View唯有多个法子控制事件传递:dispatchTouchEvent(),onTouchEvent
();

ViewGroup有四个点子控制传递:dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent
();

接下去用一张图给大家讲述下实际是怎么一步一步分发的。

总结:
1.对于 dispatchTouchEvent,onTouchEvent,return
true
是截至事件传递。return false
是回溯到父View的onTouch伊夫nt方法。
2.ViewGroup
想把温馨分发给自身的onTouch伊芙nt,需求拦截器onInterceptTouch伊夫nt方法return
true 把事件拦截下来。
3.ViewGroup 的阻挠器onInterceptTouch伊夫nt 暗中同意是不阻拦的,所以return
super.onInterceptTouch伊夫nt()=return false;
4.View
从未拦截器,为了让View能够把事件分发给本身的onTouch伊夫nt,View的dispatchTouch伊芙nt私下认可实现(super)正是把事件分发给本身的onTouch伊夫nt。

ViewGroup和View 的dispatchTouch伊夫nt
是做事件分发,那么这么些事件也许分发出去的多少个对象
注:——> 前面代表事件指标供给咋办。
① 、 自个儿消费,终结传递。——->return true
贰 、 给协调的onTouch伊芙nt处理——->
调用super.dispatchTouch伊夫nt()系统暗中认可会去调用
onInterceptTouch伊芙nt,在onInterceptTouch伊夫nt return
true就会去把事件分给自个儿的onTouch伊夫nt处理。
叁 、 传给子View——>调用super.dispatchTouch伊夫nt()暗中同意达成会去调用
onInterceptTouch伊夫nt 在onInterceptTouch伊夫nt return
false,就会把事件传给子类。
四 、不传给子View,事件终止往下传递,事件初始纪念,从父View的onTouch伊芙nt早先事件从下到上回归执行各种控件的onTouch伊夫nt——->return
false

注: 由于View没有子View所以不必要onInterceptTouch伊夫nt
来控件是或不是把事件传递给子View照旧拦截,所以View的轩然大波分发调用super.dispatchTouch伊夫nt()的时候私下认可把事件传给自身的onTouch伊芙nt处理(也就是阻挡),相比较ViewGroup的dispatchTouch伊芙nt
事件分发,View的轩然大波分发唯有dispatchTouch伊芙nt()和onTouch伊芙nt()不要求onInterceptTouch伊芙nt()参预。

到此事件分发总计收尾。若是想详细掌握事件分发机制的请看那篇博客:
http://blog.csdn.net/w525721508/article/details/78227154


View的渲染进程,或然叫View的绘图流程


那道题也是相比较老的一道题了,不过无论BAT依然小创业公司中冒出的频率十二分高
接下去就总计性的描述叁次View绘制流程,避免大书特书,接下去的讲述一切从简
目的在于各位读者耐心看完,相信您会有极大的获得!
View绘图流程是在ViewRoot.java类的performTraversals()函数中展开的
绘图部分共计需求三步:

measure() -> layout() -> draw();

1. 判读是或不是再一次总括视图大小(measure)

此间写图片描述

原理:从顶层父View像子View递归调用view.measure(),measure方法中回调onMeasure()
MeasureSpec是View的衡量内部类,衡量规格为int型,值由高二位规格形式specMode和低32位的具体尺寸specSize组成。

specMode有三种值

MeasureSpec.UPSPECIFIED :
父容器对于子容器没有其余限制,子容器想要多大就多大
MeasureSpec.EXACTLY:
父容器已经为子容器设置了尺寸,子容器应当服从那几个边界,不论子容器想要多大的长空。
MeasureSpec.AT_MOST:子容器能够是宣称大小内的私行大小

  • View的measure方法是final,不能够重载,只好重载inMeasure达成本人的衡量逻辑

  • 顶层的DecorView的MeasureSpec是由ViewRootImpl中的getRootMeasureSpec方法鲜明(LayoutParams宽高级参谋数均为MATCH_PARENT,specMode是EXACTLY,specSize为大体显示屏尺寸)。

  • ViewGroup类提供了measureChild,measureChild和measureChildWithMargins方法,简化了父子View的尺寸计算。

  • 若是是ViewGroup的子类就不能够不要求LayoutParams继承子MarginLayoutParams,否则不恐怕利用layout_margin参数。

  • View的布局大小由父View和子View共同决定。

  • 运用View的getMeasuredWidth()和getMeasuredHeight()方法来获得View度量的宽高,必须确认保证那七个格局在onMeasure流程之后被调用才能回去有效值。

2. 是不是重新分配视图的地点(layout)

此处写图片描述

原理:
layout也是从顶层父View向子View的递归调用View.layout方法的经过,父View根据上一步measure子View获得的布局大小和布局参数,将子View放在合适的岗位上。

  • View.layout方法能够被重载,ViewGroup.layout为final不可以被重载,ViewGroup.onLayout为abstract的子类必须重载达成本人的任务逻辑

  • measure甘休后获取的是各种View经衡量后的measuredWidth和measuredHeight,Layout操作完之后获得的是每种View进行岗位分配后的mLeft,mTop、mRight、mBottom,这几个值都以绝对父View

  • 凡是layout_XXX的布局属性都是对准父级View的,假若View没有父级容器则layout_XXX属性是尚未任何意义的

  • 使用View
    的getWidth()和getHright()方法获得View衡量的宽高非得确认保障那多个格局在在onLayout流程之后。

3. 是否再次绘制(draw)

此间写图片描述

原理:
draw进度也是在ViewRootImpl的performTraversals()内部调拨运输的,其调用顺序在measure()和layout()之后,那里的mView对于Actiity来说正是PhoneWindow.DecorView,ViewRootImpl中的代码会创制四个Canvas对象,然后调用View的draw()方法来执行实际的绘制工。所以又回归到了ViewGroup与View的树状递归draw进度

  • 假如该View是三个ViewGroup,则供给递归绘制其所含有的保有子View。

  • View暗中认可不绘制任何内容,真正的绘图都在大团结的子类中落实

  • View的绘图是借助onDraw()方法传入的Canvas类来进展的

  • 有别于View
    动画和ViewGroup动画,前者是View自己的动画片能够通过setAnimation添加,后者能够经过xml布局的layoutAnimation属性添加

  • 在获取画布剪切区(各样View的draw中盛传的Canvas)时会自动处理掉padding,子View获得Canvas不用关爱这几个逻辑,只关心如何绘制即可

  • 私下认可景况下子View的ViewGroup.drawChild绘制顺序和子View被添加的相继一致,不过你也能够重载ViewGroup.getChildDrawingOrder()以提供区别的一一

4. invalidate()

原理:
invalidate方法请求重绘View树(约等于draw方法),倘使View大小没有爆发变化就不会调用layout进度,并且只绘制这些“必要重绘的”View,也正是哪些View(View只绘制该View,ViewGroup绘制整个ViewGroup)请求invalidate种类措施,就绘制该View。

  • 一贯调用invalidate方法.请求重新draw,但只会绘制调用者本身。

  • 触发setSelection方法。请求重新draw,但只会绘制调用者本身。

  • 触发setVisibility方法。
    当View可视状态在INVISIBLE转换VISIBLE时会直接调用invalidate方法,继而绘制该View。当View的可视状态在INVISIBLE\VISIBLE
    转换为GONE状态时会间接调用requestLayout和invalidate方法,同时由于View树大小产生了转变,所以会呈请measure进度以及draw进度,同样只绘制要求“重新绘制”的视图。

  • 触发setEnabled方法。请求重新draw,但不会重复绘制任何View包含该调用者本身。

  • 触发requestFocus方法。请求View树的draw进度,只绘制“要求重绘”的View。

例:
当大家写3个Activity时,大家必定会透过setContentView方法将大家要出示的界面传入该格局,该方法会讲大家界面通过addView追加到id为content的二个FrameLayout(ViewGroup)中,然后addView方法中经过调运invalidate(true)去通告触发ViewRootImpl类的performTraversals()方法,至此递归绘制大家自定义的兼具布局。

5.requestLayout()

原理:
View的requestLayout时其实质便是千载难逢进步传递,直到ViewRootImpl截至,然后触发ViewRootImpl的requestLayout方法
requestLayout()方法会调用measure进程和layout进程,不会调用draw进度,也不会重复绘制任何View包罗该调用者本身。

以上为View渲染的完全进度,如不平时欢迎指正。